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);
+}