popovers: Add Copied! tooltip when `copy code` button is clicked.

`copy code` button now show a `Copied!` tooltip when clicked.
It implements a similar function used on `saved as draft` notice.

We need to modify the copy_code_button template to limit
data-tippy-trigger to not include click; otherwise, repeated clicks to
copy code will incorrectly also display the "Copy code" tooltip
alternating with "Copied".

Fixes part of #21036.
This commit is contained in:
Deekshith S Shetty 2023-02-21 21:15:12 +05:30 committed by Tim Abbott
parent f84de2212e
commit 596abf6190
4 changed files with 35 additions and 2 deletions

View File

@ -13,6 +13,7 @@ import * as realm_playground from "./realm_playground";
import * as rtl from "./rtl";
import * as stream_data from "./stream_data";
import * as timerender from "./timerender";
import {show_copied_confirmation} from "./tippyjs";
import * as user_groups from "./user_groups";
import {user_settings} from "./user_settings";
@ -227,11 +228,14 @@ export const update_elements = ($content) => {
}
const $copy_button = $(copy_code_button());
$pre.prepend($copy_button);
new ClipboardJS($copy_button[0], {
const clipboard = new ClipboardJS($copy_button[0], {
text(copy_element) {
return $(copy_element).siblings("code").text();
},
});
clipboard.on("success", () => {
show_copied_confirmation($copy_button[0]);
});
});
// Display emoji (including realm emoji) as text if

View File

@ -624,3 +624,22 @@ export function initialize() {
appendTo: () => document.body,
});
}
export function show_copied_confirmation($copy_button) {
// Display a tooltip to notify the user the message or code was copied.
const instance = tippy($copy_button, {
placement: "top",
appendTo: () => document.body,
onUntrigger() {
remove_instance();
},
});
instance.setContent($t({defaultMessage: "Copied!"}));
instance.show();
function remove_instance() {
if (!instance.state.isDestroyed) {
instance.destroy();
}
}
setTimeout(remove_instance, 1000);
}

View File

@ -1,3 +1,3 @@
<button class="btn pull-left copy_button_base copy_codeblock" data-tippy-content="{{t 'Copy code' }}" aria-label="{{t 'Copy code' }}">
<button class="btn pull-left copy_button_base copy_codeblock" data-tippy-content="{{t 'Copy code' }}" data-tippy-trigger="mouseenter" aria-label="{{t 'Copy code' }}">
{{> copy_to_clipboard_svg }}
</button>

View File

@ -13,11 +13,15 @@ class Clipboard {
constructor(...args) {
clipboard_args = args;
}
on(success, show_copied_confirmation) {
show_copied_confirmation();
}
}
mock_cjs("clipboard", Clipboard);
const realm_playground = mock_esm("../src/realm_playground");
const tippyjs = mock_esm("../src/tippyjs");
user_settings.emojiset = "apple";
const rm = zrequire("rendered_markdown");
@ -428,6 +432,8 @@ run_test("code playground none", ({override, mock_template}) => {
return undefined;
});
override(tippyjs, "show_copied_confirmation", () => {});
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, false);
assert.deepEqual(prepends, [$copy_code]);
assert_clipboard_setup();
@ -442,6 +448,8 @@ run_test("code playground single", ({override, mock_template}) => {
return [{name: "Some Javascript Playground"}];
});
override(tippyjs, "show_copied_confirmation", () => {});
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, true);
assert.deepEqual(prepends, [$view_code, $copy_code]);
assert_clipboard_setup();
@ -460,6 +468,8 @@ run_test("code playground multiple", ({override, mock_template}) => {
return ["whatever", "whatever"];
});
override(tippyjs, "show_copied_confirmation", () => {});
const {prepends, $copy_code, $view_code} = test_code_playground(mock_template, true);
assert.deepEqual(prepends, [$view_code, $copy_code]);
assert_clipboard_setup();