From de9c4715be589adb9c2b1a93cda8828243d541fd Mon Sep 17 00:00:00 2001 From: Bruno Henriques Date: Wed, 13 Nov 2024 11:50:45 +0000 Subject: [PATCH] fix(Input): adornment tooltip when disabled (#4423) --- .gitignore | 1 + packages/core/src/Button/Button.styles.ts | 12 +- .../core/src/Forms/Adornment/Adornment.tsx | 140 +++++++++--------- packages/core/src/Input/Input.styles.tsx | 8 +- packages/core/src/Input/Input.test.tsx | 6 +- 5 files changed, 87 insertions(+), 80 deletions(-) diff --git a/.gitignore b/.gitignore index 505098b678..37035791cd 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ lerna-debug.log* .env* # Build +build dist bin packages/icons/sprites diff --git a/packages/core/src/Button/Button.styles.ts b/packages/core/src/Button/Button.styles.ts index 007cc29440..bcb00bbb87 100644 --- a/packages/core/src/Button/Button.styles.ts +++ b/packages/core/src/Button/Button.styles.ts @@ -4,9 +4,7 @@ import { theme, type HvSize } from "@hitachivantara/uikit-styles"; import { outlineStyles } from "../utils/focusUtils"; export const { staticClasses, useClasses } = createClasses("HvButton", { - /** - * Classes applied to the root element - */ + /** applied to the root element */ root: { display: "inline-flex", alignItems: "center", @@ -35,17 +33,20 @@ export const { staticClasses, useClasses } = createClasses("HvButton", { borderRadius: `var(--radius, ${theme.radii.base})`, padding: theme.spacing(0, "sm"), }, + /** applied to the _left_ icon container */ startIcon: { marginLeft: theme.spacing(-1), marginTop: -1, marginBottom: -1, }, + /** applied to the _right_ icon container */ endIcon: { marginRight: theme.spacing(-1), marginTop: -1, marginBottom: -1, }, focusVisible: {}, + /** applied to the root element when disabled */ disabled: { cursor: "not-allowed", color: theme.colors.secondary_60, @@ -56,6 +57,7 @@ export const { staticClasses, useClasses } = createClasses("HvButton", { borderColor: "transparent", }, }, + /** applied to the root element when is icon-only */ icon: { margin: 0, padding: 0, @@ -64,6 +66,7 @@ export const { staticClasses, useClasses } = createClasses("HvButton", { margin: -1, }, }, + /** applied to the root element when using the `contained` variant */ contained: { color: theme.colors.atmo1, // `color-contrast(var(--color) vs ${colors.atmo1}, ${colors.base_light}, ${colors.base_dark})`, backgroundColor: "var(--color)", @@ -76,10 +79,13 @@ export const { staticClasses, useClasses } = createClasses("HvButton", { }, }, }, + /** applied to the root element when using the `subtle` variant */ subtle: { borderColor: "currentcolor", }, + /** applied to the root element when using the `ghost` variant */ ghost: {}, + /** applied to the root element when using the `semantic` variant */ semantic: { color: theme.colors.base_dark, backgroundColor: "transparent", diff --git a/packages/core/src/Forms/Adornment/Adornment.tsx b/packages/core/src/Forms/Adornment/Adornment.tsx index 9d7c7921e0..e7dad570f5 100644 --- a/packages/core/src/Forms/Adornment/Adornment.tsx +++ b/packages/core/src/Forms/Adornment/Adornment.tsx @@ -1,6 +1,10 @@ import { forwardRef, useContext } from "react"; -import { type ExtractNames } from "@hitachivantara/uikit-react-utils"; +import { + useDefaultProps, + type ExtractNames, +} from "@hitachivantara/uikit-react-utils"; +import { HvButtonBase } from "../../ButtonBase"; import { HvBaseProps } from "../../types/generic"; import { HvFormElementContext, @@ -46,77 +50,73 @@ export interface HvAdornmentProps export const HvAdornment = forwardRef< HTMLDivElement | HTMLButtonElement, HvAdornmentProps ->( - ( - { - id, - classes: classesProp, - className, - icon, - showWhen = undefined, - onClick, - isVisible = undefined, - ...others - }, - ref, - ) => { - const { classes, cx } = useClasses(classesProp); +>((props, ref) => { + const { + id, + classes: classesProp, + className, + icon, + showWhen = undefined, + onClick, + isVisible = undefined, + ...others + } = useDefaultProps("HvAdornment", props); + const { classes, cx } = useClasses(classesProp); - const { elementStatus = "", elementDisabled } = - useContext(HvFormElementContext); + const { elementStatus = "", elementDisabled } = + useContext(HvFormElementContext); - const { input } = useContext(HvFormElementDescriptorsContext); + const { input } = useContext(HvFormElementDescriptorsContext); - const displayIcon = - isVisible ?? (showWhen === undefined || elementStatus === showWhen); + const displayIcon = + isVisible ?? (showWhen === undefined || elementStatus === showWhen); - const isClickable = !!onClick; + const isClickable = !!onClick; - return isClickable ? ( - - ) : ( -
} - className={cx( - classes.root, - classes.adornment, - classes.adornmentIcon, - { - [classes.hideIcon]: !displayIcon, - [classes.disabled]: elementDisabled, - }, - className, - )} - role="presentation" - {...others} - > -
{icon}
-
- ); - }, -); + return isClickable ? ( + } + type="button" + tabIndex={-1} + aria-controls={input?.[0]?.id} + className={cx( + classes.root, + classes.adornment, + classes.adornmentButton, + { + [classes.hideIcon]: !displayIcon, + [classes.disabled]: elementDisabled, + }, + className, + )} + onClick={onClick} + onMouseDown={(event) => event.preventDefault()} + onKeyDown={noop} + disabled={elementDisabled} + {...others} + > +
{icon}
+
+ ) : ( +
} + className={cx( + classes.root, + classes.adornment, + classes.adornmentIcon, + { + [classes.hideIcon]: !displayIcon, + [classes.disabled]: elementDisabled, + }, + className, + )} + role="presentation" + {...others} + > +
{icon}
+
+ ); +}); diff --git a/packages/core/src/Input/Input.styles.tsx b/packages/core/src/Input/Input.styles.tsx index 2e916296d3..15a5121009 100644 --- a/packages/core/src/Input/Input.styles.tsx +++ b/packages/core/src/Input/Input.styles.tsx @@ -17,11 +17,9 @@ export const { staticClasses, useClasses } = createClasses("HvInput", { }, icon: { width: "30px", height: "30px" }, adornmentButton: { - backgroundColor: "transparent", - border: "none", - padding: 0, - margin: 0, - cursor: "pointer", + ":focus-visible,:hover": { + backgroundColor: "transparent", + }, }, iconClear: { display: "none" }, hasSuggestions: {}, diff --git a/packages/core/src/Input/Input.test.tsx b/packages/core/src/Input/Input.test.tsx index e32a81f1e3..e6cc2d3376 100644 --- a/packages/core/src/Input/Input.test.tsx +++ b/packages/core/src/Input/Input.test.tsx @@ -64,7 +64,8 @@ describe("Input", () => { ); expect(screen.getByRole("searchbox")).toBeDisabled(); - expect(screen.getByLabelText("Search")).toBeDisabled(); // role can't be used since the parent has aria-hidden + const adornment = screen.getByLabelText("Search"); // role can't be used since the parent has aria-hidden + expect(adornment).toHaveAttribute("aria-disabled", "true"); }); it("renders the adornment as disabled for the password when the input is disabled", () => { @@ -79,7 +80,8 @@ describe("Input", () => { ); expect(screen.getByLabelText("My input")).toBeDisabled(); // can't find by role searchbox since password inputs don't have a role... - expect(screen.getByLabelText("Reveal password")).toBeDisabled(); // role can't be used since the parent has aria-hidden + const adornment = screen.getByLabelText("Reveal password"); // roles can't be used since the parent has aria-hidden + expect(adornment).toHaveAttribute("aria-disabled", "true"); }); it("does not trigger the suggestions on focus by default", async () => {