Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(IdPrefix): add component, docs, tests #12442

Merged
merged 11 commits into from
Nov 14, 2022
11 changes: 11 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -9984,6 +9994,7 @@ Map {
"unstable_useFeatureFlag" => Object {},
"unstable_useFeatureFlags" => Object {},
"unstable_useLayoutDirection" => Object {},
"useIdPrefix" => Object {},
"useLayer" => Object {},
"usePrefix" => Object {},
"useTheme" => Object {},
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ describe('Carbon Components React', () => {
"IconButton",
"IconSkeleton",
"IconTab",
"IdPrefix",
"InlineLoading",
"InlineNotification",
"Layer",
Expand Down Expand Up @@ -248,6 +249,7 @@ describe('Carbon Components React', () => {
"unstable_useFeatureFlag",
"unstable_useFeatureFlags",
"unstable_useLayoutDirection",
"useIdPrefix",
"useLayer",
"usePrefix",
"useTheme",
Expand Down
56 changes: 56 additions & 0 deletions packages/react/src/components/IdPrefix/IdPrefix.mdx
Original file line number Diff line number Diff line change
@@ -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)

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

## Table of Contents

- [Overview](#overview)
- [Component API](#component-api)
- [Feedback](#feedback)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Overview

The `IdPrefix` component is used to change the prefix applied to the
automatically generated `id` attributes placed on certain DOM elements.

<Preview>
<Story id="components-idprefix--default" />
</Preview>

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 (
<IdPrefix prefix="custom">
<Page />
</IdPrefix>
);
}
```

## Component API

<Props />

## 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).
37 changes: 37 additions & 0 deletions packages/react/src/components/IdPrefix/IdPrefix.stories.js
Original file line number Diff line number Diff line change
@@ -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 <p>The current id prefix is: {idPrefix}</p>;
}

return (
<>
<ExampleComponent />
<IdPrefix prefix="custom">
<ExampleComponent />
</IdPrefix>
</>
);
};
Original file line number Diff line number Diff line change
@@ -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(
<IdPrefix prefix="custom">
<TestComponent />
</IdPrefix>
);

expect(calls).toEqual(['custom']);
});
});
29 changes: 29 additions & 0 deletions packages/react/src/components/IdPrefix/index.js
Original file line number Diff line number Diff line change
@@ -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 (
<IdPrefixContext.Provider value={prefix}>
{children}
</IdPrefixContext.Provider>
);
}

IdPrefix.propTypes = {
children: PropTypes.node,

/**
* The value used to prefix the auto-generated id placed on some DOM elements
*/
prefix: PropTypes.string,
};

export { IdPrefix };
2 changes: 2 additions & 0 deletions packages/react/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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';
41 changes: 41 additions & 0 deletions packages/react/src/internal/__tests__/useIdPrefix-test.js
Original file line number Diff line number Diff line change
@@ -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(<TestComponent />);
expect(value).toBe(null);
});

it('should emit the prefix in context', () => {
function TestComponent() {
const contextValue = useIdPrefix();
return <span data-testid="test">{contextValue}</span>;
}

const { getByTestId } = render(
<IdPrefixContext.Provider value="test">
<TestComponent />
</IdPrefixContext.Provider>
);

expect(getByTestId('test')).toHaveTextContent('test');
});
});
3 changes: 2 additions & 1 deletion packages/react/src/internal/__tests__/usePrefix-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ describe('usePrefix', () => {

it('should emit the prefix in context', () => {
function TestComponent() {
return <span data-testid="test">test</span>;
const contextValue = usePrefix();
return <span data-testid="test">{contextValue}</span>;
}

const { getByTestId } = render(
Expand Down
7 changes: 5 additions & 2 deletions packages/react/src/internal/useId.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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]);

Expand Down
14 changes: 14 additions & 0 deletions packages/react/src/internal/useIdPrefix.js
Original file line number Diff line number Diff line change
@@ -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);
}