From 56d961f1169ef1c3342ba131bf7a4b9845a16a51 Mon Sep 17 00:00:00 2001 From: Jeff Yates Date: Mon, 16 Dec 2024 09:26:02 -0600 Subject: [PATCH] [wb1812.3.migratewb] Migrate Wonder Blocks off old id providers (#2391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary: This is the last piece in the first batch of work. This migrates all Wonder Blocks components off our old ID providers and onto the new `Id` component. There are also some documentation tweaks to make the deprecation clearer in our stories, since that's some primary documentation for folks. With this PR, we can cut a release and then begin updating consumer repos accordingly. First, to include this update, then to migrate them off the old ways and onto the new. ### Release process: Once this small stack of PRs are landed and released, the following PRs need to be updated with that release, then landed/deployed: - Perseus: https://github.com/Khan/perseus/pull/2007 - Webapp - https://github.com/Khan/webapp/pull/28105 - https://github.com/Khan/webapp/pull/28127 Issue: WB-1812 ## Test plan: `yarn test` `yarn typecheck` `yarn start:storybook` Author: somewhatabstract Reviewers: jandrade, somewhatabstract Required Reviewers: Approved By: jandrade Checks: ⌛ Lint / Lint (ubuntu-latest, 20.x), ⌛ Check build sizes (ubuntu-latest, 20.x), ⌛ Chromatic - Build on regular PRs / chromatic (ubuntu-latest, 20.x), ⌛ Publish npm snapshot (ubuntu-latest, 20.x), ⌛ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⏭️ Chromatic - Skip on Release PR (changesets), ✅ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ✅ gerald, ⏭️ dependabot Pull Request URL: https://github.com/Khan/wonder-blocks/pull/2391 --- .changeset/witty-panthers-shave.md | 14 ++ __docs__/wonder-blocks-core/id.mdx | 39 ++++++ __docs__/wonder-blocks-core/use-unique-id.mdx | 8 +- .../with-action-scheduler.stories.tsx | 11 +- .../src/components/accordion-section.tsx | 14 +- .../src/components/id-provider.tsx | 6 +- .../wonder-blocks-core/src/components/id.tsx | 5 +- .../src/components/unique-id-provider.tsx | 6 +- .../components/__tests__/action-menu.test.tsx | 25 +--- .../__tests__/multi-select.test.tsx | 23 +--- .../__tests__/single-select.test.tsx | 23 +--- .../src/components/action-menu.tsx | 11 +- .../src/components/combobox.tsx | 20 ++- .../src/components/listbox.tsx | 13 +- .../src/components/multi-select.tsx | 11 +- .../src/components/single-select.tsx | 11 +- .../components/__tests__/checkbox.test.tsx | 2 +- .../__tests__/labeled-text-field.test.tsx | 20 ++- .../components/__tests__/text-area.test.tsx | 8 +- .../components/__tests__/text-field.test.tsx | 8 +- .../src/components/choice-internal.tsx | 25 ++-- .../src/components/labeled-text-field.tsx | 7 +- .../src/components/text-area.tsx | 8 +- .../src/components/text-field.tsx | 7 +- .../src/components/modal-header.tsx | 3 +- .../src/components/one-pane-dialog.tsx | 7 +- .../src/components/popover.tsx | 7 +- .../__tests__/search-field.test.tsx | 2 +- .../src/components/search-field.tsx | 7 +- .../src/components/switch.tsx | 14 +- .../adapters/__tests__/render-state.test.tsx | 8 +- .../src/components/__tests__/tooltip.test.tsx | 125 ++++++++---------- .../src/components/tooltip-anchor.tsx | 13 +- .../src/components/tooltip.tsx | 40 ++---- 34 files changed, 259 insertions(+), 292 deletions(-) create mode 100644 .changeset/witty-panthers-shave.md create mode 100644 __docs__/wonder-blocks-core/id.mdx diff --git a/.changeset/witty-panthers-shave.md b/.changeset/witty-panthers-shave.md new file mode 100644 index 000000000..10e609afb --- /dev/null +++ b/.changeset/witty-panthers-shave.md @@ -0,0 +1,14 @@ +--- +"@khanacademy/wonder-blocks-search-field": major +"@khanacademy/wonder-blocks-accordion": major +"@khanacademy/wonder-blocks-dropdown": major +"@khanacademy/wonder-blocks-popover": major +"@khanacademy/wonder-blocks-testing": major +"@khanacademy/wonder-blocks-tooltip": major +"@khanacademy/wonder-blocks-switch": major +"@khanacademy/wonder-blocks-modal": major +"@khanacademy/wonder-blocks-form": major +"@khanacademy/wonder-blocks-core": patch +--- + +- Migrate Wonder Blocks components off old id providers and onto new `Id` component diff --git a/__docs__/wonder-blocks-core/id.mdx b/__docs__/wonder-blocks-core/id.mdx new file mode 100644 index 000000000..e81a59ca3 --- /dev/null +++ b/__docs__/wonder-blocks-core/id.mdx @@ -0,0 +1,39 @@ +import * as React from "react"; +import {Meta, Story, Canvas} from "@storybook/blocks"; +import * as IdStories from "./id.stories"; + + + +# Id + +`Id` is a component that provides an identifier to its children. + +It is useful for situations where the `useId` hook cannot be easily used, +such as in class-based components. + +If an `id` prop is provided, that is passed through to the children; +otherwise, a unique identifier is generated. + +## Usage + +```tsx +import {Id} from "@khanacademy/wonder-blocks-core"; + +{(id) =>
Hello, world!
}
; +``` + +## Examples + +### 1. Generating an id + +An identifier will always be generated if an `id` prop is not provided, or the +provided `id` property is falsy. + + + +### 2. Passthrough an id + +If an `id` prop is provided and it is truthy, that value will be passed through +to the children. + + diff --git a/__docs__/wonder-blocks-core/use-unique-id.mdx b/__docs__/wonder-blocks-core/use-unique-id.mdx index a65d7fd2d..4891b23e2 100644 --- a/__docs__/wonder-blocks-core/use-unique-id.mdx +++ b/__docs__/wonder-blocks-core/use-unique-id.mdx @@ -1,10 +1,13 @@ import {Meta, Story, Canvas} from "@storybook/blocks"; -import * as UseUniqueIdStories from './use-unique-id.stories'; +import * as UseUniqueIdStories from "./use-unique-id.stories"; # `useUniqueIdWithoutMock` +DEPRECATED: Will be removed in a future release. Use `useId` from React or +the `Id` component. + This hook is similar to ``. It will return `null` on the initial render and then the same identifier factory for each subsequent render. The identifier factory is unique to @@ -19,6 +22,9 @@ render tree. # `useUniqueIdWithMock` +DEPRECATED: Will be removed in a future release. Use `useId` from React or +the `Id` component. + This hook is similar to ``. It will return a mock identifier factory on the initial render that doesn'that guarantee identifier uniqueness. Mock mode can help things appear on the screen diff --git a/__docs__/wonder-blocks-timing/with-action-scheduler.stories.tsx b/__docs__/wonder-blocks-timing/with-action-scheduler.stories.tsx index a95601300..e6ef598a0 100644 --- a/__docs__/wonder-blocks-timing/with-action-scheduler.stories.tsx +++ b/__docs__/wonder-blocks-timing/with-action-scheduler.stories.tsx @@ -1,7 +1,6 @@ -/* eslint-disable import/no-deprecated */ import * as React from "react"; import {Meta} from "@storybook/react"; -import {IDProvider, View} from "@khanacademy/wonder-blocks-core"; +import {Id, View} from "@khanacademy/wonder-blocks-core"; import { Unmounter, @@ -30,7 +29,7 @@ export default { } as Meta; export const IncorrectUsage = () => ( - + {(id) => ( @@ -39,11 +38,11 @@ export const IncorrectUsage = () => ( )} - + ); export const CorrectUsage = () => ( - + {(id) => ( @@ -52,5 +51,5 @@ export const CorrectUsage = () => ( )} - + ); diff --git a/packages/wonder-blocks-accordion/src/components/accordion-section.tsx b/packages/wonder-blocks-accordion/src/components/accordion-section.tsx index 3ccf97940..c9bc906a8 100644 --- a/packages/wonder-blocks-accordion/src/components/accordion-section.tsx +++ b/packages/wonder-blocks-accordion/src/components/accordion-section.tsx @@ -2,12 +2,12 @@ import * as React from "react"; import {StyleSheet} from "aphrodite"; import type {StyleDeclaration} from "aphrodite"; -// eslint-disable-next-line import/no-deprecated -import {useUniqueIdWithMock, View} from "@khanacademy/wonder-blocks-core"; +import {View} from "@khanacademy/wonder-blocks-core"; import * as tokens from "@khanacademy/wonder-blocks-tokens"; import {Body} from "@khanacademy/wonder-blocks-typography"; import type {AriaProps, StyleType} from "@khanacademy/wonder-blocks-core"; +import {useId} from "react"; import type {AccordionCornerKindType} from "./accordion"; import AccordionSectionHeader from "./accordion-section-header"; @@ -204,15 +204,15 @@ const AccordionSection = React.forwardRef(function AccordionSection( const controlledMode = expanded !== undefined && onToggle; - // eslint-disable-next-line import/no-deprecated - const ids = useUniqueIdWithMock(); - const sectionId = id ?? ids.get("accordion-section"); + const uniqueSectionId = useId(); + const sectionId = id ?? uniqueSectionId; // We need an ID for the header so that the content section's // aria-labelledby attribute can point to it. - const headerId = id ? `${id}-header` : ids.get("accordion-section-header"); + const uniqueHeaderId = useId(); + const headerId = id ? `${id}-header` : uniqueHeaderId; // We need an ID for the content section so that the opener's // aria-controls attribute can point to it. - const sectionContentUniqueId = ids.get("accordion-section-content"); + const sectionContentUniqueId = useId(); const sectionStyles = _generateStyles( cornerKind, diff --git a/packages/wonder-blocks-core/src/components/id-provider.tsx b/packages/wonder-blocks-core/src/components/id-provider.tsx index 7b87c9e26..c8f7f5d7b 100644 --- a/packages/wonder-blocks-core/src/components/id-provider.tsx +++ b/packages/wonder-blocks-core/src/components/id-provider.tsx @@ -28,6 +28,9 @@ type Props = { }; /** + * @deprecated This component is deprecated and will be removed in an + * upcoming release. Migrate existing code to use `useId` or the `Id` component. + * * This is a wrapper that returns an identifier. If the `id` prop is set, the * component will return the same id to be consumed by its children. Otherwise, * a unique id will be provided. This is beneficial for accessibility purposes, @@ -54,9 +57,6 @@ type Props = { * )} * * ``` - * - * @deprecated Use `useId` for your ID needs. If you are in a class-based - * component and cannot use hooks, then use the `Id` component. */ export default class IDProvider extends React.Component { static defaultId = "wb-id"; diff --git a/packages/wonder-blocks-core/src/components/id.tsx b/packages/wonder-blocks-core/src/components/id.tsx index 7bf054302..a058cff9c 100644 --- a/packages/wonder-blocks-core/src/components/id.tsx +++ b/packages/wonder-blocks-core/src/components/id.tsx @@ -16,7 +16,10 @@ type Props = { }; /** - * A component that provides an identifier to its children. + * `Id` is a component that provides an identifier to its children. + * + * It is useful for situations where the `useId` hook cannot be easily used, + * such as in class-based components. * * If an `id` prop is provided, that is passed through to the children; * otherwise, a unique identifier is generated. diff --git a/packages/wonder-blocks-core/src/components/unique-id-provider.tsx b/packages/wonder-blocks-core/src/components/unique-id-provider.tsx index fb086c4fd..520f2bec1 100644 --- a/packages/wonder-blocks-core/src/components/unique-id-provider.tsx +++ b/packages/wonder-blocks-core/src/components/unique-id-provider.tsx @@ -44,6 +44,9 @@ type Props = { }; /** + * @deprecated This component is deprecated and will be removed in an + * upcoming release. Migrate existing code to use `useId` or the `Id` component. + * * The `UniqueIDProvider` component is how Wonder Blocks components obtain * unique identifiers. This component ensures that server-side rendering and * initial client rendering match while allowing the provision of unique @@ -70,9 +73,6 @@ type Props = { * )} * * ``` - * - * @deprecated Use `useId` for your ID needs. If you are in a class-based - * component and cannot use hooks, then use the `Id` component. */ export default class UniqueIDProvider extends React.Component { // @ts-expect-error [FEI-5019] - TS2564 - Property '_idFactory' has no initializer and is not definitely assigned in the constructor. diff --git a/packages/wonder-blocks-dropdown/src/components/__tests__/action-menu.test.tsx b/packages/wonder-blocks-dropdown/src/components/__tests__/action-menu.test.tsx index a2d999cfd..61388ddaa 100644 --- a/packages/wonder-blocks-dropdown/src/components/__tests__/action-menu.test.tsx +++ b/packages/wonder-blocks-dropdown/src/components/__tests__/action-menu.test.tsx @@ -907,11 +907,8 @@ describe("ActionMenu", () => { const opener = await screen.findByRole("button"); // Assert - // Expect autogenerated id to be in the form uid-action-menu-opener-[number]-wb-id - expect(opener).toHaveAttribute( - "id", - expect.stringMatching(/^uid-action-menu-opener-\d+-wb-id$/), - ); + // Expect autogenerated id + expect(opener).toHaveAttribute("id", expect.any(String)); }); it("Should use the `id` prop if provided", async () => { @@ -929,6 +926,7 @@ describe("ActionMenu", () => { // Assert expect(opener).toHaveAttribute("id", id); }); + it("Should auto-generate an id for the dropdown if `dropdownId` prop is not provided", async () => { // Arrange render( @@ -945,10 +943,7 @@ describe("ActionMenu", () => { // Assert expect( await screen.findByRole("menu", {hidden: true}), - ).toHaveAttribute( - "id", - expect.stringMatching(/^uid-action-menu-dropdown-\d+-wb-id$/), - ); + ).toHaveAttribute("id", expect.any(String)); }); it("Should use the `dropdownId` prop if provided", async () => { @@ -1007,10 +1002,7 @@ describe("ActionMenu", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-action-menu-dropdown-\d+-wb-id$/), - ); + expect(dropdown.id).toBeString(); }); it("Should set the `aria-controls` attribute on the custom opener to the provided dropdownId prop", async () => { @@ -1035,7 +1027,7 @@ describe("ActionMenu", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute("aria-controls", dropdownId); + expect(dropdown.id).toBe(dropdownId); }); it("Should set the `aria-controls` attribute on the custom opener to the auto-generated dropdownId", async () => { @@ -1058,10 +1050,7 @@ describe("ActionMenu", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-action-menu-dropdown-\d+-wb-id$/), - ); + expect(dropdown.id).toBeString(); }); }); diff --git a/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx b/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx index db5f73572..3bf0ad1e3 100644 --- a/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx +++ b/packages/wonder-blocks-dropdown/src/components/__tests__/multi-select.test.tsx @@ -1828,11 +1828,9 @@ describe("MultiSelect", () => { const opener = await screen.findByRole("button"); // Assert - expect(opener).toHaveAttribute( - "id", - expect.stringMatching(/^uid-multi-select-opener-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("id", expect.any(String)); }); + it("Should use the `id` prop if provided", async () => { // Arrange const id = "test-id"; @@ -1849,6 +1847,7 @@ describe("MultiSelect", () => { // Assert expect(opener).toHaveAttribute("id", id); }); + it("Should auto-generate an id for the dropdown if `dropdownId` prop is not provided", async () => { // Arrange const {userEvent} = doRender( @@ -1866,11 +1865,9 @@ describe("MultiSelect", () => { // Assert expect( await screen.findByRole("listbox", {hidden: true}), - ).toHaveAttribute( - "id", - expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/), - ); + ).toHaveAttribute("id", expect.any(String)); }); + it("Should use the `dropdownId` prop if provided", async () => { // Arrange const dropdownId = "test-id"; @@ -1930,10 +1927,7 @@ describe("MultiSelect", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("aria-controls", expect.any(String)); }); it("Should set the `aria-controls` attribute on the custom opener to the provided dropdownId prop", async () => { @@ -1983,10 +1977,7 @@ describe("MultiSelect", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-multi-select-dropdown-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("aria-controls", expect.any(String)); }); }); diff --git a/packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx b/packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx index adf9b7068..cd0ced15b 100644 --- a/packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx +++ b/packages/wonder-blocks-dropdown/src/components/__tests__/single-select.test.tsx @@ -1396,11 +1396,9 @@ describe("SingleSelect", () => { const opener = await screen.findByRole("button"); // Assert - expect(opener).toHaveAttribute( - "id", - expect.stringMatching(/^uid-single-select-opener-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("id", expect.any(String)); }); + it("Should use the `id` prop if provided", async () => { // Arrange const id = "test-id"; @@ -1417,6 +1415,7 @@ describe("SingleSelect", () => { // Assert expect(opener).toHaveAttribute("id", id); }); + it("Should auto-generate an id for the dropdown if `dropdownId` prop is not provided", async () => { // Arrange const {userEvent} = doRender( @@ -1434,11 +1433,9 @@ describe("SingleSelect", () => { // Assert expect( await screen.findByRole("listbox", {hidden: true}), - ).toHaveAttribute( - "id", - expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/), - ); + ).toHaveAttribute("id", expect.any(String)); }); + it("Should use the `dropdownId` prop if provided", async () => { // Arrange const dropdownId = "test-id"; @@ -1506,10 +1503,7 @@ describe("SingleSelect", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("aria-controls", expect.any(String)); }); it("Should set the `aria-controls` attribute on the custom opener to the provided dropdownId prop", async () => { @@ -1561,10 +1555,7 @@ describe("SingleSelect", () => { // Assert expect(opener).toHaveAttribute("aria-controls", dropdown.id); - expect(opener).toHaveAttribute( - "aria-controls", - expect.stringMatching(/^uid-single-select-dropdown-\d+-wb-id$/), - ); + expect(opener).toHaveAttribute("aria-controls", expect.any(String)); }); }); diff --git a/packages/wonder-blocks-dropdown/src/components/action-menu.tsx b/packages/wonder-blocks-dropdown/src/components/action-menu.tsx index 91011864e..d5db71ee4 100644 --- a/packages/wonder-blocks-dropdown/src/components/action-menu.tsx +++ b/packages/wonder-blocks-dropdown/src/components/action-menu.tsx @@ -2,8 +2,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import {StyleSheet} from "aphrodite"; import { - // eslint-disable-next-line import/no-deprecated - IDProvider, + Id, type AriaProps, type StyleType, } from "@khanacademy/wonder-blocks-core"; @@ -256,7 +255,7 @@ export default class ActionMenu extends React.Component { const {disabled, menuText, opener, testId, id} = this.props; return ( - + {(uniqueOpenerId) => ( { }} )} - + ); } @@ -302,7 +301,7 @@ export default class ActionMenu extends React.Component { const items = this.getMenuItems(); return ( - + {(uniqueDropdownId) => ( { dropdownStyle={[styles.menuTopSpace, dropdownStyle]} /> )} - + ); } } diff --git a/packages/wonder-blocks-dropdown/src/components/combobox.tsx b/packages/wonder-blocks-dropdown/src/components/combobox.tsx index 890b03e54..88fe56172 100644 --- a/packages/wonder-blocks-dropdown/src/components/combobox.tsx +++ b/packages/wonder-blocks-dropdown/src/components/combobox.tsx @@ -4,12 +4,7 @@ import * as React from "react"; import caretDownIcon from "@phosphor-icons/core/regular/caret-down.svg"; import xIcon from "@phosphor-icons/core/regular/x.svg"; -import { - StyleType, - // eslint-disable-next-line import/no-deprecated - useUniqueIdWithMock, - View, -} from "@khanacademy/wonder-blocks-core"; +import {StyleType, View} from "@khanacademy/wonder-blocks-core"; import {TextField} from "@khanacademy/wonder-blocks-form"; import IconButton from "@khanacademy/wonder-blocks-icon-button"; import { @@ -21,6 +16,7 @@ import { import {DetailCell} from "@khanacademy/wonder-blocks-cell"; import {PhosphorIcon} from "@khanacademy/wonder-blocks-icon"; +import {useId} from "react"; import {useListbox} from "../hooks/use-listbox"; import {useMultipleSelection} from "../hooks/use-multiple-selection"; import { @@ -177,8 +173,8 @@ export default function Combobox({ value = "", }: Props) { // eslint-disable-next-line import/no-deprecated - const ids = useUniqueIdWithMock("combobox"); - const uniqueId = id ?? ids.get("listbox"); + const generatedUniqueId = useId(); + const uniqueId = id ?? generatedUniqueId; // Ref to the combobox input element. const comboboxRef = React.useRef(null); // Ref to the top-level node of the combobox. @@ -516,8 +512,8 @@ export default function Combobox({ return {startIconElement}; }; - - const pillIdPrefix = id ? `${id}-pill-` : ids.get("pill"); + const pillIdPrefix = `${uniqueId}-pill-`; + const textFieldId = useId(); const currentActiveDescendant = !openState ? undefined @@ -577,7 +573,7 @@ export default function Combobox({ {maybeRenderStartIcon()} {renderList} diff --git a/packages/wonder-blocks-dropdown/src/components/listbox.tsx b/packages/wonder-blocks-dropdown/src/components/listbox.tsx index fd64b2380..23eafbf1a 100644 --- a/packages/wonder-blocks-dropdown/src/components/listbox.tsx +++ b/packages/wonder-blocks-dropdown/src/components/listbox.tsx @@ -1,13 +1,9 @@ import * as React from "react"; import {StyleSheet} from "aphrodite"; -import { - StyleType, - // eslint-disable-next-line import/no-deprecated - useUniqueIdWithMock, - View, -} from "@khanacademy/wonder-blocks-core"; +import {StyleType, View} from "@khanacademy/wonder-blocks-core"; import {color} from "@khanacademy/wonder-blocks-tokens"; +import {useId} from "react"; import {useListbox} from "../hooks/use-listbox"; import {MaybeValueOrValues, OptionItemComponent} from "../util/types"; @@ -104,9 +100,8 @@ function StandaloneListbox(props: Props) { "aria-labelledby": ariaLabelledby, } = props; - // eslint-disable-next-line import/no-deprecated - const ids = useUniqueIdWithMock("listbox"); - const uniqueId = id ?? ids.get("id"); + const generatedUniqueId = useId(); + const uniqueId = id ?? generatedUniqueId; const { focusedIndex, diff --git a/packages/wonder-blocks-dropdown/src/components/multi-select.tsx b/packages/wonder-blocks-dropdown/src/components/multi-select.tsx index e749b05fe..43090a503 100644 --- a/packages/wonder-blocks-dropdown/src/components/multi-select.tsx +++ b/packages/wonder-blocks-dropdown/src/components/multi-select.tsx @@ -2,8 +2,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import { - // eslint-disable-next-line import/no-deprecated - IDProvider, + Id, type AriaProps, type StyleType, } from "@khanacademy/wonder-blocks-core"; @@ -546,7 +545,7 @@ const MultiSelect = (props: Props) => { const menuText = getMenuText(allChildren); const dropdownOpener = ( - + {(uniqueOpenerId) => { return opener ? ( { ); }} - + ); return dropdownOpener; @@ -602,7 +601,7 @@ const MultiSelect = (props: Props) => { const isDisabled = numEnabledOptions === 0 || disabled; return ( - + {(uniqueDropdownId) => ( { disabled={isDisabled} /> )} - + ); }; diff --git a/packages/wonder-blocks-dropdown/src/components/single-select.tsx b/packages/wonder-blocks-dropdown/src/components/single-select.tsx index dc4660276..27a9fa429 100644 --- a/packages/wonder-blocks-dropdown/src/components/single-select.tsx +++ b/packages/wonder-blocks-dropdown/src/components/single-select.tsx @@ -2,8 +2,7 @@ import * as React from "react"; import * as ReactDOM from "react-dom"; import { - // eslint-disable-next-line import/no-deprecated - IDProvider, + Id, type AriaProps, type StyleType, } from "@khanacademy/wonder-blocks-core"; @@ -444,7 +443,7 @@ const SingleSelect = (props: Props) => { : placeholder; const dropdownOpener = ( - + {(uniqueOpenerId) => { return opener ? ( { ); }} - + ); return dropdownOpener; @@ -498,7 +497,7 @@ const SingleSelect = (props: Props) => { const isDisabled = numEnabledOptions === 0 || disabled; return ( - + {(uniqueDropdownId) => ( { disabled={isDisabled} /> )} - + ); }; diff --git a/packages/wonder-blocks-form/src/components/__tests__/checkbox.test.tsx b/packages/wonder-blocks-form/src/components/__tests__/checkbox.test.tsx index fc08706e1..91d3a5257 100644 --- a/packages/wonder-blocks-form/src/components/__tests__/checkbox.test.tsx +++ b/packages/wonder-blocks-form/src/components/__tests__/checkbox.test.tsx @@ -36,7 +36,7 @@ describe("Checkbox", () => { const checkbox = screen.getByRole("checkbox"); // Assert - expect(checkbox).toHaveAttribute("id", "uid-choice-1-main"); + expect(checkbox).toHaveAttribute("id", expect.any(String)); }); test("clicking the checkbox triggers `onChange`", () => { diff --git a/packages/wonder-blocks-form/src/components/__tests__/labeled-text-field.test.tsx b/packages/wonder-blocks-form/src/components/__tests__/labeled-text-field.test.tsx index d91bdc708..1e06b2b59 100644 --- a/packages/wonder-blocks-form/src/components/__tests__/labeled-text-field.test.tsx +++ b/packages/wonder-blocks-form/src/components/__tests__/labeled-text-field.test.tsx @@ -58,15 +58,13 @@ describe("LabeledTextField", () => { // Act render( {}} />); + const input = await screen.findByRole("textbox"); + const result = input.getAttribute("id"); // Assert // Since the generated id is unique, we cannot know what it will be. We - // only test if the id attribute starts with "uid-", then followed by - // "text-field-" as the scope assigned to IDProvider. - const input = await screen.findByRole("textbox"); - expect(input.getAttribute("id")).toMatch( - /uid-labeled-text-field.*-field/, - ); + // only that it ends with "-field". + expect(result).toMatch(/.*-field/); }); it("type prop is passed to input", async () => { @@ -207,16 +205,14 @@ describe("LabeledTextField", () => { // ariaDescribedby is not passed in />, ); + const input = await screen.findByRole("textbox"); + const result = input.getAttribute("aria-describedby"); // Assert // Since the generated aria-describedby is unique, // we cannot know what it will be. - // We only test if the aria-describedby attribute starts with - // "uid-" and ends with "-error". - const input = await screen.findByRole("textbox"); - expect(input.getAttribute("aria-describedby")).toMatch( - /^uid-.*-error$/, - ); + // We only test if the aria-describedby attribute ends with "-error". + expect(result).toMatch(/^.*-error$/); }); it("validate prop is called when input changes", async () => { diff --git a/packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx b/packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx index cfb7c6e94..089d061f8 100644 --- a/packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx +++ b/packages/wonder-blocks-form/src/components/__tests__/text-area.test.tsx @@ -38,13 +38,13 @@ describe("TextArea", () => { // Act render(