From 8a9c11e34429b5190403010c4e19e5edc516f895 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Tue, 21 Jun 2022 17:52:56 -0500 Subject: [PATCH] feat(react): add useLayer hook (#11654) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> --- .../__snapshots__/PublicAPI-test.js.snap | 1 + packages/react/src/__tests__/index-test.js | 1 + .../Layer/{next => }/Layer-story.scss | 0 .../src/components/Layer/{next => }/Layer.mdx | 67 ++++++++++++------- .../Layer/{next => }/Layer.stories.js | 26 ++++++- .../Layer/__tests__/useLayer-test.js | 64 ++++++++++++++++++ packages/react/src/components/Layer/index.js | 12 ++++ packages/react/src/index.js | 2 +- 8 files changed, 145 insertions(+), 28 deletions(-) rename packages/react/src/components/Layer/{next => }/Layer-story.scss (100%) rename packages/react/src/components/Layer/{next => }/Layer.mdx (63%) rename packages/react/src/components/Layer/{next => }/Layer.stories.js (76%) create mode 100644 packages/react/src/components/Layer/__tests__/useLayer-test.js diff --git a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap index cad9213c6917..b4c8d31e0b3e 100644 --- a/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap +++ b/packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap @@ -8704,6 +8704,7 @@ Map { "unstable_useContextMenu" => Object {}, "unstable_useFeatureFlag" => Object {}, "unstable_useFeatureFlags" => 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 ea2f8fa3b406..7bd977da856a 100644 --- a/packages/react/src/__tests__/index-test.js +++ b/packages/react/src/__tests__/index-test.js @@ -230,6 +230,7 @@ describe('Carbon Components React', () => { "unstable_useContextMenu", "unstable_useFeatureFlag", "unstable_useFeatureFlags", + "useLayer", "usePrefix", "useTheme", ] diff --git a/packages/react/src/components/Layer/next/Layer-story.scss b/packages/react/src/components/Layer/Layer-story.scss similarity index 100% rename from packages/react/src/components/Layer/next/Layer-story.scss rename to packages/react/src/components/Layer/Layer-story.scss diff --git a/packages/react/src/components/Layer/next/Layer.mdx b/packages/react/src/components/Layer/Layer.mdx similarity index 63% rename from packages/react/src/components/Layer/next/Layer.mdx rename to packages/react/src/components/Layer/Layer.mdx index d987baae8762..076632505a92 100644 --- a/packages/react/src/components/Layer/next/Layer.mdx +++ b/packages/react/src/components/Layer/Layer.mdx @@ -10,6 +10,8 @@ import { ArgsTable, Canvas, Story } from '@storybook/addon-docs'; ## Table of Contents - [Overview](#overview) +- [Setting a custom level](#setting-a-custom-level) +- [Get the current layer](#get-the-current-layer) - [Component API](#component-api) - [Layer as](#layer-as) - [Feedback](#feedback) @@ -17,39 +19,17 @@ import { ArgsTable, Canvas, Story } from '@storybook/addon-docs'; -- You can nest layer components, up to the max -- TODO: can you explicitly set the layer -- What styles do I need to bring into my project for this to work -- How do I set the layer background? - ## Overview -The `Layer` component complements the layering model for the Carbon Design -System. It is used to render components on different "layers" in order to -correctly map contextual tokens to their correct values. In general, this means -that areas of a page that are "on top of" other areas of a page will need to use -this component. - -Any component that is rendered as a child to `Layer` will automatically map the -correct colors. For your own custom components to follow this model, make sure -they're using the contextual tokens. +The `Layer` component is used to render components on different layers. Each +layer has a specific set of token values associated with it. You can use these +tokens directly, or use contextual tokens from our styles package like `$layer` +or `$field`. -## Setting a custom level - - - - - -## Overview - -The `Layer` component is used to render components on different layers. Each -layer has a specific set of token values associated with it. You can use these -tokens directly, or use contextual tokens from our styles package like `$layer` -or `$field`. The `Layer` component accepts `children` as a prop. Each child of a `Layer` component is rendered using the layer tokens at that layer. `Layer` components @@ -67,6 +47,41 @@ can be nested indefinitely, but the token sets typically end after 3 layers. ``` +## Setting a custom level + +You can override the `level` of a `Layer` if you would like to change the presentation of a layer in your application. This is particularly helpful if you would like to reset the `Layer` level back to `0` or if you want to make sure a part of a page always renders in a certain level. + +To do this, you can use the `level` prop: + +```jsx + + + + + + +``` + +## Get the current layer + +If you are building a component and would like to know what layer the component +resides within, you can use the `useLayer` hook. + +This hook returns an object with the current `level` of the layer: + +```jsx +function ExampleComponent() { + const { level } = useLayer(); + + // ... +} +``` + +If the structure of `Layer` components in your app change, the hook will automatically update with the new `level`. + +The `level` value can be one of: `0`, `1`, or `2` and will correspond to the level of +the current layer. This value mirrors the `level` prop on the `Layer` component. + ## Component API diff --git a/packages/react/src/components/Layer/next/Layer.stories.js b/packages/react/src/components/Layer/Layer.stories.js similarity index 76% rename from packages/react/src/components/Layer/next/Layer.stories.js rename to packages/react/src/components/Layer/Layer.stories.js index ede39051917e..64aaef755c22 100644 --- a/packages/react/src/components/Layer/next/Layer.stories.js +++ b/packages/react/src/components/Layer/Layer.stories.js @@ -7,7 +7,7 @@ import './Layer-story.scss'; import React from 'react'; -import { Layer } from '../../Layer'; +import { Layer, useLayer } from '../Layer'; import mdx from './Layer.mdx'; export default { @@ -73,6 +73,30 @@ export const CustomLevel = () => { ); }; +export const UseLayer = () => { + function ExampleComponent() { + const { level } = useLayer(); + return ( +
+ The current layer level is: {level} +
+ ); + } + + return ( + <> + + + + + + ); +}; + +UseLayer.story = { + name: 'useLayer', +}; + const PlaygroundStory = (args) => { function TestComponent() { return
Test component
; diff --git a/packages/react/src/components/Layer/__tests__/useLayer-test.js b/packages/react/src/components/Layer/__tests__/useLayer-test.js new file mode 100644 index 000000000000..3817749a8cbd --- /dev/null +++ b/packages/react/src/components/Layer/__tests__/useLayer-test.js @@ -0,0 +1,64 @@ +/** + * 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 { Layer, useLayer } from '../../Layer'; + +describe('useLayer', () => { + test('default value', () => { + const values = []; + + function TestComponent() { + const value = useLayer(); + values.push(value); + return null; + } + + render(); + + expect(values).toEqual([ + { + level: 1, + }, + ]); + }); + + test('nesting', () => { + const values = []; + + function TestComponent() { + const value = useLayer(); + values.push(value); + return null; + } + + render( + <> + + + + + + + + + ); + + expect(values).toEqual([ + { + level: 1, + }, + { + level: 2, + }, + { + level: 2, + }, + ]); + }); +}); diff --git a/packages/react/src/components/Layer/index.js b/packages/react/src/components/Layer/index.js index f45052a50665..6a5ad3099833 100644 --- a/packages/react/src/components/Layer/index.js +++ b/packages/react/src/components/Layer/index.js @@ -14,6 +14,18 @@ import { LayerContext } from './LayerContext'; const levels = ['one', 'two', 'three']; const MAX_LEVEL = levels.length - 1; +/** + * A custom hook that will return information about the current layer. A common + * field to pull from this is the `level` for the layer that the component that + * calls this hook is currently in + */ +export function useLayer() { + const level = React.useContext(LayerContext); + return { + level, + }; +} + export function Layer({ as: BaseComponent = 'div', className: customClassName, diff --git a/packages/react/src/index.js b/packages/react/src/index.js index 6512df2c9c9c..cef8a5c8435b 100644 --- a/packages/react/src/index.js +++ b/packages/react/src/index.js @@ -214,7 +214,7 @@ export { } from './components/FeatureFlags'; export { Heading, Section } from './components/Heading'; export { IconButton } from './components/IconButton'; -export { Layer } from './components/Layer'; +export { Layer, useLayer } from './components/Layer'; export unstable_Menu, { MenuDivider as unstable_MenuDivider, MenuGroup as unstable_MenuGroup,