From 51f0257abeca3fc95325679ad7360ac811060c93 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 23 Jun 2021 17:37:17 -0500 Subject: [PATCH 1/3] feat(react): add support for layout and text direction --- .../src/components/Text/LayoutDirection.js | 44 +++++++++ .../react/src/components/Text/Text-story.js | 89 +++++++++++++++++++ packages/react/src/components/Text/Text.js | 52 +++++++++++ .../src/components/Text/TextDirection.js | 32 +++++++ packages/react/src/components/Text/index.js | 10 +++ 5 files changed, 227 insertions(+) create mode 100644 packages/react/src/components/Text/LayoutDirection.js create mode 100644 packages/react/src/components/Text/Text-story.js create mode 100644 packages/react/src/components/Text/Text.js create mode 100644 packages/react/src/components/Text/TextDirection.js create mode 100644 packages/react/src/components/Text/index.js diff --git a/packages/react/src/components/Text/LayoutDirection.js b/packages/react/src/components/Text/LayoutDirection.js new file mode 100644 index 000000000000..53dbe127d212 --- /dev/null +++ b/packages/react/src/components/Text/LayoutDirection.js @@ -0,0 +1,44 @@ +/** + * 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, { useContext } from 'react'; + +const LayoutDirectionContext = React.createContext('ltr'); + +function LayoutDirection({ + as: BaseComponent = 'div', + children, + direction, + ...rest +}) { + const parentDirection = useContext(LayoutDirectionContext); + if (parentDirection === direction) { + console.log('here?'); + return children; + } + + return ( + + + {children} + + + ); +} + +LayoutDirection.propTypes = { + as: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string, + PropTypes.elementType, + ]), + children: PropTypes.node, + direction: PropTypes.oneOf(['ltr', 'rtl']), +}; + +export { LayoutDirectionContext, LayoutDirection }; diff --git a/packages/react/src/components/Text/Text-story.js b/packages/react/src/components/Text/Text-story.js new file mode 100644 index 000000000000..11c8a77c818c --- /dev/null +++ b/packages/react/src/components/Text/Text-story.js @@ -0,0 +1,89 @@ +/** + * 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 { LayoutDirection, TextDirection, Text } from '../Text'; +import RadioButtonGroup from '../RadioButtonGroup'; +import RadioButton from '../RadioButton'; + +export default { + title: 'Experimental/unstable_Text', + parameters: { + component: Text, + }, +}; + +export const Default = () => ( + <> +

+ Hello world +

+

+ لكن لا بد أن أوضح لك أن كل +

+ +); + +export const LayoutAndText = () => ( + +

+ Ipsum ipsa repellat doloribus magni architecto totam Laborum maxime + ratione nobis voluptatibus facilis nostrum, necessitatibus magnam Maxime + esse consequatur nemo sit repellat Dignissimos rem nobis hic reprehenderit + ducimus? Fuga voluptatem? +

+ + + المغلوطة حول استنكار النشوة وتمجيد الألم نشأت بالفعل، وسأعرض لك التفاصيل + لتكتشف حقيقة وأساس تلك السعادة البشرية، فلا أحد يرفض أو يكره أو يتجنب + الشعور بالسعادة، ولكن بفضل هؤ. + + +

+ Ipsum ipsa repellat doloribus magni architecto totam Laborum maxime + ratione nobis voluptatibus facilis nostrum, necessitatibus magnam Maxime + esse consequatur nemo sit repellat Dignissimos rem nobis hic reprehenderit + ducimus? Fuga voluptatem? +

+
+); + +export const SetTextDirection = () => { + const legendText = 'הכותרת שלי!'; + + return ( + { + if (text === legendText) { + return 'ltr'; + } + return 'auto'; + }}> + + + + + + + ); +}; diff --git a/packages/react/src/components/Text/Text.js b/packages/react/src/components/Text/Text.js new file mode 100644 index 000000000000..a0e17d2715d0 --- /dev/null +++ b/packages/react/src/components/Text/Text.js @@ -0,0 +1,52 @@ +/** + * 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, { useContext } from 'react'; +import { TextDirectionContext } from './TextDirection'; + +function Text({ as: BaseComponent = 'span', children, dir = 'auto', ...rest }) { + const textDir = useContext(TextDirectionContext); + + if (textDir) { + const { direction, getTextDirection } = textDir; + + if (direction) { + return ( + + {children} + + ); + } + + if (getTextDirection) { + return ( + + {children} + + ); + } + } + + return ( + + {children} + + ); +} + +Text.propTypes = { + as: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.string, + PropTypes.elementType, + ]), + children: PropTypes.node.isRequired, + dir: PropTypes.oneOf(['ltr', 'rtl', 'auto']), +}; + +export { Text }; diff --git a/packages/react/src/components/Text/TextDirection.js b/packages/react/src/components/Text/TextDirection.js new file mode 100644 index 000000000000..7a4bea0e22bf --- /dev/null +++ b/packages/react/src/components/Text/TextDirection.js @@ -0,0 +1,32 @@ +/** + * 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'; + +const TextDirectionContext = React.createContext('auto'); + +function TextDirection({ children, dir, getTextDirection }) { + const value = { + direction: dir, + getTextDirection, + }; + + return ( + + {children} + + ); +} + +TextDirection.propTypes = { + children: PropTypes.node, + dir: PropTypes.oneOf(['ltr', 'rtl', 'auto']), + getTextDirection: PropTypes.func, +}; + +export { TextDirectionContext, TextDirection }; diff --git a/packages/react/src/components/Text/index.js b/packages/react/src/components/Text/index.js new file mode 100644 index 000000000000..9c63026bbfda --- /dev/null +++ b/packages/react/src/components/Text/index.js @@ -0,0 +1,10 @@ +/** + * 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. + */ + +export { LayoutDirectionContext, LayoutDirection } from './LayoutDirection'; +export { Text } from './Text'; +export { TextDirectionContext, TextDirection } from './TextDirection'; From c0efba05957ec386640f4d49c8a018e72e1aee7e Mon Sep 17 00:00:00 2001 From: Josh Black Date: Wed, 23 Jun 2021 17:37:37 -0500 Subject: [PATCH 2/3] refactor(react): update RadioButton to use Text --- packages/react/src/components/RadioButton/RadioButton.js | 3 ++- .../src/components/RadioButtonGroup/RadioButtonGroup.js | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react/src/components/RadioButton/RadioButton.js b/packages/react/src/components/RadioButton/RadioButton.js index 1160947d2076..2970af041e0d 100644 --- a/packages/react/src/components/RadioButton/RadioButton.js +++ b/packages/react/src/components/RadioButton/RadioButton.js @@ -11,6 +11,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { warning } from '../../internal/warning'; import uid from '../../tools/uniqueId'; +import { Text } from '../Text'; const { prefix } = settings; @@ -133,7 +134,7 @@ class RadioButton extends React.Component { /> ); diff --git a/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js b/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js index a65bd3450aa4..c81823b48f22 100644 --- a/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js +++ b/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js @@ -10,6 +10,7 @@ import React from 'react'; import classNames from 'classnames'; import { warning } from '../../internal/warning'; import { settings } from 'carbon-components'; +import { Text } from '../Text'; const { prefix } = settings; @@ -140,7 +141,9 @@ export default class RadioButtonGroup extends React.Component {
{legendText && ( - {legendText} + + {legendText} + )} {this.getRadioButtons()}
From f6d665bea347a670fff1d0b3b1d99f7dfb918ae4 Mon Sep 17 00:00:00 2001 From: Josh Black Date: Thu, 5 Aug 2021 18:14:13 -0500 Subject: [PATCH 3/3] feat(react): add text and layout dir components --- .../__snapshots__/DataTable-test.js.snap | 33 ++++-- .../{Text => Layout}/LayoutDirection.js | 35 +++--- .../Layout/LayoutDirectionContext.js | 12 +++ .../Layout/__tests__/LayoutDirection-test.js | 34 ++++++ .../__tests__/useLayoutDirection-test.js | 56 ++++++++++ packages/react/src/components/Layout/index.js | 9 ++ .../components/Layout/useLayoutDirection.js | 16 +++ .../RadioButtonGroup/RadioButtonGroup.js | 6 +- .../react/src/components/Text/Text-story.js | 7 +- packages/react/src/components/Text/Text.js | 83 ++++++++++---- .../src/components/Text/TextDirection.js | 35 ++++-- .../components/Text/TextDirectionContext.js | 10 ++ .../components/Text/__tests__/Text-test.js | 101 ++++++++++++++++++ .../Text/__tests__/TextDirection-test.js | 56 ++++++++++ .../components/Text/createTextComponent.js | 27 +++++ packages/react/src/components/Text/index.js | 11 +- 16 files changed, 472 insertions(+), 59 deletions(-) rename packages/react/src/components/{Text => Layout}/LayoutDirection.js (51%) create mode 100644 packages/react/src/components/Layout/LayoutDirectionContext.js create mode 100644 packages/react/src/components/Layout/__tests__/LayoutDirection-test.js create mode 100644 packages/react/src/components/Layout/__tests__/useLayoutDirection-test.js create mode 100644 packages/react/src/components/Layout/index.js create mode 100644 packages/react/src/components/Layout/useLayoutDirection.js create mode 100644 packages/react/src/components/Text/TextDirectionContext.js create mode 100644 packages/react/src/components/Text/__tests__/Text-test.js create mode 100644 packages/react/src/components/Text/__tests__/TextDirection-test.js create mode 100644 packages/react/src/components/Text/createTextComponent.js diff --git a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap index 534bb015cac6..3b24ce4a8d31 100644 --- a/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap +++ b/packages/react/src/components/DataTable/__tests__/__snapshots__/DataTable-test.js.snap @@ -591,11 +591,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = ` - - Select row - + + Select row + +
@@ -677,11 +682,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = ` - - Select row - + + Select row + + @@ -763,11 +773,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = ` - - Select row - + + Select row + + diff --git a/packages/react/src/components/Text/LayoutDirection.js b/packages/react/src/components/Layout/LayoutDirection.js similarity index 51% rename from packages/react/src/components/Text/LayoutDirection.js rename to packages/react/src/components/Layout/LayoutDirection.js index 53dbe127d212..b3f62963e3ee 100644 --- a/packages/react/src/components/Text/LayoutDirection.js +++ b/packages/react/src/components/Layout/LayoutDirection.js @@ -6,25 +6,24 @@ */ import PropTypes from 'prop-types'; -import React, { useContext } from 'react'; - -const LayoutDirectionContext = React.createContext('ltr'); +import React from 'react'; +import { LayoutDirectionContext } from './LayoutDirectionContext'; function LayoutDirection({ as: BaseComponent = 'div', children, - direction, + dir, ...rest }) { - const parentDirection = useContext(LayoutDirectionContext); - if (parentDirection === direction) { - console.log('here?'); - return children; - } + const value = React.useMemo(() => { + return { + direction: dir, + }; + }, [dir]); return ( - - + + {children} @@ -32,13 +31,25 @@ function LayoutDirection({ } LayoutDirection.propTypes = { + /** + * Customize the element type used to render the outermost node + */ as: PropTypes.oneOfType([ PropTypes.func, PropTypes.string, PropTypes.elementType, ]), + + /** + * Provide child elements or components to be rendered inside of this + * component + */ children: PropTypes.node, - direction: PropTypes.oneOf(['ltr', 'rtl']), + + /** + * Specify the layout direction of this part of the page + */ + dir: PropTypes.oneOf(['ltr', 'rtl']).isRequired, }; export { LayoutDirectionContext, LayoutDirection }; diff --git a/packages/react/src/components/Layout/LayoutDirectionContext.js b/packages/react/src/components/Layout/LayoutDirectionContext.js new file mode 100644 index 000000000000..19317c8f757e --- /dev/null +++ b/packages/react/src/components/Layout/LayoutDirectionContext.js @@ -0,0 +1,12 @@ +/** + * 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 LayoutDirectionContext = React.createContext({ + direction: 'ltr', +}); diff --git a/packages/react/src/components/Layout/__tests__/LayoutDirection-test.js b/packages/react/src/components/Layout/__tests__/LayoutDirection-test.js new file mode 100644 index 000000000000..f7da91da6aef --- /dev/null +++ b/packages/react/src/components/Layout/__tests__/LayoutDirection-test.js @@ -0,0 +1,34 @@ +/** + * 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, screen } from '@testing-library/react'; +import React from 'react'; +import { LayoutDirection } from '../'; + +describe('LayoutDirection', () => { + it('should render its children in a node that has a `dir` attribute', () => { + render( + + test + + ); + + const node = screen.getByTestId('test'); + expect(node).toHaveAttribute('dir', 'rtl'); + }); + + it('should support customizing the outermost node through the `as` prop', () => { + render( + + test + + ); + + const node = screen.getByTestId('test'); + expect(node.tagName).toBe('SECTION'); + }); +}); diff --git a/packages/react/src/components/Layout/__tests__/useLayoutDirection-test.js b/packages/react/src/components/Layout/__tests__/useLayoutDirection-test.js new file mode 100644 index 000000000000..4852e49f2e2d --- /dev/null +++ b/packages/react/src/components/Layout/__tests__/useLayoutDirection-test.js @@ -0,0 +1,56 @@ +/** + * 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 { LayoutDirection, useLayoutDirection } from '../'; + +describe('useLayoutDirection', () => { + it('should provide a default value', () => { + const calls = []; + + function TestComponent() { + const value = useLayoutDirection(); + calls.push(value); + return null; + } + + render(); + + expect(calls[0]).toEqual({ + direction: 'ltr', + }); + }); + + it('should provide the current direction from context', () => { + const calls = []; + + function TestComponent() { + const value = useLayoutDirection(); + calls.push(value); + return null; + } + + render( + + + + + + + ); + + expect(calls).toEqual([ + { + direction: 'rtl', + }, + { + direction: 'ltr', + }, + ]); + }); +}); diff --git a/packages/react/src/components/Layout/index.js b/packages/react/src/components/Layout/index.js new file mode 100644 index 000000000000..c7cd6d431bef --- /dev/null +++ b/packages/react/src/components/Layout/index.js @@ -0,0 +1,9 @@ +/** + * 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. + */ + +export { LayoutDirection } from './LayoutDirection'; +export { useLayoutDirection } from './useLayoutDirection'; diff --git a/packages/react/src/components/Layout/useLayoutDirection.js b/packages/react/src/components/Layout/useLayoutDirection.js new file mode 100644 index 000000000000..a72329a92271 --- /dev/null +++ b/packages/react/src/components/Layout/useLayoutDirection.js @@ -0,0 +1,16 @@ +/** + * 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 { useContext } from 'react'; +import { LayoutDirectionContext } from './LayoutDirectionContext'; + +/** + * Get access to the current layout direction in context + */ +export function useLayoutDirection() { + return useContext(LayoutDirectionContext); +} diff --git a/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js b/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js index 4b43b7d94089..80d9c751ca1a 100644 --- a/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js +++ b/packages/react/src/components/RadioButtonGroup/RadioButtonGroup.js @@ -10,7 +10,7 @@ import React from 'react'; import classNames from 'classnames'; import { warning } from '../../internal/warning'; import { settings } from 'carbon-components'; -import { Text } from '../Text'; +import { Legend } from '../Text'; const { prefix } = settings; @@ -149,9 +149,7 @@ export default class RadioButtonGroup extends React.Component {
{legendText && ( - - {legendText} - + {legendText} )} {this.getRadioButtons()}
diff --git a/packages/react/src/components/Text/Text-story.js b/packages/react/src/components/Text/Text-story.js index 11c8a77c818c..ea843a80f957 100644 --- a/packages/react/src/components/Text/Text-story.js +++ b/packages/react/src/components/Text/Text-story.js @@ -6,7 +6,8 @@ */ import React from 'react'; -import { LayoutDirection, TextDirection, Text } from '../Text'; +import { LayoutDirection } from '../Layout'; +import { TextDirection, Text } from '../Text'; import RadioButtonGroup from '../RadioButtonGroup'; import RadioButton from '../RadioButton'; @@ -29,14 +30,14 @@ export const Default = () => ( ); export const LayoutAndText = () => ( - +

Ipsum ipsa repellat doloribus magni architecto totam Laborum maxime ratione nobis voluptatibus facilis nostrum, necessitatibus magnam Maxime esse consequatur nemo sit repellat Dignissimos rem nobis hic reprehenderit ducimus? Fuga voluptatem?

- + المغلوطة حول استنكار النشوة وتمجيد الألم نشأت بالفعل، وسأعرض لك التفاصيل لتكتشف حقيقة وأساس تلك السعادة البشرية، فلا أحد يرفض أو يكره أو يتجنب diff --git a/packages/react/src/components/Text/Text.js b/packages/react/src/components/Text/Text.js index a0e17d2715d0..d2b25c670248 100644 --- a/packages/react/src/components/Text/Text.js +++ b/packages/react/src/components/Text/Text.js @@ -7,46 +7,89 @@ import PropTypes from 'prop-types'; import React, { useContext } from 'react'; -import { TextDirectionContext } from './TextDirection'; +import { TextDirectionContext } from './TextDirectionContext'; function Text({ as: BaseComponent = 'span', children, dir = 'auto', ...rest }) { - const textDir = useContext(TextDirectionContext); + const context = useContext(TextDirectionContext); + const textProps = {}; + const value = { + ...context, + }; - if (textDir) { - const { direction, getTextDirection } = textDir; + if (!context) { + textProps.dir = dir; + value.direction = dir; + } else { + const { direction: parentDirection, getTextDirection } = context; - if (direction) { - return ( - - {children} - - ); - } + if (getTextDirection && getTextDirection.current) { + const text = getTextFromChildren(children); + const override = getTextDirection.current(text); - if (getTextDirection) { - return ( - - {children} - - ); + if (parentDirection !== override) { + textProps.dir = override; + value.direction = override; + } else if (parentDirection === 'auto') { + textProps.dir = override; + } + } else if (parentDirection !== dir) { + textProps.dir = dir; + value.direction = dir; + } else if (parentDirection === 'auto') { + textProps.dir = dir; } } return ( - - {children} - + + + {children} + + ); } Text.propTypes = { + /** + * Provide a custom element type used to render the outermost node + */ as: PropTypes.oneOfType([ PropTypes.func, PropTypes.string, PropTypes.elementType, ]), + + /** + * Provide child elements or text to be rendered inside of this component + */ children: PropTypes.node.isRequired, + + /** + * Specify the text direction to be used for this component and any of its + * children + */ dir: PropTypes.oneOf(['ltr', 'rtl', 'auto']), }; +function getTextFromChildren(children) { + if (typeof children === 'string') { + return children; + } + + const text = React.Children.map(children, (child) => { + if (typeof child === 'string') { + return child; + } + return null; + }).filter((text) => { + return text !== null; + }); + + if (text.length === 1) { + return text[0]; + } + + return text; +} + export { Text }; diff --git a/packages/react/src/components/Text/TextDirection.js b/packages/react/src/components/Text/TextDirection.js index 7a4bea0e22bf..7e8411240790 100644 --- a/packages/react/src/components/Text/TextDirection.js +++ b/packages/react/src/components/Text/TextDirection.js @@ -6,15 +6,21 @@ */ import PropTypes from 'prop-types'; -import React from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; +import { TextDirectionContext } from './TextDirectionContext'; -const TextDirectionContext = React.createContext('auto'); +function TextDirection({ children, dir = 'auto', getTextDirection }) { + const savedCallback = useRef(getTextDirection); + const value = useMemo(() => { + return { + direction: dir, + getTextDirection: savedCallback, + }; + }, [dir]); -function TextDirection({ children, dir, getTextDirection }) { - const value = { - direction: dir, - getTextDirection, - }; + useEffect(() => { + savedCallback.current = getTextDirection; + }); return ( @@ -24,9 +30,22 @@ function TextDirection({ children, dir, getTextDirection }) { } TextDirection.propTypes = { + /** + * Provide children to be rendered inside of this component + */ children: PropTypes.node, + + /** + * Specify the text direction for rendered children + */ dir: PropTypes.oneOf(['ltr', 'rtl', 'auto']), + + /** + * Optionally provide a custom function to get the text direction for a piece + * of text. Whatever is returned will become the value of the `dir` attribute + * on a node of text. Should return one of: 'ltr', 'rtl', or 'auto' + */ getTextDirection: PropTypes.func, }; -export { TextDirectionContext, TextDirection }; +export { TextDirection }; diff --git a/packages/react/src/components/Text/TextDirectionContext.js b/packages/react/src/components/Text/TextDirectionContext.js new file mode 100644 index 000000000000..56df54803abb --- /dev/null +++ b/packages/react/src/components/Text/TextDirectionContext.js @@ -0,0 +1,10 @@ +/** + * 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 { createContext } from 'react'; + +export const TextDirectionContext = createContext(null); diff --git a/packages/react/src/components/Text/__tests__/Text-test.js b/packages/react/src/components/Text/__tests__/Text-test.js new file mode 100644 index 000000000000..76ede723cfc5 --- /dev/null +++ b/packages/react/src/components/Text/__tests__/Text-test.js @@ -0,0 +1,101 @@ +/** + * 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, screen } from '@testing-library/react'; +import React from 'react'; +import { TextDirection, Text } from '../'; + +describe('Text', () => { + it('should support specifying direction with the `dir` prop', () => { + render( + + test + + ); + + const node = screen.getByTestId('test'); + expect(node).toHaveAttribute('dir', 'rtl'); + }); + + it('should support custom elements with the `as` prop', () => { + render( + + test + + ); + + const node = screen.getByTestId('test'); + expect(node.tagName).toBe('P'); + }); + + it('should support spreading props onto the outermost node', () => { + render( + + test + + ); + + const node = screen.getByTestId('test'); + expect(node).toHaveAttribute('id', 'test'); + }); + + it('should not use redundant `dir` attributes on text nodes', () => { + render( + + outside{' '} + + inside + + + ); + + const outside = screen.getByTestId('outside'); + expect(outside).toHaveAttribute('dir', 'rtl'); + + const inside = screen.getByTestId('inside'); + expect(inside).not.toHaveAttribute('dir'); + }); + + it('should support overriding `dir` with `getTextDirection`', () => { + const getTextDirection = jest.fn().mockImplementation((input) => { + if (input === 'test') { + return 'auto'; + } + if (input === 'inner') { + return 'ltr'; + } + return 'rtl'; + }); + + render( + + test + + preinnerpost + + + inherit nested + + + ); + + expect(screen.getByTestId('flat')).toHaveAttribute('dir', 'auto'); + expect(getTextDirection).toHaveBeenCalledWith('test'); + + expect(screen.getByTestId('outer')).toHaveAttribute('dir', 'rtl'); + expect(getTextDirection).toHaveBeenCalledWith(['pre', 'post']); + + expect(screen.getByTestId('inner')).toHaveAttribute('dir', 'ltr'); + expect(getTextDirection).toHaveBeenCalledWith('inner'); + + expect(screen.getByTestId('inherit')).toHaveAttribute('dir', 'rtl'); + expect(getTextDirection).toHaveBeenCalledWith('inherit '); + + expect(screen.getByTestId('nested')).not.toHaveAttribute('dir'); + expect(getTextDirection).toHaveBeenCalledWith('nested'); + }); +}); diff --git a/packages/react/src/components/Text/__tests__/TextDirection-test.js b/packages/react/src/components/Text/__tests__/TextDirection-test.js new file mode 100644 index 000000000000..1510dae3f656 --- /dev/null +++ b/packages/react/src/components/Text/__tests__/TextDirection-test.js @@ -0,0 +1,56 @@ +/** + * 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 { TextDirection } from '../'; +import { TextDirectionContext } from '../TextDirectionContext'; + +describe('TextDirection', () => { + it('should set the direction in context', () => { + const calls = []; + + function TestComponent() { + const context = React.useContext(TextDirectionContext); + calls.push(context); + return null; + } + + const getTextDirection = jest.fn(); + + render( + + + + + + + + + + ); + + expect(calls[0]).toEqual({ + direction: 'auto', + getTextDirection: { + current: undefined, + }, + }); + expect(calls[1]).toEqual({ + direction: 'rtl', + getTextDirection: { + current: undefined, + }, + }); + expect(calls[2]).toEqual({ + direction: 'auto', + getTextDirection: { + current: getTextDirection, + }, + }); + }); +}); diff --git a/packages/react/src/components/Text/createTextComponent.js b/packages/react/src/components/Text/createTextComponent.js new file mode 100644 index 000000000000..df5306a23b35 --- /dev/null +++ b/packages/react/src/components/Text/createTextComponent.js @@ -0,0 +1,27 @@ +/** + * 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 { Text } from './Text'; + +/** + * Create a text component wrapper for a given text node type. Useful for + * returning a `Text` component for a text node like a `