From 3f3f007920bc9f5b6ec760e3a7df32440a363f68 Mon Sep 17 00:00:00 2001 From: Brian Manifold Date: Fri, 25 Apr 2025 17:43:43 -0700 Subject: [PATCH] fix(portal): Update copy to clipboard button (#8907) Why: * The copy to clipboard button was not working at all on the API new token page due to the fact that the FlowbiteJS library expects the presence of the elements in the DOM on first render. This was not true of the API Token code block. Along with that issue the existing code blocks copy to clipboard buttons did not give any visual indication that the copy had been completed. It was also somewhat difficult to see the copy to clipboard button on those code blocks as well. This commit updates the buttons to be more visible, as well as adds a phx-hook to make sure the FlowbiteJS init functions are run on every code block even if it's inserted after the initial load of the page and adds functions that are run as a callback to toggle the button text and icon to show the text has been copied. --- elixir/apps/web/assets/js/hooks.js | 37 ++++++++++++++- .../web/lib/web/components/core_components.ex | 47 +++++++++++-------- elixir/apps/web/test/support/conn_case.ex | 2 +- 3 files changed, 64 insertions(+), 22 deletions(-) diff --git a/elixir/apps/web/assets/js/hooks.js b/elixir/apps/web/assets/js/hooks.js index 583a5defd..5217edf0a 100644 --- a/elixir/apps/web/assets/js/hooks.js +++ b/elixir/apps/web/assets/js/hooks.js @@ -1,4 +1,4 @@ -import { initTabs, Popover } from "flowbite"; +import { initCopyClipboards, initTabs, Popover } from "flowbite"; let Hooks = {}; @@ -128,4 +128,39 @@ Hooks.Popover = { }, }; +Hooks.CopyClipboard = { + mounted() { + initCopyClipboards(); + + const id = this.el.id; + const clipboard = FlowbiteInstances.getInstance('CopyClipboard', `${id}-code`); + + const $defaultMessage = document.getElementById(`${id}-default-message`); + const $successMessage = document.getElementById(`${id}-success-message`); + + clipboard.updateOnCopyCallback((clipboard) => { + showSuccess(); + + // reset to default state + setTimeout(() => { + resetToDefault(); + }, 2000); + }) + + const showSuccess = () => { + $defaultMessage.classList.add('hidden'); + $successMessage.classList.remove('hidden'); + } + + const resetToDefault = () => { + $defaultMessage.classList.remove('hidden'); + $successMessage.classList.add('hidden'); + } + }, + + updated() { + this.mounted(); + } +}; + export default Hooks; diff --git a/elixir/apps/web/lib/web/components/core_components.ex b/elixir/apps/web/lib/web/components/core_components.ex index 28cdf4b13..d160a4cea 100644 --- a/elixir/apps/web/lib/web/components/core_components.ex +++ b/elixir/apps/web/lib/web/components/core_components.ex @@ -69,7 +69,7 @@ defmodule Web.CoreComponents do def code_block(assigns) do ~H""" -
+
<%= render_slot(@inner_block) %>
- +
+ +
""" end diff --git a/elixir/apps/web/test/support/conn_case.ex b/elixir/apps/web/test/support/conn_case.ex index 53794866a..aeaed1ff9 100644 --- a/elixir/apps/web/test/support/conn_case.ex +++ b/elixir/apps/web/test/support/conn_case.ex @@ -292,7 +292,7 @@ defmodule Web.ConnCase do Floki.attribute(button, "disabled") != "disabled" end) |> elements_to_text() - |> Enum.reject(&(&1 in ["", "Previous", "Next", "Clear filters"])) + |> Enum.reject(&(&1 in ["", "Previous", "Next", "Clear filters", "CopyCopied"])) end ## Wait helpers