From 133d29493bb8e72131982bd03762406c4f286df7 Mon Sep 17 00:00:00 2001 From: Taylor Jones Date: Mon, 14 Nov 2022 14:35:08 -0600 Subject: [PATCH] feat(IdPrefix): add component, docs, tests (#12442) * feat(IdPrefix): add component, docs, tests * feat(IdPrefix): add component, docs, tests * fix(IdPrefix): update useId to use prefix, tests, and export IdPrefix * chore: update snaps * docs(IdPrefix): fix stories * test(snaps): update snap Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../__snapshots__/PublicAPI-test.js.snap | 11 ++++ packages/react/src/__tests__/index-test.js | 2 + .../src/components/IdPrefix/IdPrefix.mdx | 56 +++++++++++++++++++ .../components/IdPrefix/IdPrefix.stories.js | 37 ++++++++++++ .../IdPrefix/__tests__/IdPrefix-test.js | 31 ++++++++++ .../react/src/components/IdPrefix/index.js | 29 ++++++++++ packages/react/src/index.js | 2 + .../internal/__tests__/useIdPrefix-test.js | 41 ++++++++++++++ .../src/internal/__tests__/usePrefix-test.js | 3 +- packages/react/src/internal/useId.js | 7 ++- packages/react/src/internal/useIdPrefix.js | 14 +++++ 11 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 packages/react/src/components/IdPrefix/IdPrefix.mdx create mode 100644 packages/react/src/components/IdPrefix/IdPrefix.stories.js create mode 100644 packages/react/src/components/IdPrefix/__tests__/IdPrefix-test.js create mode 100644 packages/react/src/components/IdPrefix/index.js create mode 100644 packages/react/src/internal/__tests__/useIdPrefix-test.js create mode 100644 packages/react/src/internal/useIdPrefix.js diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index e03c76540c95..036acc7a161c 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -3935,6 +3935,16 @@ Map { }, "render": [Function], }, + "IdPrefix" => Object { + "propTypes": Object { + "children": Object { + "type": "node", + }, + "prefix": Object { + "type": "string", + }, + }, + }, "InlineLoading" => Object { "propTypes": Object { "className": Object { @@ -9984,6 +9994,7 @@ Map { "unstable_useFeatureFlag" => Object {}, "unstable_useFeatureFlags" => Object {}, "unstable_useLayoutDirection" => Object {}, + "useIdPrefix" => Object {}, "useLayer" => Object {}, "usePrefix" => Object {}, "useTheme" => Object {}, diff --git a/packages/react/src/__tests__/index-test.js b/packages/react/src/__tests__/index-test.js index b9c972bbd041..de21c298ff5f 100644 --- a/packages/react/src/__tests__/index-test.js +++ b/packages/react/src/__tests__/index-test.js @@ -87,6 +87,7 @@ describe('Carbon Components React', () => { "IconButton", "IconSkeleton", "IconTab", + "IdPrefix", "InlineLoading", "InlineNotification", "Layer", @@ -248,6 +249,7 @@ describe('Carbon Components React', () => { "unstable_useFeatureFlag", "unstable_useFeatureFlags", "unstable_useLayoutDirection", + "useIdPrefix", "useLayer", "usePrefix", "useTheme", diff --git a/packages/react/src/components/IdPrefix/IdPrefix.mdx b/packages/react/src/components/IdPrefix/IdPrefix.mdx new file mode 100644 index 000000000000..0c46810f3ab3 --- /dev/null +++ b/packages/react/src/components/IdPrefix/IdPrefix.mdx @@ -0,0 +1,56 @@ +import { Story, Props, Source, Preview } from '@storybook/addon-docs/blocks'; +import { IdPrefix } from '../IdPrefix'; + +# Prefix + +[Source code](https://github.com/carbon-design-system/carbon/tree/main/packages/react/src/components/ClassPrefix) + + + + +## Table of Contents + +- [Overview](#overview) +- [Component API](#component-api) +- [Feedback](#feedback) + + + +## Overview + +The `IdPrefix` component is used to change the prefix applied to the +automatically generated `id` attributes placed on certain DOM elements. + + + + + +This component is used intended to be used in limited cases, primarily only if +you have id conflics when using v10 and v11 packages at the same time during +migration. + +In React, you can use `IdPrefix` anywhere in your component tree and specify the +prefix with the `prefix` prop. Most often it's used in the project root wrapping +the entire project: + +```jsx +import { IdPrefix } from '@carbon/react'; + +export default function MyApp() { + return ( + + + + ); +} +``` + +## Component API + + + +## Feedback + +Help us improve this component by providing feedback, asking questions on Slack, +or updating this file on +[GitHub](https://github.com/carbon-design-system/carbon/edit/main/packages/react/src/components/ClassPrefix/ClassPrefix.mdx). diff --git a/packages/react/src/components/IdPrefix/IdPrefix.stories.js b/packages/react/src/components/IdPrefix/IdPrefix.stories.js new file mode 100644 index 000000000000..542daceadb99 --- /dev/null +++ b/packages/react/src/components/IdPrefix/IdPrefix.stories.js @@ -0,0 +1,37 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import { IdPrefix } from '.'; +import { useIdPrefix } from '../../internal/useIdPrefix'; +import mdx from './IdPrefix.mdx'; + +export default { + title: 'Components/IdPrefix', + component: IdPrefix, + parameters: { + docs: { + page: mdx, + }, + }, +}; + +export const Default = () => { + function ExampleComponent() { + const idPrefix = useIdPrefix(); + return

The current id prefix is: {idPrefix}

; + } + + return ( + <> + + + + + + ); +}; diff --git a/packages/react/src/components/IdPrefix/__tests__/IdPrefix-test.js b/packages/react/src/components/IdPrefix/__tests__/IdPrefix-test.js new file mode 100644 index 000000000000..74bb90faaeb0 --- /dev/null +++ b/packages/react/src/components/IdPrefix/__tests__/IdPrefix-test.js @@ -0,0 +1,31 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { render } from '@testing-library/react'; +import React from 'react'; +import { IdPrefix } from '../../IdPrefix'; +import { useIdPrefix } from '../../../internal/useIdPrefix'; + +describe('IdPrefix', () => { + it('should set the prefix value used by usePrefix', () => { + const calls = []; + + function TestComponent() { + const prefix = useIdPrefix(); + calls.push(prefix); + return null; + } + + render( + + + + ); + + expect(calls).toEqual(['custom']); + }); +}); diff --git a/packages/react/src/components/IdPrefix/index.js b/packages/react/src/components/IdPrefix/index.js new file mode 100644 index 000000000000..da32bdb3a7ec --- /dev/null +++ b/packages/react/src/components/IdPrefix/index.js @@ -0,0 +1,29 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import PropTypes from 'prop-types'; +import React from 'react'; +import { IdPrefixContext } from '../../internal/useIdPrefix'; + +function IdPrefix({ children, prefix }) { + return ( + + {children} + + ); +} + +IdPrefix.propTypes = { + children: PropTypes.node, + + /** + * The value used to prefix the auto-generated id placed on some DOM elements + */ + prefix: PropTypes.string, +}; + +export { IdPrefix }; diff --git a/packages/react/src/index.js b/packages/react/src/index.js index af64c6560312..57f33606e848 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -71,6 +71,7 @@ export FormGroup from './components/FormGroup'; export FormItem from './components/FormItem'; export FormLabel from './components/FormLabel'; export { Grid, Row, Column, ColumnHang, FlexGrid } from './components/Grid'; +export { IdPrefix } from './components/IdPrefix'; export InlineLoading from './components/InlineLoading'; export Link from './components/Link'; export ListItem from './components/ListItem'; @@ -271,3 +272,4 @@ export { export { DefinitionTooltip } from './components/Tooltip/next/DefinitionTooltip'; export { GlobalTheme, Theme, useTheme } from './components/Theme'; export { usePrefix } from './internal/usePrefix'; +export { useIdPrefix } from './internal/useIdPrefix'; diff --git a/packages/react/src/internal/__tests__/useIdPrefix-test.js b/packages/react/src/internal/__tests__/useIdPrefix-test.js new file mode 100644 index 000000000000..0f1b91d3c5bc --- /dev/null +++ b/packages/react/src/internal/__tests__/useIdPrefix-test.js @@ -0,0 +1,41 @@ +/** + * Copyright IBM Corp. 2020 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { cleanup, render } from '@testing-library/react'; +import React from 'react'; +import { useIdPrefix, IdPrefixContext } from '../useIdPrefix'; + +describe('usePrefix', () => { + afterEach(cleanup); + + it('should emit the default prefix without context', () => { + let value = null; + + function TestComponent() { + value = useIdPrefix(); + return null; + } + + render(); + expect(value).toBe(null); + }); + + it('should emit the prefix in context', () => { + function TestComponent() { + const contextValue = useIdPrefix(); + return {contextValue}; + } + + const { getByTestId } = render( + + + + ); + + expect(getByTestId('test')).toHaveTextContent('test'); + }); +}); diff --git a/packages/react/src/internal/__tests__/usePrefix-test.js b/packages/react/src/internal/__tests__/usePrefix-test.js index 4d7afab6278b..a855dd3dda0c 100644 --- a/packages/react/src/internal/__tests__/usePrefix-test.js +++ b/packages/react/src/internal/__tests__/usePrefix-test.js @@ -26,7 +26,8 @@ describe('usePrefix', () => { it('should emit the prefix in context', () => { function TestComponent() { - return test; + const contextValue = usePrefix(); + return {contextValue}; } const { getByTestId } = render( diff --git a/packages/react/src/internal/useId.js b/packages/react/src/internal/useId.js index 483716c0868b..bdd5e977ad77 100644 --- a/packages/react/src/internal/useId.js +++ b/packages/react/src/internal/useId.js @@ -26,6 +26,7 @@ import { useEffect, useLayoutEffect, useState } from 'react'; import setupGetInstanceId from '../tools/setupGetInstanceId'; import { canUseDOM } from './environment'; +import { useIdPrefix } from './useIdPrefix'; const getId = setupGetInstanceId(); const useIsomorphicLayoutEffect = canUseDOM ? useLayoutEffect : useEffect; @@ -38,16 +39,18 @@ let serverHandoffCompleted = false; * @returns {string} */ export function useId(prefix = 'id') { + const _prefix = useIdPrefix(); + const [id, setId] = useState(() => { if (serverHandoffCompleted) { - return `${prefix}-${getId()}`; + return `${_prefix ? `${_prefix}-` : ``}${prefix}-${getId()}`; } return null; }); useIsomorphicLayoutEffect(() => { if (id === null) { - setId(`${prefix}-${getId()}`); + setId(`${_prefix ? `${_prefix}-` : ``}${prefix}-${getId()}`); } }, [getId]); diff --git a/packages/react/src/internal/useIdPrefix.js b/packages/react/src/internal/useIdPrefix.js new file mode 100644 index 000000000000..15c603dbb704 --- /dev/null +++ b/packages/react/src/internal/useIdPrefix.js @@ -0,0 +1,14 @@ +/** + * Copyright IBM Corp. 2016, 2018 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; + +export const IdPrefixContext = React.createContext(null); + +export function useIdPrefix() { + return React.useContext(IdPrefixContext); +}