Skip to content

Commit ff862e1

Browse files
authored
feat(react): redesign descriptions api (#117)
* feat(react): redesign descriptions api * chore: lint
1 parent 83a8994 commit ff862e1

18 files changed

Lines changed: 953 additions & 297 deletions

.changeset/smooth-llamas-tease.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+
Redesign the Descriptions component with a new data-driven API, responsive columns, semantic rendering modes, richer docs demos, and updated layout behavior.

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

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,114 @@
33
exports[`<Descriptions /> should match the snapshot 1`] = `
44
<DocumentFragment>
55
<div
6-
class="ty-descriptions ty-descriptions_md"
6+
class="ty-descriptions ty-descriptions_md ty-descriptions_horizontal ty-descriptions_list ty-descriptions_label-start ty-descriptions_content-start"
77
>
8+
<div
9+
class="ty-descriptions__header"
10+
>
11+
<div
12+
class="ty-descriptions__title"
13+
>
14+
Profile
15+
</div>
16+
</div>
817
<div
918
class="ty-descriptions__body"
1019
>
11-
<table>
12-
<tbody>
13-
<tr
14-
class="ty-descriptions__row"
20+
<dl
21+
class="ty-descriptions__list"
22+
style="grid-template-columns: repeat(3, minmax(0, 1fr));"
23+
>
24+
<div
25+
class="ty-descriptions__list-item"
26+
style="grid-column: span 1;"
27+
>
28+
<dt
29+
class="ty-descriptions__item-label"
1530
>
16-
<td
17-
class="ty-descriptions__item"
18-
colspan="1"
31+
<div
32+
class="ty-descriptions__label-inner"
1933
>
2034
<span
21-
class="ty-descriptions__item-label ty-descriptions__item_colon"
35+
class="ty-descriptions__label-text"
2236
>
2337
Name
2438
</span>
2539
<span
26-
class="ty-descriptions__item-content"
40+
class="ty-descriptions__separator"
2741
>
28-
John
42+
:
2943
</span>
30-
</td>
31-
<td
32-
class="ty-descriptions__item"
33-
colspan="2"
44+
</div>
45+
</dt>
46+
<dd
47+
class="ty-descriptions__item-content"
48+
>
49+
John
50+
</dd>
51+
</div>
52+
<div
53+
class="ty-descriptions__list-item"
54+
style="grid-column: span 1;"
55+
>
56+
<dt
57+
class="ty-descriptions__item-label"
58+
>
59+
<div
60+
class="ty-descriptions__label-inner"
3461
>
3562
<span
36-
class="ty-descriptions__item-label ty-descriptions__item_colon"
63+
class="ty-descriptions__label-text"
3764
>
3865
Age
3966
</span>
4067
<span
41-
class="ty-descriptions__item-content"
68+
class="ty-descriptions__separator"
69+
>
70+
:
71+
</span>
72+
</div>
73+
</dt>
74+
<dd
75+
class="ty-descriptions__item-content"
76+
>
77+
30
78+
</dd>
79+
</div>
80+
<div
81+
class="ty-descriptions__list-item"
82+
style="grid-column: span 1;"
83+
>
84+
<dt
85+
class="ty-descriptions__item-label"
86+
>
87+
<div
88+
class="ty-descriptions__label-inner"
89+
>
90+
<span
91+
class="ty-descriptions__label-text"
92+
>
93+
Address
94+
</span>
95+
<span
96+
class="ty-descriptions__separator"
4297
>
43-
30
98+
:
4499
</span>
45-
</td>
46-
</tr>
47-
</tbody>
48-
</table>
100+
</div>
101+
</dt>
102+
<dd
103+
class="ty-descriptions__item-content"
104+
>
105+
Sydney
106+
</dd>
107+
</div>
108+
</dl>
109+
</div>
110+
<div
111+
class="ty-descriptions__footer"
112+
>
113+
Footer
49114
</div>
50115
</div>
51116
</DocumentFragment>

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

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import Descriptions from '../index';
55
describe('<Descriptions />', () => {
66
it('should match the snapshot', () => {
77
const { asFragment } = render(
8-
<Descriptions>
8+
<Descriptions title="Profile" footer="Footer">
99
<Descriptions.Item label="Name">John</Descriptions.Item>
1010
<Descriptions.Item label="Age">30</Descriptions.Item>
11+
<Descriptions.Item label="Address" span="fill">
12+
Sydney
13+
</Descriptions.Item>
1114
</Descriptions>
1215
);
1316
expect(asFragment()).toMatchSnapshot();
@@ -20,15 +23,18 @@ describe('<Descriptions />', () => {
2023
</Descriptions>
2124
);
2225
expect(container.firstChild).toHaveClass('ty-descriptions');
26+
expect(container.querySelector('dl')).toBeInTheDocument();
2327
});
2428

25-
it('should render title', () => {
29+
it('should render header and footer', () => {
2630
const { getByText } = render(
27-
<Descriptions title="User Info">
31+
<Descriptions title="User Info" extra={<button>Action</button>} footer="Summary footer">
2832
<Descriptions.Item label="Name">John</Descriptions.Item>
2933
</Descriptions>
3034
);
3135
expect(getByText('User Info')).toBeInTheDocument();
36+
expect(getByText('Action')).toBeInTheDocument();
37+
expect(getByText('Summary footer')).toBeInTheDocument();
3238
});
3339

3440
it('should render items', () => {
@@ -40,4 +46,34 @@ describe('<Descriptions />', () => {
4046
expect(getByText('Name')).toBeInTheDocument();
4147
expect(getByText('John')).toBeInTheDocument();
4248
});
49+
50+
it('should render items from items prop', () => {
51+
const { getByText } = render(
52+
<Descriptions
53+
items={[
54+
{ key: 'name', label: 'Name', content: 'John' },
55+
{ key: 'role', label: 'Role', content: 'Maintainer' },
56+
]}
57+
/>
58+
);
59+
60+
expect(getByText('Name')).toBeInTheDocument();
61+
expect(getByText('Maintainer')).toBeInTheDocument();
62+
});
63+
64+
it('should use table semantic when bordered', () => {
65+
const { container } = render(
66+
<Descriptions bordered items={[{ key: 'name', label: 'Name', content: 'John' }]} />
67+
);
68+
69+
expect(container.querySelector('table')).toBeInTheDocument();
70+
});
71+
72+
it('should render empty placeholder for nullish content', () => {
73+
const { getByText } = render(
74+
<Descriptions items={[{ key: 'name', label: 'Name', content: null }]} empty="Pending" />
75+
);
76+
77+
expect(getByText('Pending')).toBeInTheDocument();
78+
});
4379
});
Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,22 @@
11
import React from 'react';
2-
import { Descriptions } from '@tiny-design/react';
2+
import { Button, Descriptions, Tag } from '@tiny-design/react';
33

44
export default function BasicDemo() {
55
return (
6-
<Descriptions title="User Info">
7-
<Descriptions.Item label="UserName">React</Descriptions.Item>
8-
<Descriptions.Item label="Telephone">0200004567</Descriptions.Item>
9-
<Descriptions.Item label="Live">Sydney, Australia</Descriptions.Item>
10-
<Descriptions.Item label="Remark">Great</Descriptions.Item>
11-
<Descriptions.Item label="Address">456P+HW Camperdown, New South Wales</Descriptions.Item>
6+
<Descriptions
7+
title="Workspace Profile"
8+
extra={<Button size="sm" type="primary">Edit</Button>}
9+
columns={2}
10+
footer={<span style={{ color: 'var(--ty-color-text-3)' }}>Last synced 2 minutes ago</span>}>
11+
<Descriptions.Item label="Name">Tiny Studio</Descriptions.Item>
12+
<Descriptions.Item label="Region">Australia Southeast</Descriptions.Item>
13+
<Descriptions.Item label="Owner" extra={<Tag size="sm">Core</Tag>}>
14+
Product Ops
15+
</Descriptions.Item>
16+
<Descriptions.Item label="Website">tiny.design</Descriptions.Item>
17+
<Descriptions.Item label="Summary" span="fill">
18+
Shared component workspace for tokens, docs, charts, and platform-specific UI packages.
19+
</Descriptions.Item>
1220
</Descriptions>
1321
);
14-
}
22+
}
Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,40 @@
11
import React from 'react';
2-
import { Descriptions, Badge } from '@tiny-design/react';
2+
import { Badge, Descriptions } from '@tiny-design/react';
33

44
export default function BorderDemo() {
55
return (
6-
<Descriptions title="User Info" bordered>
7-
<Descriptions.Item label="Product">Cloud Database</Descriptions.Item>
8-
<Descriptions.Item label="Billing Mode">Prepaid</Descriptions.Item>
9-
<Descriptions.Item label="Automatic Renewal">YES</Descriptions.Item>
10-
<Descriptions.Item label="Order time">2018-04-24 18:00:00</Descriptions.Item>
11-
<Descriptions.Item label="Usage Time" span={2}>
12-
2019-04-24 18:00:00
13-
</Descriptions.Item>
14-
<Descriptions.Item label="Status" span={3}>
15-
<Badge dot processing color="#1890ff" />
16-
Running
17-
</Descriptions.Item>
18-
<Descriptions.Item label="Negotiated Amount">$80.00</Descriptions.Item>
19-
<Descriptions.Item label="Discount">$20.00</Descriptions.Item>
20-
<Descriptions.Item label="Official Receipts">$60.00</Descriptions.Item>
21-
<Descriptions.Item label="Config Info">
22-
Data disk type: MongoDB
23-
<br />
24-
Database version: 3.4
25-
<br />
26-
Package: dds.mongo.mid
27-
<br />
28-
Storage space: 10 GB
29-
<br />
30-
Replication factor: 3
31-
<br />
32-
Region: East China 1
33-
</Descriptions.Item>
34-
</Descriptions>
6+
<Descriptions
7+
title="Deployment Contract"
8+
bordered
9+
semantic="table"
10+
columns={2}
11+
items={[
12+
{ key: 'service', label: 'Service', content: 'Managed DB' },
13+
{ key: 'billing', label: 'Billing', content: 'Annual prepaid' },
14+
{ key: 'renewal', label: 'Renewal', content: 'Enabled' },
15+
{ key: 'orderedAt', label: 'Ordered', content: 'Apr 18, 10:30' },
16+
{ key: 'period', label: 'Usage Window', content: 'Apr 18, 2026 to Apr 18, 2027', span: 'fill' },
17+
{
18+
key: 'status',
19+
label: 'Status',
20+
content: (
21+
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
22+
<Badge dot processing color="#1890ff" />
23+
Running
24+
</span>
25+
),
26+
span: 'fill',
27+
},
28+
{ key: 'amount', label: 'Amount', content: '$80.00' },
29+
{ key: 'discount', label: 'Discount', content: '$20.00' },
30+
{ key: 'total', label: 'Receipts', content: '$60.00' },
31+
{
32+
key: 'config',
33+
label: 'Config',
34+
span: 'fill',
35+
content: 'MongoDB / 3 replicas / 10 GB',
36+
},
37+
]}
38+
/>
3539
);
36-
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import { Descriptions } from '@tiny-design/react';
3+
4+
export default function EmptyHiddenDemo() {
5+
return (
6+
<Descriptions
7+
title="Empty And Hidden"
8+
columns={2}
9+
empty="Not configured"
10+
items={[
11+
{ key: 'env', label: 'Environment', content: 'Production' },
12+
{ key: 'token', label: 'Access Token', content: null },
13+
{ key: 'internal', label: 'Internal Note', content: 'Only for admins', hidden: true },
14+
{ key: 'fallback', label: 'Rollback Plan', content: undefined },
15+
]}
16+
/>
17+
);
18+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import React from 'react';
2+
import { Descriptions, Tag } from '@tiny-design/react';
3+
4+
export default function RenderAlignDemo() {
5+
return (
6+
<Descriptions
7+
title="Render And Align"
8+
semantic="table"
9+
bordered
10+
columns={1}
11+
labelAlign="end"
12+
contentAlign="start"
13+
items={[
14+
{ key: 'severity', label: 'Severity', content: 'High' },
15+
{ key: 'owner', label: 'Owner', content: 'UI Foundation' },
16+
{ key: 'eta', label: 'ETA', content: '2 days' },
17+
{ key: 'scope', label: 'Scope', content: 'Descriptions demo refinement' },
18+
]}
19+
labelRender={(item) => (
20+
<span style={{ display: 'inline-block', minWidth: 88, fontSize: 12, letterSpacing: '0.08em' }}>
21+
{item.label}
22+
</span>
23+
)}
24+
contentRender={(item) => {
25+
if (item.key === 'severity') {
26+
return <Tag color="warning">High</Tag>;
27+
}
28+
29+
return item.content;
30+
}}
31+
/>
32+
);
33+
}

0 commit comments

Comments
 (0)