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(react): add support for layout and text direction #9013

Merged
merged 8 commits into from
Aug 12, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -591,11 +591,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = `
<span
className="bx--radio-button__appearance"
/>
<span
<Text
className="bx--visually-hidden"
>
Select row
</span>
<span
className="bx--visually-hidden"
dir="auto"
>
Select row
</span>
</Text>
</label>
</div>
</RadioButton>
Expand Down Expand Up @@ -677,11 +682,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = `
<span
className="bx--radio-button__appearance"
/>
<span
<Text
className="bx--visually-hidden"
>
Select row
</span>
<span
className="bx--visually-hidden"
dir="auto"
>
Select row
</span>
</Text>
</label>
</div>
</RadioButton>
Expand Down Expand Up @@ -763,11 +773,16 @@ exports[`DataTable selection -- radio buttons should render 1`] = `
<span
className="bx--radio-button__appearance"
/>
<span
<Text
className="bx--visually-hidden"
>
Select row
</span>
<span
className="bx--visually-hidden"
dir="auto"
>
Select row
</span>
</Text>
</label>
</div>
</RadioButton>
Expand Down
55 changes: 55 additions & 0 deletions packages/react/src/components/Layout/LayoutDirection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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 { LayoutDirectionContext } from './LayoutDirectionContext';

function LayoutDirection({
as: BaseComponent = 'div',
children,
dir,
...rest
}) {
const value = React.useMemo(() => {
return {
direction: dir,
};
}, [dir]);

return (
<LayoutDirectionContext.Provider value={value}>
<BaseComponent dir={dir} {...rest}>
{children}
</BaseComponent>
</LayoutDirectionContext.Provider>
);
}

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,

/**
* Specify the layout direction of this part of the page
*/
dir: PropTypes.oneOf(['ltr', 'rtl']).isRequired,
};

export { LayoutDirectionContext, LayoutDirection };
12 changes: 12 additions & 0 deletions packages/react/src/components/Layout/LayoutDirectionContext.js
Original file line number Diff line number Diff line change
@@ -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',
});
Original file line number Diff line number Diff line change
@@ -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(
<LayoutDirection dir="rtl" data-testid="test">
<span>test</span>
</LayoutDirection>
);

const node = screen.getByTestId('test');
expect(node).toHaveAttribute('dir', 'rtl');
});

it('should support customizing the outermost node through the `as` prop', () => {
render(
<LayoutDirection as="section" data-testid="test" dir="rtl">
<span>test</span>
</LayoutDirection>
);

const node = screen.getByTestId('test');
expect(node.tagName).toBe('SECTION');
});
});
Original file line number Diff line number Diff line change
@@ -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(<TestComponent />);

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(
<LayoutDirection dir="rtl">
<TestComponent />
<LayoutDirection dir="ltr">
<TestComponent />
</LayoutDirection>
</LayoutDirection>
);

expect(calls).toEqual([
{
direction: 'rtl',
},
{
direction: 'ltr',
},
]);
});
});
9 changes: 9 additions & 0 deletions packages/react/src/components/Layout/index.js
Original file line number Diff line number Diff line change
@@ -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';
16 changes: 16 additions & 0 deletions packages/react/src/components/Layout/useLayoutDirection.js
Original file line number Diff line number Diff line change
@@ -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);
}
3 changes: 2 additions & 1 deletion packages/react/src/components/RadioButton/RadioButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -133,7 +134,7 @@ class RadioButton extends React.Component {
/>
<label htmlFor={this.uid} className={`${prefix}--radio-button__label`}>
<span className={`${prefix}--radio-button__appearance`} />
{labelText && <span className={innerLabelClasses}>{labelText}</span>}
{labelText && <Text className={innerLabelClasses}>{labelText}</Text>}
</label>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import React from 'react';
import classNames from 'classnames';
import { warning } from '../../internal/warning';
import { settings } from 'carbon-components';
import { Legend } from '../Text';

const { prefix } = settings;

Expand Down Expand Up @@ -148,7 +149,7 @@ export default class RadioButtonGroup extends React.Component {
<div className={`${prefix}--form-item`}>
<fieldset className={wrapperClasses} disabled={disabled}>
{legendText && (
<legend className={`${prefix}--label`}>{legendText}</legend>
<Legend className={`${prefix}--label`}>{legendText}</Legend>
)}
{this.getRadioButtons()}
</fieldset>
Expand Down
90 changes: 90 additions & 0 deletions packages/react/src/components/Text/Text-story.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* 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 } from '../Layout';
import { TextDirection, Text } from '../Text';
import RadioButtonGroup from '../RadioButtonGroup';
import RadioButton from '../RadioButton';

export default {
title: 'Experimental/unstable_Text',
parameters: {
component: Text,
},
};

export const Default = () => (
<>
<p>
<Text>Hello world</Text>
</p>
<p>
<Text>لكن لا بد أن أوضح لك أن كل</Text>
</p>
</>
);

export const LayoutAndText = () => (
<LayoutDirection dir="ltr">
<p>
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?
</p>
<LayoutDirection dir="rtl">
<Text as="p">
المغلوطة حول استنكار النشوة وتمجيد الألم نشأت بالفعل، وسأعرض لك التفاصيل
لتكتشف حقيقة وأساس تلك السعادة البشرية، فلا أحد يرفض أو يكره أو يتجنب
الشعور بالسعادة، ولكن بفضل هؤ.
</Text>
</LayoutDirection>
<p>
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?
</p>
</LayoutDirection>
);

export const SetTextDirection = () => {
const legendText = 'הכותרת שלי!';

return (
<TextDirection
getTextDirection={(text) => {
if (text === legendText) {
return 'ltr';
}
return 'auto';
}}>
<RadioButtonGroup
legendText={legendText}
name="radio-button-group"
defaultSelected="radio-1"
style={{ maxWidth: '400px' }}>
<RadioButton
labelText="שלום עולם Option 1"
value="radio-1"
id="radio-1"
/>
<RadioButton
labelText="שלום עולם Option 2"
value="radio-1"
id="radio-1"
/>
<RadioButton
labelText="שלום עולם Option 3"
value="radio-1"
id="radio-1"
/>
</RadioButtonGroup>
</TextDirection>
);
};
Loading