-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[wb1812.2.idcomponent] Add the Id component (#2389)
## Summary: This adds a simple CaaF (children-as-a-function) component to generate unique IDs as a stand-in for the `useId` hook. This is useful for cases where one needs to generate unique IDs for a component, but cannot use the hook without a larger refactor. Issue: WB-1812 ## Test plan: `yarn test` `yarn start:storybook` to check for the added docs `yarn typecheck` Author: somewhatabstract Reviewers: jeresig Required Reviewers: Approved By: jeresig Checks: ⌛ Prime node_modules cache for primary configuration (ubuntu-latest, 20.x), ⌛ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⌛ gerald, ⏭️ dependabot Pull Request URL: #2389
- Loading branch information
1 parent
b6009b7
commit 897686b
Showing
5 changed files
with
114 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@khanacademy/wonder-blocks-core": minor | ||
--- | ||
|
||
- Add the `Id` component for cases where `useId` cannot be used directly |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import * as React from "react"; | ||
|
||
import {Meta} from "@storybook/react"; | ||
import {View, Id} from "@khanacademy/wonder-blocks-core"; | ||
import {Body, BodyMonospace} from "@khanacademy/wonder-blocks-typography"; | ||
import {Strut} from "@khanacademy/wonder-blocks-layout"; | ||
import {spacing} from "@khanacademy/wonder-blocks-tokens"; | ||
|
||
export default { | ||
title: "Packages / Core / Id", | ||
|
||
parameters: { | ||
chromatic: { | ||
// We don't need a snapshot for this. | ||
disableSnapshot: true, | ||
}, | ||
}, | ||
} as Meta; | ||
|
||
export const GeneratedIdExample = () => ( | ||
<View> | ||
<Id> | ||
{(id) => ( | ||
<View style={{flexDirection: "row"}}> | ||
<Body>Generated identifier: </Body> | ||
<Strut size={spacing.xSmall_8} /> | ||
<BodyMonospace>{id}</BodyMonospace> | ||
</View> | ||
)} | ||
</Id> | ||
</View> | ||
); | ||
|
||
export const PassedThroughIdExample = () => ( | ||
<View> | ||
<Id id="my-identifier"> | ||
{(id) => ( | ||
<View style={{flexDirection: "row"}}> | ||
<Body>Passed through identifier: </Body> | ||
<Strut size={spacing.xSmall_8} /> | ||
<BodyMonospace>{id}</BodyMonospace> | ||
</View> | ||
)} | ||
</Id> | ||
</View> | ||
); |
28 changes: 28 additions & 0 deletions
28
packages/wonder-blocks-core/src/components/__tests__/id.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import * as React from "react"; | ||
|
||
import {render} from "@testing-library/react"; | ||
import {Id} from "../id"; | ||
|
||
describe("Id", () => { | ||
it("should provide an id to the children", () => { | ||
// Arrange | ||
const childrenFn = jest.fn().mockReturnValue(null); | ||
|
||
// Act | ||
render(<Id>{childrenFn}</Id>); | ||
|
||
// Assert | ||
expect(childrenFn).toHaveBeenCalledWith(expect.any(String)); | ||
}); | ||
|
||
it("should pass through the given id to the children", () => { | ||
// Arrange | ||
const childrenFn = jest.fn().mockReturnValue(null); | ||
|
||
// Act | ||
render(<Id id="my-id">{childrenFn}</Id>); | ||
|
||
// Assert | ||
expect(childrenFn).toHaveBeenCalledWith("my-id"); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import {useId} from "react"; | ||
import * as React from "react"; | ||
|
||
type Props = { | ||
/** | ||
* An identifier to use. | ||
* | ||
* If this is omitted, an identifier is generated. | ||
*/ | ||
id?: string | undefined; | ||
|
||
/** | ||
* A function that to render children with the given identifier. | ||
*/ | ||
children: (id: string) => React.ReactNode; | ||
}; | ||
|
||
/** | ||
* A component that provides an identifier to its children. | ||
* | ||
* If an `id` prop is provided, that is passed through to the children; | ||
* otherwise, a unique identifier is generated. | ||
*/ | ||
export const Id = ({id, children}: Props) => { | ||
const generatedId = useId(); | ||
|
||
// If we already have an ID, then use that. | ||
// Otherwise, use the generated ID. | ||
// NOTE: We can't call hooks conditionally, but it should be pretty cheap | ||
// to call useId() and not use the result, rather than the alternative | ||
// which would be to have a separate component for cases where we need | ||
// to call the hook and then render the component conditionally. | ||
return <>{children(id ?? generatedId)}</>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters