Skip to content

Commit 90fdffc

Browse files
authored
Merge pull request #8 from airbnb/garrett--auto-direction
[AutoDirectionProvider] Add component for automatically detected direction
2 parents 1339e80 + 6d93ac8 commit 90fdffc

File tree

4 files changed

+153
-0
lines changed

4 files changed

+153
-0
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,26 @@ import DirectionProvider, { DIRECTIONS } from 'react-with-direction/dist/Directi
6363
</DirectionProvider>
6464
```
6565

66+
## AutoDirectionProvider
67+
68+
Use `AutoDirectionProvider` around, for example, user-generated content where the text direction is unknown or may change. This renders a `DirectionProvider` with the `direction` prop automatically set based on the `text` prop provided.
69+
70+
Direction will be determined based on the first strong LTR/RTL character in the `text` string. Strings with no strong direction (e.g., numbers) will inherit the direction from its nearest `DirectionProvider` ancestor or default to LTR.
71+
72+
Usage example:
73+
74+
```js
75+
import AutoDirectionProvider from 'react-with-direction/dist/AutoDirectionProvider';
76+
```
77+
78+
```js
79+
<AutoDirectionProvider text={userGeneratedContent}>
80+
<ExampleComponent>
81+
{userGeneratedContent}
82+
</ExampleComponent>
83+
</AutoDirectionProvider>
84+
```
85+
6686
[package-url]: https://npmjs.org/package/react-with-direction
6787
[npm-version-svg]: http://versionbadg.es/airbnb/react-with-direction.svg
6888
[travis-svg]: https://travis-ci.org/airbnb/react-with-direction.svg

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"airbnb-prop-types": "^2.8.1",
2626
"brcast": "^2.0.2",
2727
"deepmerge": "^1.5.1",
28+
"direction": "^1.0.1",
2829
"hoist-non-react-statics": "^2.3.1",
2930
"object.assign": "^4.1.0",
3031
"object.values": "^1.0.4",

src/AutoDirectionProvider.jsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import PropTypes from 'prop-types';
2+
import React from 'react';
3+
import { forbidExtraProps } from 'airbnb-prop-types';
4+
import getDirection from 'direction';
5+
import directionPropType from './proptypes/direction';
6+
import DirectionProvider from './DirectionProvider';
7+
import withDirection from './withDirection';
8+
9+
const propTypes = forbidExtraProps({
10+
children: PropTypes.node.isRequired,
11+
direction: directionPropType.isRequired,
12+
inline: PropTypes.bool,
13+
text: PropTypes.string.isRequired,
14+
});
15+
16+
const defaultProps = {
17+
inline: false,
18+
};
19+
20+
function AutoDirectionProvider({
21+
children,
22+
direction,
23+
inline,
24+
text,
25+
}) {
26+
const textDirection = getDirection(text);
27+
const dir = textDirection === 'neutral' ? direction : textDirection;
28+
29+
return (
30+
<DirectionProvider
31+
direction={dir}
32+
inline={inline}
33+
>
34+
{React.Children.only(children)}
35+
</DirectionProvider>
36+
);
37+
}
38+
39+
AutoDirectionProvider.propTypes = propTypes;
40+
AutoDirectionProvider.defaultProps = defaultProps;
41+
42+
export default withDirection(AutoDirectionProvider);
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import React from 'react';
2+
import { expect } from 'chai';
3+
import { shallow } from 'enzyme';
4+
5+
import AutoDirectionProvider from '../src/AutoDirectionProvider';
6+
import DirectionProvider from '../src/DirectionProvider';
7+
import { DIRECTIONS, CHANNEL } from '../src/constants';
8+
import mockBrcast from './mocks/brcast_mock';
9+
10+
describe('<AutoDirectionProvider>', () => {
11+
it('renders a DirectionProvider', () => {
12+
const wrapper = shallow((
13+
<AutoDirectionProvider text="a">
14+
<div />
15+
</AutoDirectionProvider>
16+
)).dive();
17+
18+
expect(wrapper).to.have.exactly(1).descendants(DirectionProvider);
19+
});
20+
21+
describe('direction prop', () => {
22+
it('is LTR correct for LTR strings', () => {
23+
const wrapper = shallow((
24+
<AutoDirectionProvider text="a">
25+
<div />
26+
</AutoDirectionProvider>
27+
)).dive();
28+
29+
expect(wrapper.find(DirectionProvider)).to.have.prop('direction', DIRECTIONS.LTR);
30+
});
31+
32+
it('is RTL correct for RTL strings', () => {
33+
const wrapper = shallow((
34+
<AutoDirectionProvider text="א">
35+
<div />
36+
</AutoDirectionProvider>
37+
)).dive();
38+
39+
expect(wrapper.find(DirectionProvider)).to.have.prop('direction', DIRECTIONS.RTL);
40+
});
41+
42+
it('is inherited from context for neutral strings', () => {
43+
const wrapper = shallow(
44+
(
45+
<AutoDirectionProvider text="1">
46+
<div />
47+
</AutoDirectionProvider>
48+
), {
49+
context: {
50+
[CHANNEL]: mockBrcast({
51+
data: DIRECTIONS.RTL,
52+
}),
53+
},
54+
},
55+
).dive();
56+
57+
expect(wrapper.find(DirectionProvider)).to.have.prop('direction', DIRECTIONS.RTL);
58+
});
59+
});
60+
61+
it('renders its children', () => {
62+
const children = <div>Foo</div>;
63+
64+
const wrapper = shallow((
65+
<AutoDirectionProvider text="a">
66+
{children}
67+
</AutoDirectionProvider>
68+
)).dive();
69+
70+
expect(wrapper).to.contain(children);
71+
});
72+
73+
it('passes the inline prop to DirectionProvider', () => {
74+
let wrapper = shallow((
75+
<AutoDirectionProvider text="a">
76+
<div />
77+
</AutoDirectionProvider>
78+
)).dive();
79+
80+
expect(wrapper.find(DirectionProvider)).to.have.prop('inline', false);
81+
82+
wrapper = shallow((
83+
<AutoDirectionProvider text="a" inline>
84+
<div />
85+
</AutoDirectionProvider>
86+
)).dive();
87+
88+
expect(wrapper.find(DirectionProvider)).to.have.prop('inline', true);
89+
});
90+
});

0 commit comments

Comments
 (0)