From 596abf6190fdab7aa874a8c2688e85e2963d88b7 Mon Sep 17 00:00:00 2001 From: Deekshith S Shetty Date: Tue, 21 Feb 2023 21:15:12 +0530 Subject: [PATCH] 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. --- web/src/rendered_markdown.js | 6 +++++- web/src/tippyjs.js | 19 +++++++++++++++++++ web/templates/copy_code_button.hbs | 2 +- web/tests/rendered_markdown.test.js | 10 ++++++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/web/src/rendered_markdown.js b/web/src/rendered_markdown.js index c0b40805b7..26ed41b811 100644 --- a/web/src/rendered_markdown.js +++ b/web/src/rendered_markdown.js @@ -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 diff --git a/web/src/tippyjs.js b/web/src/tippyjs.js index fc8b00fec7..1dbc60a159 100644 --- a/web/src/tippyjs.js +++ b/web/src/tippyjs.js @@ -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); +} diff --git a/web/templates/copy_code_button.hbs b/web/templates/copy_code_button.hbs index 547c172ede..0c2daf1bc2 100644 --- a/web/templates/copy_code_button.hbs +++ b/web/templates/copy_code_button.hbs @@ -1,3 +1,3 @@ - diff --git a/web/tests/rendered_markdown.test.js b/web/tests/rendered_markdown.test.js index 7a8d8ba77c..79b3eae5b5 100644 --- a/web/tests/rendered_markdown.test.js +++ b/web/tests/rendered_markdown.test.js @@ -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();