uikit-react-public 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Table/Table.context.d.ts +6 -0
- package/dist/components/Table/Table.d.ts +4 -3
- package/dist/components/Table/Table.stories.d.ts +3 -3
- package/dist/components/Table/Table.types.d.ts +6 -0
- package/dist/components/Table/index.d.ts +1 -1
- package/dist/components/Table/subcomponents/Cell/Cell.d.ts +8 -1
- package/dist/components/Table/subcomponents/Cell/Cell.stories.d.ts +5 -1
- package/dist/components/Table/subcomponents/Cell/CellContent.d.ts +3 -2
- package/dist/components/Table/subcomponents/HeadCell/HeadCell.d.ts +4 -2
- package/dist/components/Table/subcomponents/HeadCell/HeadCell.stories.d.ts +3 -1
- package/dist/components/Table/subcomponents/Row.d.ts +8 -1
- package/dist/index.js +6564 -6194
- package/lib/components/Snackbar/Snackbar.tsx +2 -3
- package/lib/components/Snackbar/__tests__/__snapshots__/Snackbar.test.tsx.snap +2 -2
- package/lib/components/Table/Table.context.ts +12 -0
- package/lib/components/Table/Table.tsx +91 -12
- package/lib/components/Table/Table.types.ts +21 -0
- package/lib/components/Table/__tests__/Table.test.tsx +95 -1
- package/lib/components/Table/__tests__/__snapshots__/Table.test.tsx.snap +54 -13
- package/lib/components/Table/index.ts +6 -1
- package/lib/components/Table/subcomponents/Body.tsx +16 -3
- package/lib/components/Table/subcomponents/Cell/Cell.tsx +193 -4
- package/lib/components/Table/subcomponents/Cell/CellContent.tsx +63 -3
- package/lib/components/Table/subcomponents/Cell/__tests__/__snapshots__/Cell.test.tsx.snap +15 -10
- package/lib/components/Table/subcomponents/Head.tsx +14 -3
- package/lib/components/Table/subcomponents/HeadCell/HeadCell.tsx +17 -4
- package/lib/components/Table/subcomponents/HeadCell/__tests__/__snapshots__/HeadCell.test.tsx.snap +3 -3
- package/lib/components/Table/subcomponents/Row.tsx +195 -6
- package/lib/components/Table/subcomponents/SortIcon.tsx +4 -4
- package/lib/theme/__tests__/useTheme.test.tsx +2 -2
- package/lib/theme/useTheme.tsx +1 -1
- package/package.json +1 -1
|
@@ -1,47 +1,236 @@
|
|
|
1
|
+
import { Children, cloneElement, isValidElement, useState } from 'react';
|
|
1
2
|
import { css, cx } from '@emotion/css';
|
|
2
3
|
import { useTheme } from '../../../theme';
|
|
4
|
+
import { useTableContext } from '../Table.context';
|
|
5
|
+
import Icon from '../../Icon/Icon';
|
|
6
|
+
import type { TableCellProps } from './Cell/Cell';
|
|
7
|
+
import type { NormalizedTableMobileRole } from '../Table.types';
|
|
3
8
|
|
|
4
9
|
export interface TableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
|
5
10
|
selected?: boolean;
|
|
11
|
+
mobileExpanded?: boolean;
|
|
12
|
+
defaultMobileExpanded?: boolean;
|
|
13
|
+
mobileCollapsible?: boolean;
|
|
14
|
+
mobileDetailsLabel?: string;
|
|
15
|
+
mobileExpandLabel?: string;
|
|
16
|
+
mobileCollapseLabel?: string;
|
|
17
|
+
onMobileExpandedChange?: (expanded: boolean) => void;
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
const NAME = 'ucl-uikit-table__row';
|
|
9
21
|
|
|
22
|
+
const isMobileBodyRole = (mobileRole?: NormalizedTableMobileRole) =>
|
|
23
|
+
!mobileRole || mobileRole === 'field';
|
|
24
|
+
|
|
25
|
+
const isMobileRole = (
|
|
26
|
+
mobileRole: NormalizedTableMobileRole | undefined,
|
|
27
|
+
expectedMobileRole: NormalizedTableMobileRole
|
|
28
|
+
) => mobileRole === expectedMobileRole;
|
|
29
|
+
|
|
10
30
|
const Row = ({
|
|
11
31
|
selected = false,
|
|
32
|
+
mobileExpanded,
|
|
33
|
+
defaultMobileExpanded = true,
|
|
34
|
+
mobileCollapsible = true,
|
|
35
|
+
mobileDetailsLabel = 'Details',
|
|
36
|
+
mobileExpandLabel = 'Show details',
|
|
37
|
+
mobileCollapseLabel = 'Hide details',
|
|
38
|
+
onMobileExpandedChange,
|
|
12
39
|
className,
|
|
13
40
|
children,
|
|
14
41
|
...props
|
|
15
42
|
}: TableRowProps) => {
|
|
16
43
|
const [theme] = useTheme();
|
|
44
|
+
const { columns } = useTableContext();
|
|
45
|
+
const [uncontrolledMobileExpanded, setUncontrolledMobileExpanded] = useState(
|
|
46
|
+
defaultMobileExpanded
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const isMobileExpanded =
|
|
50
|
+
mobileExpanded === undefined ? uncontrolledMobileExpanded : mobileExpanded;
|
|
51
|
+
|
|
52
|
+
const hasMobileBodyFields = Children.toArray(children).some(
|
|
53
|
+
(child, index) =>
|
|
54
|
+
isValidElement(child) && isMobileBodyRole(columns[index]?.mobileRole)
|
|
55
|
+
);
|
|
56
|
+
const hasMobileSelection = Children.toArray(children).some(
|
|
57
|
+
(child, index) =>
|
|
58
|
+
isValidElement(child) &&
|
|
59
|
+
isMobileRole(columns[index]?.mobileRole, 'selection')
|
|
60
|
+
);
|
|
61
|
+
const hasMobilePrimaryMeta = Children.toArray(children).some(
|
|
62
|
+
(child, index) =>
|
|
63
|
+
isValidElement(child) &&
|
|
64
|
+
isMobileRole(columns[index]?.mobileRole, 'primaryMeta')
|
|
65
|
+
);
|
|
66
|
+
const hasMobileContextMenu = Children.toArray(children).some(
|
|
67
|
+
(child, index) =>
|
|
68
|
+
isValidElement(child) &&
|
|
69
|
+
isMobileRole(columns[index]?.mobileRole, 'contextMenu')
|
|
70
|
+
);
|
|
71
|
+
const firstMobileBodyFieldIndex = Children.toArray(children).findIndex(
|
|
72
|
+
(child, index) =>
|
|
73
|
+
isValidElement(child) && isMobileBodyRole(columns[index]?.mobileRole)
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const canMobileCollapse = mobileCollapsible && hasMobileBodyFields;
|
|
77
|
+
const effectiveMobileExpanded = !canMobileCollapse || isMobileExpanded;
|
|
78
|
+
|
|
79
|
+
const handleMobileExpandedChange = () => {
|
|
80
|
+
const nextMobileExpanded = !isMobileExpanded;
|
|
81
|
+
|
|
82
|
+
if (mobileExpanded === undefined)
|
|
83
|
+
setUncontrolledMobileExpanded(nextMobileExpanded);
|
|
84
|
+
|
|
85
|
+
onMobileExpandedChange?.(nextMobileExpanded);
|
|
86
|
+
};
|
|
17
87
|
|
|
18
88
|
const baseStyle = css`
|
|
19
89
|
height: 40px;
|
|
20
|
-
border-bottom: 1px solid ${theme.
|
|
90
|
+
border-bottom: 1px solid ${theme.colour.border.default};
|
|
21
91
|
|
|
22
92
|
&:hover {
|
|
23
|
-
background-color: ${theme.
|
|
93
|
+
background-color: ${theme.colour.surface.secondary};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@media screen and (max-width: ${theme.breakpoints.tablet - 1}px) {
|
|
97
|
+
display: grid;
|
|
98
|
+
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
99
|
+
align-items: start;
|
|
100
|
+
column-gap: ${theme.padding.p16};
|
|
101
|
+
row-gap: 0;
|
|
102
|
+
height: auto;
|
|
103
|
+
padding: ${theme.padding.p16};
|
|
104
|
+
margin-bottom: ${theme.margin.m16};
|
|
105
|
+
border: ${theme.border.b1} solid ${theme.colour.border.default};
|
|
106
|
+
border-radius: ${theme.radius.r4};
|
|
107
|
+
background-color: ${theme.colour.surface.default};
|
|
108
|
+
|
|
109
|
+
&:hover {
|
|
110
|
+
background-color: ${theme.colour.surface.default};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
&[data-mobile-expanded='false'] > [data-mobile-role='field'] {
|
|
114
|
+
display: none;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
&[data-mobile-expanded='false'] > [data-mobile-role='primaryAction'] {
|
|
118
|
+
display: none;
|
|
119
|
+
}
|
|
24
120
|
}
|
|
25
121
|
`;
|
|
26
122
|
|
|
27
123
|
const highlightedStyle = css`
|
|
28
|
-
background-color: ${theme.
|
|
124
|
+
background-color: ${theme.colour.surface.brandSecondary};
|
|
29
125
|
|
|
30
126
|
&:hover {
|
|
31
|
-
background-color: ${theme.
|
|
127
|
+
background-color: ${theme.colour.surface.brandSecondary};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@media screen and (max-width: ${theme.breakpoints.tablet - 1}px) {
|
|
131
|
+
background-color: ${theme.colour.surface.brandSecondary};
|
|
132
|
+
border-color: ${theme.colour.border.brandSubtle};
|
|
133
|
+
|
|
134
|
+
&:hover {
|
|
135
|
+
background-color: ${theme.colour.surface.brandSecondary};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
const mobileContextMenuGridStyle = css`
|
|
141
|
+
@media screen and (max-width: ${theme.breakpoints.tablet - 1}px) {
|
|
142
|
+
grid-template-columns: auto minmax(0, 1fr) auto auto;
|
|
32
143
|
}
|
|
33
144
|
`;
|
|
34
145
|
|
|
35
|
-
const style = cx(
|
|
146
|
+
const style = cx(
|
|
147
|
+
baseStyle,
|
|
148
|
+
selected && highlightedStyle,
|
|
149
|
+
hasMobileContextMenu && mobileContextMenuGridStyle,
|
|
150
|
+
className,
|
|
151
|
+
NAME
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
const mobileDetailsCellStyle = css`
|
|
155
|
+
display: block;
|
|
156
|
+
grid-column: 1 / -1;
|
|
157
|
+
order: 10;
|
|
158
|
+
margin-top: ${theme.margin.m8};
|
|
159
|
+
|
|
160
|
+
@media screen and (min-width: ${theme.breakpoints.tablet}px) {
|
|
161
|
+
display: none;
|
|
162
|
+
}
|
|
163
|
+
`;
|
|
164
|
+
|
|
165
|
+
const mobileDetailsButtonStyle = css`
|
|
166
|
+
all: unset;
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
justify-content: space-between;
|
|
170
|
+
box-sizing: border-box;
|
|
171
|
+
width: 100%;
|
|
172
|
+
min-height: ${theme.height.h24};
|
|
173
|
+
color: ${theme.colour.text.secondary};
|
|
174
|
+
font-family: ${theme.typography.body.sm.fontFamily};
|
|
175
|
+
font-feature-settings: ${theme.typography.body.sm.fontSettings};
|
|
176
|
+
font-size: ${theme.typography.body.sm.fontSize}px;
|
|
177
|
+
font-weight: ${theme.typography.body.sm.fontWeight};
|
|
178
|
+
line-height: ${theme.typography.body.sm.lineHeight}%;
|
|
179
|
+
cursor: pointer;
|
|
180
|
+
|
|
181
|
+
&:focus-visible {
|
|
182
|
+
box-shadow: ${theme.boxShadow.focus};
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
let columnIndex = -1;
|
|
187
|
+
const mobileChildren = Children.map(children, (child) => {
|
|
188
|
+
if (!isValidElement<TableCellProps>(child)) return child;
|
|
189
|
+
|
|
190
|
+
columnIndex += 1;
|
|
191
|
+
|
|
192
|
+
return cloneElement(child, {
|
|
193
|
+
__mobileColumnIndex: columnIndex,
|
|
194
|
+
__mobileHasSelection: hasMobileSelection,
|
|
195
|
+
__mobileHasPrimaryMeta: hasMobilePrimaryMeta,
|
|
196
|
+
__mobileIsFirstBodyField: columnIndex === firstMobileBodyFieldIndex,
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const mobileDetailsAriaLabel = isMobileExpanded
|
|
201
|
+
? mobileCollapseLabel
|
|
202
|
+
: mobileExpandLabel;
|
|
36
203
|
|
|
37
204
|
return (
|
|
38
205
|
<tr
|
|
39
206
|
className={style}
|
|
40
207
|
data-testid={NAME}
|
|
41
208
|
aria-selected={selected}
|
|
209
|
+
data-mobile-expanded={effectiveMobileExpanded}
|
|
42
210
|
{...props}
|
|
43
211
|
>
|
|
44
|
-
{
|
|
212
|
+
{mobileChildren}
|
|
213
|
+
{canMobileCollapse && (
|
|
214
|
+
<td
|
|
215
|
+
className={mobileDetailsCellStyle}
|
|
216
|
+
data-mobile-role='details'
|
|
217
|
+
>
|
|
218
|
+
<button
|
|
219
|
+
type='button'
|
|
220
|
+
className={mobileDetailsButtonStyle}
|
|
221
|
+
aria-label={mobileDetailsAriaLabel}
|
|
222
|
+
aria-expanded={isMobileExpanded}
|
|
223
|
+
onClick={handleMobileExpandedChange}
|
|
224
|
+
>
|
|
225
|
+
<span>{mobileDetailsLabel}</span>
|
|
226
|
+
{isMobileExpanded ? (
|
|
227
|
+
<Icon.ChevronUp size={20} />
|
|
228
|
+
) : (
|
|
229
|
+
<Icon.ChevronDown size={20} />
|
|
230
|
+
)}
|
|
231
|
+
</button>
|
|
232
|
+
</td>
|
|
233
|
+
)}
|
|
45
234
|
</tr>
|
|
46
235
|
);
|
|
47
236
|
};
|
|
@@ -11,12 +11,12 @@ const SortingIcon = ({ sortOrder, size = 16 }: SortingIconProps) => {
|
|
|
11
11
|
|
|
12
12
|
const leftArrowColour =
|
|
13
13
|
sortOrder === 'asc'
|
|
14
|
-
? theme.
|
|
15
|
-
: theme.
|
|
14
|
+
? theme.colour.icon.default
|
|
15
|
+
: theme.colour.icon.disabled;
|
|
16
16
|
const rightArrowColour =
|
|
17
17
|
sortOrder === 'desc'
|
|
18
|
-
? theme.
|
|
19
|
-
: theme.
|
|
18
|
+
? theme.colour.icon.default
|
|
19
|
+
: theme.colour.icon.disabled;
|
|
20
20
|
|
|
21
21
|
return (
|
|
22
22
|
<svg
|
|
@@ -100,14 +100,14 @@ describe('useTheme', () => {
|
|
|
100
100
|
);
|
|
101
101
|
|
|
102
102
|
expect(document.body).toHaveStyle({
|
|
103
|
-
backgroundColor: lightTheme.colour.bg.
|
|
103
|
+
backgroundColor: lightTheme.colour.bg.default,
|
|
104
104
|
color: lightTheme.colour.text.default,
|
|
105
105
|
});
|
|
106
106
|
|
|
107
107
|
fireEvent.click(screen.getByTestId('set-dark'));
|
|
108
108
|
|
|
109
109
|
expect(document.body).toHaveStyle({
|
|
110
|
-
backgroundColor: darkTheme.colour.bg.
|
|
110
|
+
backgroundColor: darkTheme.colour.bg.default,
|
|
111
111
|
color: darkTheme.colour.text.default,
|
|
112
112
|
});
|
|
113
113
|
});
|
package/lib/theme/useTheme.tsx
CHANGED