Skip to content

Commit b856bc0

Browse files
Split ListTableNested's Sublist Table into a dedicated sub-component (#893)
- Create separate SublistTable as a child component in ListTableNested - Modify styles for ListTableNested and SublistTable to align sublist items to left instead of spreading them out - Fix issues with overflowing keys and values
1 parent 97b90ec commit b856bc0

File tree

6 files changed

+156
-58
lines changed

6 files changed

+156
-58
lines changed

src/components/list-table-nested/list-table-nested.styles.ts

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -54,53 +54,9 @@ export const styled = {
5454
ContentContainer: createStyled(
5555
'div',
5656
({ $theme }: { $theme: Theme }): StyleObject => ({
57-
...$theme.typography.ParagraphSmall,
58-
})
59-
),
60-
Sublist: createStyled(
61-
'div',
62-
({ $theme }: { $theme: Theme }): StyleObject => ({
63-
display: 'flex',
64-
flexDirection: 'column',
65-
alignItems: 'stretch',
6657
flexGrow: 1,
67-
rowGap: $theme.sizing.scale400,
68-
[$theme.mediaQuery.medium]: {
69-
rowGap: 0,
70-
},
71-
})
72-
),
73-
SublistItem: createStyled(
74-
'div',
75-
({ $theme }: { $theme: Theme }): StyleObject => ({
76-
display: 'flex',
77-
[$theme.mediaQuery.medium]: {
78-
flexDirection: 'row',
79-
justifyContent: 'space-between',
80-
alignItems: 'center',
81-
':not(:last-child)': {
82-
borderBottom: `1px solid ${$theme.colors.borderOpaque}`,
83-
},
84-
},
85-
flexDirection: 'column',
86-
alignItems: 'start',
87-
gap: $theme.sizing.scale200,
88-
paddingTop: $theme.sizing.scale100,
89-
paddingBottom: $theme.sizing.scale100,
90-
})
91-
),
92-
SublistItemLabel: createStyled(
93-
'div',
94-
({ $theme }: { $theme: Theme }): StyleObject => ({
95-
...$theme.typography.LabelSmall,
96-
[$theme.mediaQuery.medium]: {
97-
minWidth: '120px',
98-
},
99-
})
100-
),
101-
SublistItemValue: createStyled(
102-
'div',
103-
({ $theme }: { $theme: Theme }): StyleObject => ({
58+
overflow: 'hidden',
59+
overflowWrap: 'anywhere',
10460
...$theme.typography.ParagraphSmall,
10561
})
10662
),

src/components/list-table-nested/list-table-nested.tsx

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22

33
import { styled } from './list-table-nested.styles';
44
import type { Props } from './list-table-nested.types';
5+
import SublistTable from './sublist-table/sublist-table';
56

67
/**
78
* Renders a responsive table for displaying items as label-value pairs, or groups of label-value pairs.
@@ -19,18 +20,7 @@ export default function ListTableNested({ items }: Props) {
1920
)}
2021
</styled.TitleBlock>
2122
{item.kind === 'group' ? (
22-
<styled.Sublist>
23-
{item.items.map((sublistItem) => (
24-
<styled.SublistItem key={sublistItem.key}>
25-
<styled.SublistItemLabel>
26-
{sublistItem.label}:
27-
</styled.SublistItemLabel>
28-
<styled.SublistItemValue>
29-
{sublistItem.value}
30-
</styled.SublistItemValue>
31-
</styled.SublistItem>
32-
))}
33-
</styled.Sublist>
23+
<SublistTable items={item.items} />
3424
) : (
3525
<styled.ContentContainer>{item.value}</styled.ContentContainer>
3626
)}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import React from 'react';
2+
3+
import { render, screen, within } from '@/test-utils/rtl';
4+
5+
import SublistTable from '../sublist-table';
6+
import { type SublistItem } from '../sublist-table.types';
7+
8+
const mockSublistItems: Array<SublistItem> = [
9+
{
10+
key: 'subKey1',
11+
label: 'Sub Key 1',
12+
value: 'mock-value-1',
13+
},
14+
{
15+
key: 'subKey2',
16+
label: 'Sub Key 2',
17+
value: 'mock-value-2',
18+
},
19+
{
20+
key: 'subKey3',
21+
label: 'Sub Key 3',
22+
value: (
23+
<div data-testid="mock-badge-container">
24+
<div data-testid="mock-badge">Active</div>
25+
</div>
26+
),
27+
},
28+
];
29+
30+
describe(SublistTable.name, () => {
31+
it('renders all sublist items correctly', () => {
32+
render(<SublistTable items={mockSublistItems} />);
33+
34+
const sublistItems = screen.getAllByText(/Sub Key \d:/);
35+
expect(sublistItems).toHaveLength(3);
36+
37+
expect(screen.getByText('Sub Key 1:')).toBeInTheDocument();
38+
expect(screen.getByText('mock-value-1')).toBeInTheDocument();
39+
40+
expect(screen.getByText('Sub Key 2:')).toBeInTheDocument();
41+
expect(screen.getByText('mock-value-2')).toBeInTheDocument();
42+
43+
expect(screen.getByText('Sub Key 3:')).toBeInTheDocument();
44+
});
45+
46+
it('renders item with simple value correctly', () => {
47+
render(<SublistTable items={[mockSublistItems[0]]} />);
48+
49+
const sublistItem = screen.getByText('Sub Key 1:').parentElement;
50+
if (!sublistItem) throw new Error('Sublist item not found');
51+
52+
expect(within(sublistItem).getByText('Sub Key 1:')).toBeInTheDocument();
53+
expect(within(sublistItem).getByText('mock-value-1')).toBeInTheDocument();
54+
});
55+
56+
it('renders item with complex value correctly', () => {
57+
render(<SublistTable items={[mockSublistItems[2]]} />);
58+
59+
const sublistItem = screen.getByText('Sub Key 3:').parentElement;
60+
if (!sublistItem) throw new Error('Sublist item not found');
61+
62+
expect(within(sublistItem).getByText('Sub Key 3:')).toBeInTheDocument();
63+
expect(
64+
within(sublistItem).getByTestId('mock-badge-container')
65+
).toBeInTheDocument();
66+
expect(within(sublistItem).getByTestId('mock-badge')).toBeInTheDocument();
67+
expect(within(sublistItem).getByText('Active')).toBeInTheDocument();
68+
});
69+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { type Theme, styled as createStyled } from 'baseui';
2+
import { type StyleObject } from 'styletron-react';
3+
4+
export const styled = {
5+
Sublist: createStyled(
6+
'div',
7+
({ $theme }: { $theme: Theme }): StyleObject => ({
8+
display: 'flex',
9+
flexDirection: 'column',
10+
alignItems: 'stretch',
11+
flexGrow: 1,
12+
rowGap: $theme.sizing.scale400,
13+
[$theme.mediaQuery.medium]: {
14+
rowGap: 0,
15+
},
16+
})
17+
),
18+
SublistItem: createStyled(
19+
'div',
20+
({ $theme }: { $theme: Theme }): StyleObject => ({
21+
display: 'flex',
22+
[$theme.mediaQuery.medium]: {
23+
flexDirection: 'row',
24+
alignItems: 'baseline',
25+
columnGap: $theme.sizing.scale700,
26+
':not(:last-child)': {
27+
borderBottom: `1px solid ${$theme.colors.borderOpaque}`,
28+
},
29+
},
30+
flexDirection: 'column',
31+
alignItems: 'start',
32+
rowGap: $theme.sizing.scale200,
33+
paddingTop: $theme.sizing.scale100,
34+
paddingBottom: $theme.sizing.scale100,
35+
})
36+
),
37+
SublistItemLabel: createStyled(
38+
'div',
39+
({ $theme }: { $theme: Theme }): StyleObject => ({
40+
...$theme.typography.LabelSmall,
41+
overflow: 'hidden',
42+
overflowWrap: 'anywhere',
43+
[$theme.mediaQuery.medium]: {
44+
minWidth: '120px',
45+
maxWidth: '120px',
46+
},
47+
})
48+
),
49+
SublistItemValue: createStyled(
50+
'div',
51+
({ $theme }: { $theme: Theme }): StyleObject => ({
52+
...$theme.typography.ParagraphSmall,
53+
overflow: 'hidden',
54+
overflowWrap: 'anywhere',
55+
})
56+
),
57+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { styled } from './sublist-table.styles';
2+
import { type Props } from './sublist-table.types';
3+
4+
export default function SublistTable({ items }: Props) {
5+
return (
6+
<styled.Sublist>
7+
{items.map((sublistItem) => (
8+
<styled.SublistItem key={sublistItem.key}>
9+
<styled.SublistItemLabel>
10+
{sublistItem.label}:
11+
</styled.SublistItemLabel>
12+
<styled.SublistItemValue>{sublistItem.value}</styled.SublistItemValue>
13+
</styled.SublistItem>
14+
))}
15+
</styled.Sublist>
16+
);
17+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export type SublistItem = {
2+
key: string;
3+
label: string;
4+
value: React.ReactNode;
5+
};
6+
7+
export type Props = {
8+
items: Array<SublistItem>;
9+
};

0 commit comments

Comments
 (0)