diff --git a/frontend/playwright/ui/specs/colorpicker.spec.js b/frontend/playwright/ui/specs/colorpicker.spec.js index e727f4aac7..a0e28eea07 100644 --- a/frontend/playwright/ui/specs/colorpicker.spec.js +++ b/frontend/playwright/ui/specs/colorpicker.spec.js @@ -94,7 +94,7 @@ test("Create a LINEAR gradient", async ({ page }) => { await expect(inputOpacityGlobal).toBeVisible(); await expect( - workspacePage.page.getByText("Linear gradient").nth(1), + workspacePage.page.getByText("Linear gradient") ).toBeVisible(); }); @@ -178,7 +178,7 @@ test("Create a RADIAL gradient", async ({ page }) => { await expect(inputOpacityGlobal).toBeVisible(); await expect( - workspacePage.page.getByText("Radial gradient").nth(1), + workspacePage.page.getByText("Radial gradient") ).toBeVisible(); }); diff --git a/frontend/playwright/ui/specs/tokens/apply.spec.js b/frontend/playwright/ui/specs/tokens/apply.spec.js index 2952a23c87..a05e28b2ff 100644 --- a/frontend/playwright/ui/specs/tokens/apply.spec.js +++ b/frontend/playwright/ui/specs/tokens/apply.spec.js @@ -83,7 +83,7 @@ test.describe("Tokens: Apply token", () => { await brTokenPillSM.click(); // Change token from dropdown - const brTokenOptionXl = borderRadiusSection.getByLabel("borderRadius.xl"); + const brTokenOptionXl = borderRadiusSection.getByRole('option', { name: 'borderRadius.xl' }) await expect(brTokenOptionXl).toBeVisible(); await brTokenOptionXl.click(); @@ -149,7 +149,7 @@ test.describe("Tokens: Apply token", () => { await detachButton.click(); // Open dropdown from input - const dropdownBtn = layerMenuSection.getByLabel("Open token list"); + const dropdownBtn = layerMenuSection.getByRole('button', { name: 'Open token list' }) await expect(dropdownBtn).toBeVisible(); await dropdownBtn.click(); @@ -225,8 +225,8 @@ test.describe("Tokens: Apply token", () => { await expect(firstShadowFields).toBeVisible(); // Fill in the shadow values - const offsetXInput = firstShadowFields.getByLabel("X"); - const offsetYInput = firstShadowFields.getByLabel("Y"); + const offsetXInput = firstShadowFields.getByRole('textbox', { name: 'X' }); + const offsetYInput = firstShadowFields.getByRole('textbox', { name: 'Y' }); const blurInput = firstShadowFields.getByRole("textbox", { name: "Blur", }); @@ -299,8 +299,8 @@ test.describe("Tokens: Apply token", () => { await expect(thirdShadowFields).toBeVisible(); // User adds values for the third shadow - const thirdOffsetXInput = thirdShadowFields.getByLabel("X"); - const thirdOffsetYInput = thirdShadowFields.getByLabel("Y"); + const thirdOffsetXInput = thirdShadowFields.getByRole('textbox', { name: 'X' }); + const thirdOffsetYInput = thirdShadowFields.getByRole('textbox', { name: 'Y' }); const thirdBlurInput = thirdShadowFields.getByRole("textbox", { name: "Blur", }); @@ -328,10 +328,10 @@ test.describe("Tokens: Apply token", () => { // Verify that the first shadow kept its values const firstOffsetXValue = await firstShadowFields - .getByLabel("X") + .getByRole('textbox', { name: 'X' }) .inputValue(); const firstOffsetYValue = await firstShadowFields - .getByLabel("Y") + .getByRole('textbox', { name: 'Y' }) .inputValue(); const firstBlurValue = await firstShadowFields .getByRole("textbox", { name: "Blur" }) @@ -357,10 +357,10 @@ test.describe("Tokens: Apply token", () => { await expect(newSecondShadowFields).toBeVisible(); const secondOffsetXValue = await newSecondShadowFields - .getByLabel("X") + .getByRole('textbox', { name: 'X' }) .inputValue(); const secondOffsetYValue = await newSecondShadowFields - .getByLabel("Y") + .getByRole('textbox', { name: 'Y' }) .inputValue(); const secondBlurValue = await newSecondShadowFields .getByRole("textbox", { name: "Blur" }) @@ -410,10 +410,10 @@ test.describe("Tokens: Apply token", () => { // Verify first shadow values are still there const restoredFirstOffsetX = await firstShadowFields - .getByLabel("X") + .getByRole('textbox', { name: 'X' }) .inputValue(); const restoredFirstOffsetY = await firstShadowFields - .getByLabel("Y") + .getByRole('textbox', { name: 'Y' }) .inputValue(); const restoredFirstBlur = await firstShadowFields .getByRole("textbox", { name: "Blur" }) @@ -433,10 +433,10 @@ test.describe("Tokens: Apply token", () => { // Verify second shadow values are still there const restoredSecondOffsetX = await newSecondShadowFields - .getByLabel("X") + .getByRole('textbox', { name: 'X' }) .inputValue(); const restoredSecondOffsetY = await newSecondShadowFields - .getByLabel("Y") + .getByRole('textbox', { name: 'Y' }) .inputValue(); const restoredSecondBlur = await newSecondShadowFields .getByRole("textbox", { name: "Blur" }) @@ -518,7 +518,7 @@ test.describe("Tokens: Apply token", () => { await dimensionSMTokenPill.nth(1).click(); // Change token from dropdown - const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl"); + const dimensionTokenOptionXl = measuresSection.getByRole('option', { name: 'dimension.xl' }) await expect(dimensionTokenOptionXl).toBeVisible(); await dimensionTokenOptionXl.click(); @@ -572,7 +572,7 @@ test.describe("Tokens: Apply token", () => { await dimensionSMTokenPill.click(); // Change token from dropdown - const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl"); + const dimensionTokenOptionXl = measuresSection.getByRole('option', { name: 'dimension.xl' }); await expect(dimensionTokenOptionXl).toBeVisible(); await dimensionTokenOptionXl.click(); @@ -626,7 +626,7 @@ test.describe("Tokens: Apply token", () => { await dimensionSMTokenPill.click(); // Change token from dropdown - const dimensionTokenOptionXl = measuresSection.getByLabel("dimension.xl"); + const dimensionTokenOptionXl = measuresSection.getByRole('option', { name: 'dimension.xl' }); await expect(dimensionTokenOptionXl).toBeVisible(); await dimensionTokenOptionXl.click(); @@ -682,7 +682,7 @@ test.describe("Tokens: Apply token", () => { // Change token from dropdown const dimensionTokenOptionXl = - borderRadiusSection.getByLabel("dimension.xl"); + borderRadiusSection.getByRole('option', { name: 'dimension.xl' }); await expect(dimensionTokenOptionXl).toBeVisible(); await dimensionTokenOptionXl.click(); @@ -751,7 +751,7 @@ test.describe("Tokens: Apply token", () => { }); await tokenDropdown.click(); - const widthOptionSmall = firstStrokeRow.getByLabel("width-small"); + const widthOptionSmall = firstStrokeRow.getByRole('option', { name: 'width-small' }); await expect(widthOptionSmall).toBeVisible(); await widthOptionSmall.click(); const StrokeWidthPillSmall = firstStrokeRow.getByRole("button", { @@ -831,15 +831,10 @@ test.describe("Tokens: Apply token", () => { }); await detachButton.click(); await expect(marginPillXL).not.toBeVisible(); - const horizontalMarginInput = layoutItemSectionSidebar.getByText( - "Horizontal marginOpen token", - ); - await expect(horizontalMarginInput).toBeVisible(); - - const tokenDropdown = horizontalMarginInput.getByRole("button", { + const horizontalMarginInput = layoutItemSectionSidebar.getByRole("button", { name: "Open token list", }); - await tokenDropdown.click(); + await horizontalMarginInput.nth(1).click(); await expect(dimensionTokenOptionXl).toBeVisible(); await dimensionTokenOptionXl.click(); diff --git a/frontend/playwright/ui/specs/tokens/crud.spec.js b/frontend/playwright/ui/specs/tokens/crud.spec.js index b726db042d..e96ef55ae8 100644 --- a/frontend/playwright/ui/specs/tokens/crud.spec.js +++ b/frontend/playwright/ui/specs/tokens/crud.spec.js @@ -1024,7 +1024,7 @@ test.describe("Tokens - creation", () => { const nameField = tokensUpdateCreateModal.getByLabel("Name"); await nameField.fill("typography.empty"); - const valueField = tokensUpdateCreateModal.getByLabel("Font Size"); + const valueField = tokensUpdateCreateModal.getByRole("textbox", {name: "Font Size"}); // Insert a value and then delete it await valueField.fill("1"); @@ -1716,12 +1716,12 @@ test.describe("Tokens tab - edition", () => { // Fill font-family to verify to verify that input value doesn't get split into list of characters const fontFamilyField = tokensUpdateCreateModal - .getByLabel("Font family") + .getByRole("textbox", { name: "Font family" }) .first(); await fontFamilyField.fill("OneWord"); // Invalidate incorrect values for font size - const fontSizeField = tokensUpdateCreateModal.getByLabel(/Font Size/i); + const fontSizeField = tokensUpdateCreateModal.getByRole("textbox", { name: "Font Size" }); await fontSizeField.fill("invalid"); await expect( tokensUpdateCreateModal.getByText(/Invalid token value:/), @@ -1736,13 +1736,13 @@ test.describe("Tokens tab - edition", () => { await fontSizeField.fill("16"); await expect(saveButton).toBeEnabled(); - const fontWeightField = tokensUpdateCreateModal.getByLabel(/Font Weight/i); + const fontWeightField = tokensUpdateCreateModal.getByRole("textbox", { name: "Font Weight" }); const letterSpacingField = - tokensUpdateCreateModal.getByLabel(/Letter Spacing/i); - const lineHeightField = tokensUpdateCreateModal.getByLabel(/Line Height/i); - const textCaseField = tokensUpdateCreateModal.getByLabel(/Text Case/i); + tokensUpdateCreateModal.getByRole("textbox", { name: "Letter Spacing" }); + const lineHeightField = tokensUpdateCreateModal.getByRole("textbox", { name: "Line Height" }); + const textCaseField = tokensUpdateCreateModal.getByRole("textbox", { name: "Text Case" }); const textDecorationField = - tokensUpdateCreateModal.getByLabel(/Text Decoration/i); + tokensUpdateCreateModal.getByRole("textbox", { name: "Text Decoration" }); // Capture all values before switching tabs const originalValues = { @@ -1800,6 +1800,7 @@ test.describe("Tokens tab - edition", () => { const colorToken = tokensSidebar.getByRole("button", { name: "100", }); + await expect(colorToken).toBeVisible(); await colorToken.click({ button: "right" }); diff --git a/frontend/src/app/main/ui/ds/buttons/icon_button.cljs b/frontend/src/app/main/ui/ds/buttons/icon_button.cljs index 3d766db460..1457dabc06 100644 --- a/frontend/src/app/main/ui/ds/buttons/icon_button.cljs +++ b/frontend/src/app/main/ui/ds/buttons/icon_button.cljs @@ -33,6 +33,8 @@ (let [variant (d/nilv variant "primary") + button-ref (mf/use-ref nil) + tooltip-id (mf/use-id) @@ -47,10 +49,12 @@ props (mf/spread-props props {:class [class button-class] + :ref button-ref :aria-labelledby tooltip-id})] [:> tooltip* {:content aria-label :class tooltip-class + :trigger-ref button-ref :placement tooltip-placement :id tooltip-id} [:> :button props diff --git a/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs b/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs index 3c13988cd3..18d5fc8a7d 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs +++ b/frontend/src/app/main/ui/ds/controls/shared/token_option.cljs @@ -28,7 +28,8 @@ {::mf/schema schema:token-option} [{:keys [id name on-click selected ref focused resolved] :rest props}] (let [internal-id (mf/use-id) - id (d/nilv id internal-id)] + id (d/nilv id internal-id) + element-ref (mf/use-ref nil)] [:li {:value id :class (stl/css-case :token-option true :option-with-pill true @@ -50,10 +51,12 @@ :aria-hidden (when name true)}] [:span {:class (stl/css :icon-placeholder)}]) [:> tooltip* {:content name + :trigger-ref element-ref :id (dm/str id "-name") :class (stl/css :option-text)} - ;; Add ellipsis - [:span {:aria-labelledby (dm/str id "-name")} + ;; Add ellipsis + [:span {:aria-labelledby (dm/str id "-name") + :ref element-ref} name]] (when resolved [:> :span {:class (stl/css :option-pill)} diff --git a/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs b/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs index 56ceedc848..58a3202c80 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs +++ b/frontend/src/app/main/ui/ds/controls/utilities/input_field.cljs @@ -84,6 +84,7 @@ :on-click on-icon-click}]) (if aria-label [:> tooltip* {:content aria-label + :trigger-ref (or ref input-ref) :class (stl/css :tooltip-wrapper) :id tooltip-id} [:> "input" props]] diff --git a/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs b/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs index 8f4920ae69..c008ce2bd0 100644 --- a/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs +++ b/frontend/src/app/main/ui/ds/controls/utilities/token_field.cljs @@ -43,6 +43,7 @@ (tr "ds.inputs.token-field.no-active-token-option")) default-id (mf/use-id) id (d/nilv id default-id) + pill-ref (mf/use-ref nil) focus-wrapper (mf/use-fn @@ -53,6 +54,7 @@ (dom/focus! (mf/ref-val token-wrapper-ref)))))] [:> tooltip* {:content property :class (stl/css :token-field-wrapper) + :trigger-ref token-wrapper-ref :id (dm/str default-id "-input")} [:div {:class [class (stl/css-case :token-field true :with-icon (some? slot-start) @@ -70,8 +72,10 @@ [:div {:class (stl/css :content-wrapper)} [:> tooltip* {:content content + :trigger-ref pill-ref :id (dm/str id "-pill")} [:button {:on-click on-click + :ref pill-ref :class (stl/css-case :pill true :no-set-pill (not set-active?) :pill-disabled disabled) diff --git a/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs b/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs index 707540fd95..690384647c 100644 --- a/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs +++ b/frontend/src/app/main/ui/ds/tooltip/tooltip.cljs @@ -6,7 +6,6 @@ (ns app.main.ui.ds.tooltip.tooltip (:require-macros - [app.common.data.macros :as dm] [app.main.style :as stl]) (:require [app.common.data :as d] @@ -15,10 +14,10 @@ [app.util.timers :as ts] [rumext.v2 :as mf])) -(def ^:private ^:const arrow-height 12) -(def ^:private ^:const half-arrow-height (/ arrow-height 2)) (def ^:private ^:const overlay-offset 32) +(defonce active-tooltip (atom nil)) + (defn- clear-schedule [ref] (when-let [schedule (mf/ref-val ref)] @@ -29,20 +28,6 @@ [ref delay f] (mf/set-ref-val! ref (ts/schedule delay f))) -(defn- show-popover - [node] - (when (.-isConnected ^js node) - (.showPopover ^js node))) - -(defn- hide-popover - [node] - (when (and (some? node) - (fn? (.-hidePopover node))) - (dom/unset-css-property! node "block-size") - (dom/unset-css-property! node "inset-block-start") - (dom/unset-css-property! node "inset-inline-start") - (.hidePopover ^js node))) - (defn- calculate-placement-bounding-rect "Given a placement, calcultates the bounding rect for it taking in account provided tooltip bounding rect and the origin bounding @@ -72,18 +57,18 @@ :height tooltip-height} "left" - {:top (- (+ trigger-top (/ trigger-height 2) half-arrow-height) (/ tooltip-height 2)) - :left (- trigger-left tooltip-width arrow-height) + {:top (- (+ trigger-top (/ trigger-height 2)) (/ tooltip-height 2)) + :left (- trigger-left tooltip-width) :right (+ (- trigger-left tooltip-width) tooltip-width) - :bottom (+ (- (+ trigger-top (/ trigger-height 2) half-arrow-height) (/ tooltip-height 2)) tooltip-height) + :bottom (+ (- (+ trigger-top (/ trigger-height 2)) (/ tooltip-height 2)) tooltip-height) :width tooltip-width :height tooltip-height} "right" - {:top (- (+ trigger-top (/ trigger-height 2) half-arrow-height) (/ tooltip-height 2)) + {:top (- (+ trigger-top (/ trigger-height 2)) (/ tooltip-height 2)) :left (+ trigger-right offset) :right (+ trigger-right offset tooltip-width) - :bottom (+ (- (+ trigger-top (/ trigger-height 2) half-arrow-height) (/ tooltip-height 2)) tooltip-height) + :bottom (+ (- (+ trigger-top (/ trigger-height 2)) (/ tooltip-height 2)) tooltip-height) :width tooltip-width :height tooltip-height} @@ -153,22 +138,6 @@ (recur (rest placements)) #js [placement placement-brect]))))) -(defn- update-tooltip-position - "Update the tooltip position having in account the current window - size, placement. It calculates the appropriate placement and updates - the dom with the result." - [tooltip placement origin-brect offset] - (show-popover tooltip) - (let [tooltip-brect (dom/get-bounding-rect tooltip) - tooltip-brect (assoc tooltip-brect :height (:height tooltip-brect) :width (:width tooltip-brect)) - window-size (dom/get-window-size)] - (when-let [[placement placement-rect] (find-matching-placement placement tooltip-brect origin-brect window-size offset)] - (let [height (:height placement-rect)] - (dom/set-css-property! tooltip "block-size" (dm/str height "px")) - (dom/set-css-property! tooltip "inset-block-start" (dm/str (:top placement-rect) "px")) - (dom/set-css-property! tooltip "inset-inline-start" (dm/str (:left placement-rect) "px"))) - placement))) - (def ^:private schema:tooltip [:map [:class {:optional true} [:maybe :string]] @@ -176,19 +145,26 @@ [:offset {:optional true} :int] [:delay {:optional true} :int] [:content [:or fn? :string map?]] + [:trigger-ref {:optional true} [:maybe :any]] [:placement {:optional true} [:maybe [:enum "top" "bottom" "left" "right" "top-right" "bottom-right" "bottom-left" "top-left"]]]]) (mf/defc tooltip* {::mf/schema schema:tooltip} - [{:keys [class id children content placement offset delay] :rest props}] + [{:keys [class id children content placement offset delay trigger-ref aria-label] :rest props}] (let [internal-id (mf/use-id) - trigger-ref (mf/use-ref nil) + internal-trigger-ref (mf/use-ref nil) + trigger-ref (or trigger-ref internal-trigger-ref) + + tooltip-ref (mf/use-ref nil) id (d/nilv id internal-id) + tooltip-id + (mf/use-id) + placement* (mf/use-state #(d/nilv placement "top")) @@ -201,35 +177,35 @@ schedule-ref (mf/use-ref nil) + visible* + (mf/use-state false) + visible (deref visible*) + on-show (mf/use-fn - (mf/deps id placement offset) - (fn [event] - - (let [current (dom/get-current-target event) - related (dom/get-related-target event) - is-node? (fn [node] (and node (.-nodeType node)))] - (when-not (and related (is-node? related) (.contains current related)) - (clear-schedule schedule-ref) - (when-let [tooltip (dom/get-element id)] - (let [origin-brect - (dom/get-bounding-rect (mf/ref-val trigger-ref)) - - update-position - (fn [] - (let [new-placement (update-tooltip-position tooltip placement origin-brect offset)] - (when (not= new-placement placement) - (reset! placement* new-placement))))] - - (add-schedule schedule-ref delay update-position))))))) + (mf/deps tooltip-id delay) + (fn [_] + (let [trigger-el (mf/ref-val trigger-ref)] + (clear-schedule schedule-ref) + (add-schedule schedule-ref (d/nilv delay 300) + (fn [] + (prn tooltip-id) + (when-let [active @active-tooltip] + (when (not= (:id active) tooltip-id) + (when-let [tooltip-el (dom/get-element (:id active))] + (dom/set-css-property! tooltip-el "display" "none")) + (reset! active-tooltip nil))) + (reset! active-tooltip {:id tooltip-id :trigger trigger-el}) + (reset! visible* true)))))) on-hide (mf/use-fn - (mf/deps id) + (mf/deps tooltip-id) (fn [] - (when-let [tooltip (dom/get-element id)] - (clear-schedule schedule-ref) - (hide-popover tooltip)))) + (clear-schedule schedule-ref) + (reset! visible* false) + (when (= (:id @active-tooltip) tooltip-id) + (reset! active-tooltip nil)))) handle-key-down (mf/use-fn @@ -250,28 +226,62 @@ :tooltip-bottom-left (identical? placement "bottom-left") :tooltip-top-left (identical? placement "top-left")) + content + (if (fn? content) + (content) + content) props (mf/spread-props props {:on-mouse-enter on-show :on-mouse-leave on-hide :on-focus on-show :on-blur on-hide + :ref internal-trigger-ref :on-key-down handle-key-down - :ref trigger-ref + :id id :class [class (stl/css :tooltip-trigger)] - :aria-describedby id}) - content - (if (fn? content) - (content) - content)] + :aria-label (if (string? content) + content + aria-label)})] + + (mf/use-effect + (mf/deps visible placement offset) + (fn [] + (when visible + (let [trigger-el (mf/ref-val trigger-ref) + tooltip-el (mf/ref-val tooltip-ref)] + (when (and trigger-el tooltip-el) + (js/requestAnimationFrame + (fn [] + (let [origin-brect (dom/get-bounding-rect trigger-el) + tooltip-brect (dom/get-bounding-rect tooltip-el) + window-size (dom/get-window-size)] + (when-let [[new-placement placement-rect] + (find-matching-placement + placement + tooltip-brect + origin-brect + window-size + offset)] + (dom/set-css-property! tooltip-el "inset-block-start" + (str (:top placement-rect) "px")) + (dom/set-css-property! tooltip-el "inset-inline-start" + (str (:left placement-rect) "px")) + + (when (not= new-placement placement) + (reset! placement* new-placement))))))))))) [:> :div props children - [:div {:class (stl/css :tooltip) - :id id - :popover "auto" - :role "tooltip"} - [:div {:class tooltip-class} - [:div {:class (stl/css :tooltip-content)} content] - [:div {:class (stl/css :tooltip-arrow) - :id "tooltip-arrow"}]]]])) + (when visible + (mf/portal + (mf/html + [:div {:class (stl/css :tooltip) + :role "tooltip" + :id tooltip-id + :ref tooltip-ref} + [:div {:class tooltip-class} + [:div {:class (stl/css :tooltip-content)} content] + [:div {:class (stl/css :tooltip-arrow) + :id "tooltip-arrow"}]]]) + (.-body js/document)))])) diff --git a/frontend/src/app/main/ui/ds/tooltip/tooltip.scss b/frontend/src/app/main/ui/ds/tooltip/tooltip.scss index 11df707d51..79fe80f774 100644 --- a/frontend/src/app/main/ui/ds/tooltip/tooltip.scss +++ b/frontend/src/app/main/ui/ds/tooltip/tooltip.scss @@ -6,17 +6,19 @@ @use "ds/_sizes.scss" as *; @use "ds/_borders.scss" as *; +@use "ds/z-index.scss" as *; @use "ds/typography.scss" as t; $arrow-side: 12px; .tooltip { - position: absolute; + position: fixed; max-inline-size: $sz-352; background-color: transparent; overflow: hidden; inline-size: fit-content; block-size: fit-content; + z-index: var(--z-index-notifications); } .tooltip-content-wrapper { diff --git a/frontend/src/app/main/ui/ds/utilities/swatch.cljs b/frontend/src/app/main/ui/ds/utilities/swatch.cljs index 3f1a605efa..40b54eb607 100644 --- a/frontend/src/app/main/ui/ds/utilities/swatch.cljs +++ b/frontend/src/app/main/ui/ds/utilities/swatch.cljs @@ -96,8 +96,9 @@ image (:image background) format (if id? "rounded" "square") element-id (mf/use-id) - has-opacity? (and (some? (:color background)) - (< (:opacity background) 1)) + has-opacity? (and (some? (:color background)) + (< (:opacity background) 1)) + element-ref (mf/use-ref nil) on-click (mf/use-fn (mf/deps background on-click) @@ -120,7 +121,8 @@ (mf/spread-props props {:class class :on-click on-click :type button-type - :aria-labelledby element-id}) + :aria-labelledby element-id + :ref element-ref}) children (mf/html [:> element-type props (cond @@ -147,6 +149,7 @@ [:> tooltip* {:content (if tooltip-content tooltip-content (color-title background)) + :trigger-ref element-ref :id element-id} children] diff --git a/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.cljs b/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.cljs index cc3042d9cb..ea67e04c7f 100644 --- a/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.cljs +++ b/frontend/src/app/main/ui/inspect/styles/property_detail_copiable.cljs @@ -23,11 +23,12 @@ (mf/defc property-detail-copiable* {::mf/schema schema:property-detail-copiable} - [{:keys [color token copied on-click children]}] + [{:keys [color token copied on-click children ref]}] [:button {:class (stl/css-case :property-detail-copiable true :property-detail-copied copied :property-detail-copiable-color (some? color)) - :on-click on-click} + :on-click on-click + :ref ref} (when color [:> swatch* {:background color :size "small"}]) diff --git a/frontend/src/app/main/ui/inspect/styles/rows/color_properties_row.cljs b/frontend/src/app/main/ui/inspect/styles/rows/color_properties_row.cljs index 3f192c3b82..145c452fc1 100644 --- a/frontend/src/app/main/ui/inspect/styles/rows/color_properties_row.cljs +++ b/frontend/src/app/main/ui/inspect/styles/rows/color_properties_row.cljs @@ -41,6 +41,7 @@ color-image-name (:name color-image) color-image-url (when (some? color-image) (cfg/resolve-file-media color-image)) + row-ref (mf/use-ref nil) color-opacity (mf/use-memo (mf/deps color) #(dm/str (-> color @@ -96,6 +97,7 @@ (if token [:> tooltip* {:id (:name token) :class (stl/css :tooltip-token-wrapper) + :trigger-ref row-ref :content #(mf/html [:div {:class (stl/css :tooltip-token)} [:div {:class (stl/css :tooltip-token-title)} @@ -104,6 +106,7 @@ (:resolved-value token)]])} [:> property-detail-copiable* {:color color :token token + :ref row-ref :copied copied :on-click copy-attr} formatted-color-value]] diff --git a/frontend/src/app/main/ui/inspect/styles/rows/properties_row.cljs b/frontend/src/app/main/ui/inspect/styles/rows/properties_row.cljs index 0a317d7339..9203305eac 100644 --- a/frontend/src/app/main/ui/inspect/styles/rows/properties_row.cljs +++ b/frontend/src/app/main/ui/inspect/styles/rows/properties_row.cljs @@ -37,6 +37,7 @@ copiable-value (if (some? token) (:name token) property) + row-ref (mf/use-ref nil) copy-attr (mf/use-fn @@ -54,6 +55,7 @@ (let [token-type (:type token)] [:> tooltip* {:id (:name token) :class (stl/css :tooltip-token-wrapper) + :trigger-ref row-ref :content #(mf/html [:div {:class (stl/css :tooltip-token)} [:div {:class (stl/css :tooltip-token-title)} @@ -75,6 +77,7 @@ (:resolved-value token))]])} [:> property-detail-copiable* {:token token :copied copied + :ref row-ref :on-click copy-attr} detail]]) [:> property-detail-copiable* {:copied copied :on-click copy-attr} detail]) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs index dfef56e0ac..89be5ed48f 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_tokens.cljs @@ -44,12 +44,15 @@ (on-token-pill-click event token))) id-tooltip (mf/use-id) resolved (:resolved-value token) - color-value (dwta/value->color resolved)] + color-value (dwta/value->color resolved) + item-ref (mf/use-ref nil)] [:> tooltip* {:id id-tooltip :style {:width "100%"} + :trigger-ref item-ref :content (:name token)} [:button {:class (stl/css-case :color-token-item true :color-token-selected selected) + :ref item-ref :aria-labelledby id-tooltip :on-click on-click} [:> swatch* {:background color-value diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index afe72d64fe..7e6e47241a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -94,6 +94,7 @@ not-active (or (empty? active-tokens) (nil? token)) id (dm/str (:id token) "-name") + token-name-ref (mf/use-ref nil) swatch-tooltip-content (cond not-active (tr "ds.inputs.token-field.no-active-token-option") @@ -126,8 +127,11 @@ :size "small"}]] [:> tooltip* {:content name-tooltip-content :id id + :aria-label (str (tr "workspace.tokens.token-name") ": " applied-token-name) + :trigger-ref token-name-ref :class (stl/css :token-tooltip)} [:div {:class (stl/css :token-name) + :ref token-name-ref :aria-labelledby id} (or token-name applied-token-name)]] [:div {:class (stl/css :token-actions)} diff --git a/frontend/src/app/main/ui/workspace/tokens/management/forms/modals.scss b/frontend/src/app/main/ui/workspace/tokens/management/forms/modals.scss index 666bbab5f9..c9dc66715a 100644 --- a/frontend/src/app/main/ui/workspace/tokens/management/forms/modals.scss +++ b/frontend/src/app/main/ui/workspace/tokens/management/forms/modals.scss @@ -4,17 +4,27 @@ // // Copyright (c) KALEIDOS INC -@use "refactor/common-refactor.scss" as deprecated; +@use "ds/_sizes.scss" as *; +@use "ds/_borders.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/z-index.scss" as *; .token-modal-wrapper { - @extend .modal-container-base; - @include deprecated.menuShadow; + border-radius: $br-4; + background-color: var(--color-background-primary); + border: $b-2 solid var(--color-background-quaternary); + min-width: $sz-364; + min-height: $sz-192; + max-width: $sz-512; + max-height: $sz-512; + box-shadow: 0px 0px $sz-12 0px var(--color-shadow-dark); position: absolute; width: auto; min-width: auto; - z-index: 11; + z-index: var(--z-index-set); overflow-y: auto; overflow-x: hidden; + padding: var(--sp-xxxl); &.token-modal-large { max-block-size: 95vh; } @@ -22,6 +32,6 @@ .close-btn { position: absolute; - top: deprecated.$s-6; - right: deprecated.$s-6; + top: px2rem(6); + right: px2rem(6); }