Skip to content

Commit e6e661f

Browse files
authored
feat(typography): add UnorderedList and OrderedList components (#388)
1 parent 380a0c4 commit e6e661f

19 files changed

+913
-2
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
```jsx
2+
const { Well } = require('@zendeskgarden/react-notifications/src');
3+
const { Checkbox, Field, Input, Label } = require('@zendeskgarden/react-forms/src');
4+
const {
5+
Dropdown,
6+
Select,
7+
Field: SelectField,
8+
Label: SelectLabel,
9+
Menu,
10+
Item: MenuItem
11+
} = require('@zendeskgarden/react-dropdowns/src');
12+
const { Range, RangeField, Label: RangeLabel } = require('@zendeskgarden/react-ranges/src');
13+
14+
initialState = {
15+
length: 1,
16+
levels: 1,
17+
ordered: false,
18+
size: UnorderedList.defaultProps.size,
19+
start: 1
20+
};
21+
22+
const text = [
23+
'garden es bonus vobis proinde vos postulo essum magis kohlrabi welsh onion daikon amaranth tatsoi tomatillo melon azuki bean garlic beet greens corn soko endive gumbo gourd shallot courgette tatsoi pea sprouts fava bean collard greens dandelion okra wakame tomato cucumber earthnut pea peanut soko zucchini.'.split(
24+
' '
25+
),
26+
'greens yarrow ricebean rutabaga endive cauliflower sea lettuce kohlrabi amaranth water spinach avocado daikon napa cabbage asparagus winter purslane kale celery potato scallion desert raisin horseradish spinach carrot soko lotus root water spinach fennel kombu maize bamboo shoot green bean swiss chard seakale pumpkin sprout coriander.'.split(
27+
' '
28+
),
29+
'water chestnut gourd swiss chard wakame kohlrabi beetroot carrot watercress corn amaranth salsify bunya nuts nori azuki bean chickweed potato bell pepper artichoke chestnut eggplant winter purslane fennel azuki bean earthnut pea sierra leone bologi leek soko chicory celtuce parsley jícama salsify celery quandong swiss chard.'.split(
30+
' '
31+
),
32+
'rock melon radish asparagus spinach beetroot water spinach okra water chestnut ricebean pea catsear courgette summer purslane water spinach arugula pea tatsoi aubergine spring onion bush tomato kale radicchio turnip chicory salsify pea sprouts fava bean dandelion zucchini burdock yarrow chickpea dandelion sorrel courgette turnip greens.'.split(
33+
' '
34+
)
35+
];
36+
const getType = (ordered, level) => {
37+
const types = ordered ? ['decimal', 'lower-alpha', 'lower-roman'] : ['disc', 'circle', 'square'];
38+
const index = level % types.length;
39+
40+
return types[index];
41+
};
42+
43+
const NestedList = ({ level = 0, ...props }) => {
44+
const content = text.map(string => string.slice(0, state.length).join(' '));
45+
const List = state.ordered ? OrderedList : UnorderedList;
46+
47+
if (level < state.levels) {
48+
return (
49+
<List start={state.start} type={getType(state.ordered, level)} {...props}>
50+
<List.Item>{content[0]}</List.Item>
51+
<List.Item>
52+
{content[1]}
53+
<NestedList level={level + 1} {...props} />
54+
</List.Item>
55+
<List.Item>{content[2]}</List.Item>
56+
<List.Item>{content[3]}</List.Item>
57+
</List>
58+
);
59+
} else {
60+
return <></>;
61+
}
62+
};
63+
64+
<>
65+
<Well recessed style={{ width: 300 }}>
66+
<Field>
67+
<Checkbox
68+
checked={state.ordered}
69+
onChange={event => setState({ ordered: event.target.checked })}
70+
>
71+
<Label style={{ marginBottom: 8 }}>Ordered</Label>
72+
</Checkbox>
73+
</Field>
74+
<RangeField>
75+
<RangeLabel>Levels</RangeLabel>
76+
<Range
77+
max={9}
78+
min={1}
79+
value={state.levels}
80+
onChange={event => setState({ levels: event.target.value })}
81+
/>
82+
</RangeField>
83+
<RangeField>
84+
<RangeLabel>Length</RangeLabel>
85+
<Range
86+
max={text[0].length}
87+
min={1}
88+
value={state.length}
89+
onChange={event => setState({ length: event.target.value })}
90+
/>
91+
</RangeField>
92+
<Dropdown selectedItem={state.size} onSelect={size => setState({ size })}>
93+
<SelectField>
94+
<SelectLabel>Size</SelectLabel>
95+
<Select small>{state.size}</Select>
96+
</SelectField>
97+
<Menu small>
98+
<MenuItem value="small">small</MenuItem>
99+
<MenuItem value="medium">medium (default)</MenuItem>
100+
<MenuItem value="large">large</MenuItem>
101+
</Menu>
102+
</Dropdown>
103+
<Field>
104+
<Label>Start</Label>
105+
<Input
106+
disabled={!state.ordered}
107+
small
108+
type="number"
109+
value={state.start}
110+
onChange={event => setState({ start: event.target.value })}
111+
/>
112+
</Field>
113+
</Well>
114+
<MD tag="p">
115+
Nori grape silver beet broccoli kombu beet greens fava bean potato quandong celery. Bunya nuts
116+
black-eyed pea prairie turnip leek lentil turnip greens parsnip. Sea lettuce lettuce water
117+
chestnut eggplant winter purslane fennel azuki bean earthnut pea sierra leone bologi leek soko
118+
chicory celtuce parsley jícama salsify.
119+
</MD>
120+
<NestedList {...state} />
121+
<MD tag="p" style={{ marginBottom: 0 }}>
122+
Soko radicchio bunya nuts gram dulse silver beet parsnip napa cabbage lotus root sea lettuce
123+
brussels sprout cabbage. Catsear cauliflower garbanzo yarrow salsify chicory garlic bell pepper
124+
napa cabbage lettuce tomato kale arugula melon sierra leone bologi rutabaga tigernut. Sea
125+
lettuce gumbo grape kale kombu cauliflower salsify kohlrabi okra.
126+
</MD>
127+
</>;
128+
```

packages/typography/src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,5 @@ export { default as XXL } from './views/XXL';
1313
export { default as XXXL } from './views/XXXL';
1414
export { default as Code } from './views/Code';
1515
export { default as Ellipsis } from './views/Ellipsis';
16+
export { default as OrderedList } from './views/lists/OrderedList';
17+
export { default as UnorderedList } from './views/lists/UnorderedList';

packages/typography/src/index.spec.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,20 @@ import * as rootIndex from './';
1010

1111
describe('Index', () => {
1212
it('exports all components and utilities', async () => {
13-
const exports = await getExports({ cwd: __dirname });
13+
const exports = await getExports({
14+
cwd: __dirname,
15+
fileMapper: files => {
16+
return files
17+
.filter(file => !/Item|use(Ordered|Unordered)ListContext|styles/u.test(file))
18+
.map(entry =>
19+
entry
20+
.replace(/\.js$/u, '')
21+
.split('/')
22+
.pop()
23+
)
24+
.sort();
25+
}
26+
});
1427

1528
expect(Object.keys(rootIndex).sort()).toEqual(exports);
1629
});
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
### Sizes
2+
3+
```jsx
4+
<p style={{marginTop: 0}}>Size <Code>small</Code></p>
5+
6+
<OrderedList size="small">
7+
<OrderedList.Item>One</OrderedList.Item>
8+
<OrderedList.Item>Two</OrderedList.Item>
9+
<OrderedList.Item>Three</OrderedList.Item>
10+
</OrderedList>
11+
12+
<p>Size <Code>medium</Code> (default)</p>
13+
14+
<OrderedList size="medium">
15+
<OrderedList.Item>One</OrderedList.Item>
16+
<OrderedList.Item>Two</OrderedList.Item>
17+
<OrderedList.Item>Three</OrderedList.Item>
18+
</OrderedList>
19+
20+
<p>Size <Code>large</Code></p>
21+
22+
<OrderedList size="large">
23+
<OrderedList.Item>One</OrderedList.Item>
24+
<OrderedList.Item>Two</OrderedList.Item>
25+
<OrderedList.Item>Three</OrderedList.Item>
26+
</OrderedList>
27+
```
28+
29+
### Types
30+
31+
```jsx
32+
<p style={{marginTop: 0}}>Type <Code>decimal</Code> (default)</p>
33+
34+
<OrderedList>
35+
<OrderedList.Item>One</OrderedList.Item>
36+
<OrderedList.Item>Two</OrderedList.Item>
37+
<OrderedList.Item>Three</OrderedList.Item>
38+
</OrderedList>
39+
40+
<p>Type <Code>decimal-leading-zero</Code></p>
41+
42+
<OrderedList type="decimal-leading-zero">
43+
<OrderedList.Item>One</OrderedList.Item>
44+
<OrderedList.Item>Two</OrderedList.Item>
45+
<OrderedList.Item>Three</OrderedList.Item>
46+
</OrderedList>
47+
48+
<p>Type <Code>lower-alpha</Code></p>
49+
50+
<OrderedList type="lower-alpha">
51+
<OrderedList.Item>One</OrderedList.Item>
52+
<OrderedList.Item>Two</OrderedList.Item>
53+
<OrderedList.Item>Three</OrderedList.Item>
54+
</OrderedList>
55+
56+
<p>Type <Code>upper-alpha</Code></p>
57+
58+
<OrderedList type="upper-alpha">
59+
<OrderedList.Item>One</OrderedList.Item>
60+
<OrderedList.Item>Two</OrderedList.Item>
61+
<OrderedList.Item>Three</OrderedList.Item>
62+
</OrderedList>
63+
64+
<p>Type <Code>lower-roman</Code></p>
65+
66+
<OrderedList type="lower-roman">
67+
<OrderedList.Item>One</OrderedList.Item>
68+
<OrderedList.Item>Two</OrderedList.Item>
69+
<OrderedList.Item>Three</OrderedList.Item>
70+
</OrderedList>
71+
72+
<p>Type <Code>upper-roman</Code></p>
73+
74+
<OrderedList type="upper-roman">
75+
<OrderedList.Item>One</OrderedList.Item>
76+
<OrderedList.Item>Two</OrderedList.Item>
77+
<OrderedList.Item>Three</OrderedList.Item>
78+
</OrderedList>
79+
```
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/**
2+
* Copyright Zendesk, Inc.
3+
*
4+
* Use of this source code is governed under the Apache License, Version 2.0
5+
* found at http://www.apache.org/licenses/LICENSE-2.0.
6+
*/
7+
8+
import React, { createContext } from 'react';
9+
import PropTypes from 'prop-types';
10+
import styled from 'styled-components';
11+
import { retrieveTheme } from '@zendeskgarden/react-theming';
12+
import OrderedListItem from './OrderedListItem';
13+
import { listCSS } from './styles';
14+
15+
const COMPONENT_ID = 'typography.ordered_list';
16+
17+
const TYPE = {
18+
DECIMAL: 'decimal',
19+
DECIMAL_LEADING_ZERO: 'decimal-leading-zero',
20+
LOWER_ALPHA: 'lower-alpha',
21+
LOWER_ROMAN: 'lower-roman',
22+
UPPER_ALPHA: 'upper-alpha',
23+
UPPER_ROMAN: 'upper-roman'
24+
};
25+
26+
const SIZE = {
27+
SMALL: 'small',
28+
MEDIUM: 'medium',
29+
LARGE: 'large'
30+
};
31+
32+
const StyledOrderedList = styled.ol.attrs({
33+
'data-garden-id': COMPONENT_ID,
34+
'data-garden-version': PACKAGE_VERSION
35+
})`
36+
${props => listCSS(props)};
37+
${props => retrieveTheme(COMPONENT_ID, props)};
38+
`;
39+
40+
export const OrderedListContext = createContext();
41+
42+
/**
43+
* Accepts all `ol` props
44+
*/
45+
const OrderedList = ({ size, children, ...other }) => (
46+
<OrderedListContext.Provider value={{ size }}>
47+
<StyledOrderedList {...other}>{children}</StyledOrderedList>
48+
</OrderedListContext.Provider>
49+
);
50+
51+
OrderedList.propTypes = {
52+
children: PropTypes.node,
53+
size: PropTypes.oneOf([SIZE.SMALL, SIZE.MEDIUM, SIZE.LARGE]),
54+
type: PropTypes.oneOf([
55+
TYPE.DECIMAL,
56+
TYPE.DECIMAL_LEADING_ZERO,
57+
TYPE.LOWER_ALPHA,
58+
TYPE.UPPER_ALPHA,
59+
TYPE.LOWER_ROMAN,
60+
TYPE.UPPER_ROMAN
61+
])
62+
};
63+
64+
OrderedList.defaultProps = {
65+
size: SIZE.MEDIUM
66+
};
67+
68+
OrderedList.Item = OrderedListItem;
69+
70+
/** @component */
71+
export default OrderedList;

0 commit comments

Comments
 (0)