Skip to content

Commit b6e03be

Browse files
authored
feat(divider): redesign divider api and demos (#105)
* fix: menu styles * feat(divider): redesign divider api and demos * chore: lint * test: snapshot
1 parent 9467b03 commit b6e03be

27 files changed

Lines changed: 374 additions & 125 deletions

.changeset/clean-dividers-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tiny-design/react': patch
3+
---
4+
5+
Revamp `Divider` with a new API: replace `type`, `align`, and `dashed` with `orientation`, `titlePlacement`, `variant`, `plain`, and `titleGap`.

packages/react/src/divider/__tests__/__snapshots__/divider.test.tsx.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
exports[`<Divider /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6-
class="ty-divider ty-divider_horizontal ty-divider_center"
6+
aria-orientation="horizontal"
7+
class="ty-divider ty-divider_horizontal ty-divider_solid"
78
role="separator"
89
/>
910
</DocumentFragment>

packages/react/src/divider/__tests__/divider.test.tsx

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,57 @@ describe('<Divider />', () => {
1616
it('should render horizontal type by default', () => {
1717
const { container } = render(<Divider />);
1818
expect(container.firstChild).toHaveClass('ty-divider_horizontal');
19+
expect(container.firstChild).toHaveClass('ty-divider_solid');
20+
expect(container.firstChild).toHaveAttribute('aria-orientation', 'horizontal');
1921
});
2022

21-
it('should render vertical type', () => {
22-
const { container } = render(<Divider type="vertical" />);
23+
it('should render vertical orientation', () => {
24+
const { container } = render(<Divider orientation="vertical" />);
2325
expect(container.firstChild).toHaveClass('ty-divider_vertical');
26+
expect(container.firstChild).toHaveAttribute('aria-orientation', 'vertical');
2427
});
2528

26-
it('should render dashed style', () => {
27-
const { container } = render(<Divider dashed />);
28-
expect(container.firstChild).toHaveClass('ty-divider_horizontal-dashed');
29+
it('should render variant style', () => {
30+
const { container } = render(<Divider variant="dashed" />);
31+
expect(container.firstChild).toHaveClass('ty-divider_dashed');
2932
});
3033

3134
it('should render with text', () => {
3235
const { getByText } = render(<Divider>Section</Divider>);
3336
expect(getByText('Section')).toBeInTheDocument();
3437
});
3538

36-
it('should render text with alignment', () => {
37-
const { container } = render(<Divider align="left">Left</Divider>);
39+
it('should render text with title placement', () => {
40+
const { container } = render(<Divider titlePlacement="start">Left</Divider>);
41+
expect(container.firstChild).toHaveClass('ty-divider_start');
42+
expect(container.firstChild).toHaveClass('ty-divider_text');
3843
expect(container.querySelector('.ty-divider_inner-text')).toBeInTheDocument();
3944
});
45+
46+
it('should render falsy children content', () => {
47+
const { container, getByText } = render(<Divider>{0}</Divider>);
48+
expect(container.firstChild).toHaveClass('ty-divider_text');
49+
expect(getByText('0')).toBeInTheDocument();
50+
});
51+
52+
it('should render plain style', () => {
53+
const { container } = render(<Divider plain>Plain</Divider>);
54+
expect(container.firstChild).toHaveClass('ty-divider_plain');
55+
});
56+
57+
it('should apply title gap custom property', () => {
58+
const { container } = render(<Divider titleGap={24}>Gap</Divider>);
59+
expect(container.firstChild).toHaveStyle('--ty-divider-title-gap: 24px');
60+
});
61+
62+
it('should not render inner text for vertical divider', () => {
63+
const { container, queryByText } = render(
64+
<Divider orientation="vertical" titlePlacement="start">
65+
Hidden
66+
</Divider>,
67+
);
68+
expect(container.firstChild).not.toHaveClass('ty-divider_text');
69+
expect(container.firstChild).not.toHaveClass('ty-divider_start');
70+
expect(queryByText('Hidden')).not.toBeInTheDocument();
71+
});
4072
});

packages/react/src/divider/demo/AlignTitle.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,27 @@ import { Divider } from '@tiny-design/react';
44
export default function AlignTitleDemo() {
55
return (
66
<>
7-
<Divider align="left">Left Text</Divider>
7+
<Divider titlePlacement="start">
8+
Account Summary
9+
</Divider>
810
<p>
911
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
1012
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
1113
</p>
12-
<Divider align="right">Right Text</Divider>
14+
<Divider>
15+
Recent Activity
16+
</Divider>
17+
<p>
18+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
19+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
20+
</p>
21+
<Divider titlePlacement="end">
22+
Archived
23+
</Divider>
1324
<p>
1425
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
1526
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
1627
</p>
1728
</>
1829
);
19-
}
30+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import { Card, Divider, Tag, Text } from '@tiny-design/react';
3+
4+
const rowStyle: React.CSSProperties = {
5+
display: 'flex',
6+
justifyContent: 'space-between',
7+
alignItems: 'center',
8+
gap: 12,
9+
};
10+
11+
export default function ContentSectionDemo() {
12+
return (
13+
<Card
14+
style={{
15+
maxWidth: 520,
16+
borderRadius: 16,
17+
background:
18+
'linear-gradient(180deg, color-mix(in srgb, var(--ty-color-primary) 5%, white), white)',
19+
}}
20+
>
21+
<Card.Content>
22+
<div style={rowStyle}>
23+
<div>
24+
<Text strong>Release Checklist</Text>
25+
<div style={{ marginTop: 6 }}>
26+
<Text type="secondary">Prepare the final rollout for the next product update.</Text>
27+
</div>
28+
</div>
29+
<Tag color="info" variant="soft">v1.12</Tag>
30+
</div>
31+
32+
<Divider titlePlacement="start" plain>
33+
QA Status
34+
</Divider>
35+
<div style={rowStyle}>
36+
<div>
37+
<Text>Visual review</Text>
38+
<div style={{ marginTop: 4 }}>
39+
<Text type="secondary">Tokens, spacing, and states confirmed.</Text>
40+
</div>
41+
</div>
42+
<Tag color="success" variant="soft">Passed</Tag>
43+
</div>
44+
45+
<Divider titlePlacement="start" plain>
46+
Deployment
47+
</Divider>
48+
<div style={rowStyle}>
49+
<div>
50+
<Text>Production rollout</Text>
51+
<div style={{ marginTop: 4 }}>
52+
<Text type="secondary">Waiting for final approval from release owners.</Text>
53+
</div>
54+
</div>
55+
<Tag color="warning" variant="soft">Pending</Tag>
56+
</div>
57+
</Card.Content>
58+
</Card>
59+
);
60+
}

packages/react/src/divider/demo/Horizontal.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,26 @@ export default function HorizontalDemo() {
1313
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
1414
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
1515
</p>
16-
<Divider>With Text</Divider>
16+
<Divider>Solid Section</Divider>
1717
<p>
1818
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
1919
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
2020
</p>
21-
<Divider dashed />
21+
<Divider variant="dashed">Dashed Section</Divider>
22+
<p>
23+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
24+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
25+
</p>
26+
<Divider variant="dotted">Dotted Section</Divider>
27+
<p>
28+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
29+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
30+
</p>
31+
<Divider plain>Quick Actions</Divider>
2232
<p>
2333
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
2434
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
2535
</p>
2636
</>
2737
);
28-
}
38+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import { Divider } from '@tiny-design/react';
3+
4+
export default function PlainAndGapDemo() {
5+
return (
6+
<>
7+
<Divider>Default Title Gap</Divider>
8+
<p>
9+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
10+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
11+
</p>
12+
<Divider titleGap={24}>Wide Title Gap</Divider>
13+
<p>
14+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
15+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
16+
</p>
17+
<Divider plain>Plain Supporting Text</Divider>
18+
<p>
19+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed nonne merninisti
20+
licere mihi ista probare, quae sunt a te dicta? Refert tamen, quo modo.
21+
</p>
22+
</>
23+
);
24+
}

packages/react/src/divider/demo/Vertical.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { Divider } from '@tiny-design/react';
33

44
export default function VerticalDemo() {
55
return (
6-
<>
7-
Text
8-
<Divider type="vertical" />
9-
<a href="#">Link</a>
10-
<Divider type="vertical" />
11-
<a href="#">Link</a>
12-
</>
6+
<div style={{ display: 'inline-flex', alignItems: 'center' }}>
7+
<span>Draft</span>
8+
<Divider orientation="vertical" />
9+
<a href="#">Preview</a>
10+
<Divider orientation="vertical" />
11+
<a href="#">Publish</a>
12+
</div>
1313
);
14-
}
14+
}

packages/react/src/divider/divider.tsx

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,50 @@ import { DividerProps } from './types';
66

77
const Divider = React.memo(React.forwardRef<HTMLDivElement, DividerProps>((props, ref) => {
88
const {
9-
type = 'horizontal',
10-
dashed = false,
11-
align = 'center',
9+
orientation = 'horizontal',
10+
variant = 'solid',
11+
titlePlacement = 'center',
12+
plain = false,
13+
titleGap,
1214
prefixCls: customisedCls,
1315
className,
1416
children,
17+
style,
1518
...otherProps
1619
} = props;
1720
const configContext = useContext(ConfigContext);
1821
const prefixCls = getPrefixCls('divider', configContext.prefixCls, customisedCls);
19-
const cls = classNames(prefixCls, className, `${prefixCls}_${type}`, `${prefixCls}_${align}`, {
20-
[`${prefixCls}_${type}-dashed`]: dashed,
21-
[`${prefixCls}_text`]: children,
22-
});
22+
const hasChildren = children !== null && children !== undefined;
23+
const hasInnerText = orientation === 'horizontal' && hasChildren;
24+
const cls = classNames(
25+
prefixCls,
26+
className,
27+
`${prefixCls}_${orientation}`,
28+
`${prefixCls}_${variant}`,
29+
hasInnerText && `${prefixCls}_${titlePlacement}`,
30+
{
31+
[`${prefixCls}_text`]: hasInnerText,
32+
[`${prefixCls}_plain`]: plain,
33+
},
34+
);
35+
const mergedStyle = hasInnerText && titleGap !== undefined
36+
? {
37+
...style,
38+
['--ty-divider-title-gap' as string]:
39+
typeof titleGap === 'number' ? `${titleGap}px` : titleGap,
40+
}
41+
: style;
2342

2443
return (
25-
<div {...otherProps} ref={ref} role="separator" className={cls}>
26-
{children && <span className={`${prefixCls}_inner-text`}>{children}</span>}
44+
<div
45+
{...otherProps}
46+
ref={ref}
47+
role="separator"
48+
aria-orientation={orientation}
49+
className={cls}
50+
style={mergedStyle}
51+
>
52+
{hasInnerText && <span className={`${prefixCls}_inner-text`}>{children}</span>}
2753
</div>
2854
);
2955
}));

packages/react/src/divider/index.md

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import AlignTitleDemo from './demo/AlignTitle';
22
import AlignTitleSource from './demo/AlignTitle.tsx?raw';
3+
import ContentSectionDemo from './demo/ContentSection';
4+
import ContentSectionSource from './demo/ContentSection.tsx?raw';
35
import HorizontalDemo from './demo/Horizontal';
46
import HorizontalSource from './demo/Horizontal.tsx?raw';
7+
import PlainAndGapDemo from './demo/PlainAndGap';
8+
import PlainAndGapSource from './demo/PlainAndGap.tsx?raw';
59
import VerticalDemo from './demo/Vertical';
610
import VerticalSource from './demo/Vertical.tsx?raw';
711

@@ -28,7 +32,7 @@ import { Divider } from 'tiny-design';
2832

2933
### Horizontal
3034

31-
Divider default type is `horizontal`. Support inner text inside Divider.
35+
Divider defaults to horizontal. Horizontal dividers support inner text, line variants, and plain text styling.
3236

3337
<DemoBlock component={HorizontalDemo} source={HorizontalSource} />
3438

@@ -37,31 +41,51 @@ Divider default type is `horizontal`. Support inner text inside Divider.
3741

3842
### Vertical
3943

40-
Use `type="vertical"` make it vertical.
44+
Use `orientation="vertical"` to render a vertical divider.
4145

4246
<DemoBlock component={VerticalDemo} source={VerticalSource} />
4347

48+
</Demo>
49+
<Demo>
50+
51+
### Plain And Gap
52+
53+
Use `plain` to reduce emphasis and `titleGap` to control title spacing.
54+
55+
<DemoBlock component={PlainAndGapDemo} source={PlainAndGapSource} />
56+
4457
</Demo>
4558
</Column>
4659
<Column>
4760
<Demo>
4861

4962
### Align Title
5063

51-
Set orientation of divider to left or right, default is `center`.
64+
Set the title placement to `start`, `center`, or `end`. This only applies to horizontal dividers with inner text.
5265

5366
<DemoBlock component={AlignTitleDemo} source={AlignTitleSource} />
5467

68+
</Demo>
69+
<Demo>
70+
71+
### In Content
72+
73+
Use dividers as lightweight section labels inside cards, settings panels, or summaries.
74+
75+
<DemoBlock component={ContentSectionDemo} source={ContentSectionSource} />
76+
5577
</Demo>
5678
</Column>
5779
</Layout>
5880

5981
## Props
6082

61-
| Property | Description | Type | Default |
62-
| --------- | -------------------------------- | ---------------------------------------------- | ------------ |
63-
| type | direction type of divider | enum: `horizontal` &#124; `vertical` | `horizontal` |
64-
| dashed | whether line is dashed | boolean | false |
65-
| align | position of title inside divider | enum: `left` &#124; `right` &#124; `center` | `center` |
66-
| style | style object of container object | CSSProperties | - |
67-
| className | className of container | string | - |
83+
| Property | Description | Type | Default |
84+
| -------------- | ------------------------------------------------------ | -------------------------------------------- | -------------- |
85+
| orientation | orientation of divider | enum: `horizontal` &#124; `vertical` | `horizontal` |
86+
| variant | line style variant | enum: `solid` &#124; `dashed` &#124; `dotted` | `solid` |
87+
| titlePlacement | position of title inside horizontal divider with text | enum: `start` &#124; `center` &#124; `end` | `center` |
88+
| plain | whether divider text uses plain emphasis | boolean | false |
89+
| titleGap | horizontal gap between divider title and line | string &#124; number | token value |
90+
| style | style object of container | CSSProperties | - |
91+
| className | className of container | string | - |

0 commit comments

Comments
 (0)