@arbor-education/design-system.components 0.13.1 → 0.15.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/CHANGELOG.md +18 -0
- package/dist/components/articleCard/ArticleCard.d.ts +30 -0
- package/dist/components/articleCard/ArticleCard.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.js +24 -0
- package/dist/components/articleCard/ArticleCard.js.map +1 -0
- package/dist/components/articleCard/ArticleCard.stories.d.ts +18 -0
- package/dist/components/articleCard/ArticleCard.stories.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.stories.js +112 -0
- package/dist/components/articleCard/ArticleCard.stories.js.map +1 -0
- package/dist/components/articleCard/ArticleCard.test.d.ts +2 -0
- package/dist/components/articleCard/ArticleCard.test.d.ts.map +1 -0
- package/dist/components/articleCard/ArticleCard.test.js +49 -0
- package/dist/components/articleCard/ArticleCard.test.js.map +1 -0
- package/dist/components/card/Card.d.ts +41 -12
- package/dist/components/card/Card.d.ts.map +1 -1
- package/dist/components/card/Card.js +46 -17
- package/dist/components/card/Card.js.map +1 -1
- package/dist/components/card/Card.stories.d.ts +9 -84
- package/dist/components/card/Card.stories.d.ts.map +1 -1
- package/dist/components/card/Card.stories.js +15 -73
- package/dist/components/card/Card.stories.js.map +1 -1
- package/dist/components/card/Card.test.js +50 -152
- package/dist/components/card/Card.test.js.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.d.ts.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.js +14 -2
- package/dist/components/formField/inputs/number/NumberInput.js.map +1 -1
- package/dist/components/formField/inputs/number/NumberInput.test.js +21 -0
- package/dist/components/formField/inputs/number/NumberInput.test.js.map +1 -1
- package/dist/components/formField/inputs/time/TimeInput.d.ts +1 -1
- package/dist/components/formField/inputs/time/TimeInput.stories.d.ts +1 -1
- package/dist/components/icoText/IcoText.d.ts +37 -0
- package/dist/components/icoText/IcoText.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.js +29 -0
- package/dist/components/icoText/IcoText.js.map +1 -0
- package/dist/components/icoText/IcoText.stories.d.ts +34 -0
- package/dist/components/icoText/IcoText.stories.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.stories.js +24 -0
- package/dist/components/icoText/IcoText.stories.js.map +1 -0
- package/dist/components/icoText/IcoText.test.d.ts +2 -0
- package/dist/components/icoText/IcoText.test.d.ts.map +1 -0
- package/dist/components/icoText/IcoText.test.js +27 -0
- package/dist/components/icoText/IcoText.test.js.map +1 -0
- package/dist/components/kpiCard/KPICard.d.ts +13 -0
- package/dist/components/kpiCard/KPICard.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.js +8 -0
- package/dist/components/kpiCard/KPICard.js.map +1 -0
- package/dist/components/kpiCard/KPICard.stories.d.ts +9 -0
- package/dist/components/kpiCard/KPICard.stories.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.stories.js +18 -0
- package/dist/components/kpiCard/KPICard.stories.js.map +1 -0
- package/dist/components/kpiCard/KPICard.test.d.ts +2 -0
- package/dist/components/kpiCard/KPICard.test.d.ts.map +1 -0
- package/dist/components/kpiCard/KPICard.test.js +37 -0
- package/dist/components/kpiCard/KPICard.test.js.map +1 -0
- package/dist/components/kvpList/KVPList.d.ts +34 -0
- package/dist/components/kvpList/KVPList.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.js +20 -0
- package/dist/components/kvpList/KVPList.js.map +1 -0
- package/dist/components/kvpList/KVPList.stories.d.ts +27 -0
- package/dist/components/kvpList/KVPList.stories.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.stories.js +18 -0
- package/dist/components/kvpList/KVPList.stories.js.map +1 -0
- package/dist/components/kvpList/KVPList.test.d.ts +2 -0
- package/dist/components/kvpList/KVPList.test.d.ts.map +1 -0
- package/dist/components/kvpList/KVPList.test.js +29 -0
- package/dist/components/kvpList/KVPList.test.js.map +1 -0
- package/dist/components/singleUser/SingleUser.d.ts +1 -1
- package/dist/components/table/Table.d.ts +1 -0
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +4 -2
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +1 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +88 -0
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +184 -0
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellEditors/NumberCellEditor.d.ts +13 -0
- package/dist/components/table/cellEditors/NumberCellEditor.d.ts.map +1 -0
- package/dist/components/table/cellEditors/NumberCellEditor.js +35 -0
- package/dist/components/table/cellEditors/NumberCellEditor.js.map +1 -0
- package/dist/components/tabs/TabsItem.stories.d.ts +2 -2
- package/dist/index.css +205 -22
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +14 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/articleCard/ArticleCard.stories.tsx +132 -0
- package/src/components/articleCard/ArticleCard.test.tsx +121 -0
- package/src/components/articleCard/ArticleCard.tsx +100 -0
- package/src/components/articleCard/articleCard.scss +39 -0
- package/src/components/card/Card.stories.tsx +35 -79
- package/src/components/card/Card.test.tsx +72 -190
- package/src/components/card/Card.tsx +117 -58
- package/src/components/card/card.scss +18 -31
- package/src/components/formField/inputs/number/NumberInput.test.tsx +28 -0
- package/src/components/formField/inputs/number/NumberInput.tsx +15 -0
- package/src/components/icoText/IcoText.stories.tsx +47 -0
- package/src/components/icoText/IcoText.test.tsx +41 -0
- package/src/components/icoText/IcoText.tsx +93 -0
- package/src/components/icoText/icoText.scss +34 -0
- package/src/components/kpiCard/KPICard.stories.tsx +47 -0
- package/src/components/kpiCard/KPICard.test.tsx +60 -0
- package/src/components/kpiCard/KPICard.tsx +45 -0
- package/src/components/kpiCard/kpiCard.scss +35 -0
- package/src/components/kvpList/KVPList.stories.tsx +51 -0
- package/src/components/kvpList/KVPList.test.tsx +66 -0
- package/src/components/kvpList/KVPList.tsx +109 -0
- package/src/components/kvpList/kvpList.scss +64 -0
- package/src/components/table/Table.stories.tsx +93 -0
- package/src/components/table/Table.test.tsx +255 -0
- package/src/components/table/Table.tsx +6 -0
- package/src/components/table/cellEditors/NumberCellEditor.tsx +83 -0
- package/src/components/table/cellEditors/numberCellEditor.scss +11 -0
- package/src/components/table/table.scss +11 -0
- package/src/index.scss +5 -0
- package/src/index.ts +14 -4
- package/src/tokens.scss +6 -0
- package/tokens/json/Arbor.json +30 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
|
|
3
|
+
export type KVPListProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
4
|
+
children?: React.ReactNode;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type KVPListRowProps = React.HTMLAttributes<HTMLDListElement> & {
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
orientation?: 'horizontal' | 'vertical';
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type KVPListTermProps = React.HTMLAttributes<HTMLElement> & {
|
|
13
|
+
children?: React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type KVPListDefinitionProps = React.HTMLAttributes<HTMLElement> & {
|
|
17
|
+
children?: React.ReactNode;
|
|
18
|
+
isRow?: boolean;
|
|
19
|
+
isPercentage?: boolean;
|
|
20
|
+
prominence?: 'neutral' | 'strong';
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const KVPListRoot = ({
|
|
24
|
+
children,
|
|
25
|
+
className,
|
|
26
|
+
role,
|
|
27
|
+
'aria-label': ariaLabel,
|
|
28
|
+
'aria-labelledby': ariaLabelledBy,
|
|
29
|
+
...rest
|
|
30
|
+
}: KVPListProps): React.JSX.Element => (
|
|
31
|
+
<div
|
|
32
|
+
className={classNames('ds-kvp-list', className)}
|
|
33
|
+
role={role ?? (ariaLabel || ariaLabelledBy ? 'group' : undefined)}
|
|
34
|
+
aria-label={ariaLabel}
|
|
35
|
+
aria-labelledby={ariaLabelledBy}
|
|
36
|
+
{...rest}
|
|
37
|
+
>
|
|
38
|
+
{children}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const KVPListRow = ({
|
|
43
|
+
children,
|
|
44
|
+
className,
|
|
45
|
+
orientation = 'vertical',
|
|
46
|
+
...rest
|
|
47
|
+
}: KVPListRowProps): React.JSX.Element => (
|
|
48
|
+
<dl
|
|
49
|
+
className={classNames(
|
|
50
|
+
'ds-kvp-list__row',
|
|
51
|
+
`ds-kvp-list__row--${orientation}`,
|
|
52
|
+
className,
|
|
53
|
+
)}
|
|
54
|
+
{...rest}
|
|
55
|
+
>
|
|
56
|
+
{children}
|
|
57
|
+
</dl>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const KVPListTerm = ({
|
|
61
|
+
children,
|
|
62
|
+
className,
|
|
63
|
+
...rest
|
|
64
|
+
}: KVPListTermProps): React.JSX.Element => (
|
|
65
|
+
<dt className={classNames('ds-kvp-list__term', className)} {...rest}>
|
|
66
|
+
{children}
|
|
67
|
+
</dt>
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
const KVPListDefinition = ({
|
|
71
|
+
children,
|
|
72
|
+
className,
|
|
73
|
+
isRow = false,
|
|
74
|
+
isPercentage = false,
|
|
75
|
+
prominence = 'neutral',
|
|
76
|
+
...rest
|
|
77
|
+
}: KVPListDefinitionProps): React.JSX.Element => (
|
|
78
|
+
<dd
|
|
79
|
+
className={classNames(
|
|
80
|
+
'ds-kvp-list__description',
|
|
81
|
+
`ds-kvp-list__description--${prominence}`,
|
|
82
|
+
{
|
|
83
|
+
'ds-kvp-list__description--row': isRow,
|
|
84
|
+
},
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
{...rest}
|
|
88
|
+
>
|
|
89
|
+
{children}
|
|
90
|
+
{isPercentage
|
|
91
|
+
? (
|
|
92
|
+
<span className="ds-kvp-list__suffix">
|
|
93
|
+
%
|
|
94
|
+
</span>
|
|
95
|
+
)
|
|
96
|
+
: null}
|
|
97
|
+
</dd>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
KVPListRoot.displayName = 'KVPList';
|
|
101
|
+
KVPListRow.displayName = 'KVPList.Row';
|
|
102
|
+
KVPListTerm.displayName = 'KVPList.Term';
|
|
103
|
+
KVPListDefinition.displayName = 'KVPList.Definition';
|
|
104
|
+
|
|
105
|
+
export const KVPList = Object.assign(KVPListRoot, {
|
|
106
|
+
Row: KVPListRow,
|
|
107
|
+
Term: KVPListTerm,
|
|
108
|
+
Definition: KVPListDefinition,
|
|
109
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
.ds-kvp-list {
|
|
2
|
+
display: flex;
|
|
3
|
+
width: 100%;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: var(--spacing-medium);
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.ds-kvp-list__term,
|
|
10
|
+
.ds-kvp-list__description {
|
|
11
|
+
margin: 0;
|
|
12
|
+
flex: 1;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.ds-kvp-list__term,
|
|
16
|
+
.ds-kvp-list__description--neutral {
|
|
17
|
+
color: var(--kvp-list-color-text-subtle);
|
|
18
|
+
font-family: var(--type-body-p-family);
|
|
19
|
+
font-size: var(--type-body-p-size);
|
|
20
|
+
font-weight: var(--type-body-p-weight);
|
|
21
|
+
line-height: var(--type-body-line-height);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.ds-kvp-list__description--strong {
|
|
25
|
+
color: var(--kvp-list-color-text-strong);
|
|
26
|
+
font-family: var(--type-headings-h2-family);
|
|
27
|
+
font-size: var(--type-headings-h2-size);
|
|
28
|
+
font-weight: var(--type-headings-h2-weight);
|
|
29
|
+
line-height: var(--type-headings-h2-line-height);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ds-kvp-list__description--row {
|
|
33
|
+
flex-basis: 100%;
|
|
34
|
+
width: 100%;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.ds-kvp-list__suffix {
|
|
38
|
+
margin-left: var(--spacing-xsmall);
|
|
39
|
+
font: inherit;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.ds-kvp-list__description {
|
|
43
|
+
text-align: right;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.ds-kvp-list__row {
|
|
47
|
+
display: flex;
|
|
48
|
+
width: 100%;
|
|
49
|
+
margin: 0;
|
|
50
|
+
gap: var(--spacing-small);
|
|
51
|
+
|
|
52
|
+
&--vertical {
|
|
53
|
+
flex-direction: column;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&--horizontal {
|
|
57
|
+
flex-wrap: wrap;
|
|
58
|
+
align-items: center;
|
|
59
|
+
|
|
60
|
+
.ds-kvp-list__description:not(.ds-kvp-list__description--row) {
|
|
61
|
+
flex: 0 0 auto;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -52,6 +52,7 @@ interface RowData {
|
|
|
52
52
|
email: { value: string } & CellColors;
|
|
53
53
|
role: { value: string } & CellColors;
|
|
54
54
|
status: { value: string } & CellColors;
|
|
55
|
+
marks: number;
|
|
55
56
|
active: boolean;
|
|
56
57
|
time: string;
|
|
57
58
|
dateOfBirth: Date;
|
|
@@ -64,6 +65,7 @@ const sampleData: RowData[] = [
|
|
|
64
65
|
email: { value: 'alice.johnson@example.com' },
|
|
65
66
|
role: { value: 'Developer' },
|
|
66
67
|
status: { value: 'Active' },
|
|
68
|
+
marks: 84,
|
|
67
69
|
active: true,
|
|
68
70
|
time: '2026-01-28 12:00:00',
|
|
69
71
|
dateOfBirth: new Date(1990, 0, 1),
|
|
@@ -74,6 +76,7 @@ const sampleData: RowData[] = [
|
|
|
74
76
|
email: { value: 'bob.smith@example.com' },
|
|
75
77
|
role: { value: 'Designer' },
|
|
76
78
|
status: { value: 'Active' },
|
|
79
|
+
marks: 76,
|
|
77
80
|
active: false,
|
|
78
81
|
time: '2026-01-28 13:00:00',
|
|
79
82
|
dateOfBirth: new Date(1991, 1, 2),
|
|
@@ -84,6 +87,7 @@ const sampleData: RowData[] = [
|
|
|
84
87
|
email: { value: 'charlie.brown@example.com' },
|
|
85
88
|
role: { value: 'Manager' },
|
|
86
89
|
status: { value: 'Inactive' },
|
|
90
|
+
marks: 92,
|
|
87
91
|
active: true,
|
|
88
92
|
time: '2026-01-28 14:00:00',
|
|
89
93
|
dateOfBirth: new Date(1992, 2, 3),
|
|
@@ -94,6 +98,7 @@ const sampleData: RowData[] = [
|
|
|
94
98
|
email: { value: 'diana.prince@example.com' },
|
|
95
99
|
role: { value: 'Developer' },
|
|
96
100
|
status: { value: 'Active' },
|
|
101
|
+
marks: 88,
|
|
97
102
|
active: false,
|
|
98
103
|
time: '2026-01-28 15:00:00',
|
|
99
104
|
dateOfBirth: new Date(1993, 3, 4),
|
|
@@ -104,6 +109,7 @@ const sampleData: RowData[] = [
|
|
|
104
109
|
email: { value: 'ethan.hunt@example.com' },
|
|
105
110
|
role: { value: 'Analyst' },
|
|
106
111
|
status: { value: 'Active' },
|
|
112
|
+
marks: 69,
|
|
107
113
|
active: false,
|
|
108
114
|
time: '2026-01-28 16:00:00',
|
|
109
115
|
dateOfBirth: new Date(1994, 4, 5),
|
|
@@ -154,6 +160,17 @@ const sampleColumnDefs: (ColDef | ColGroupDef)[] = [
|
|
|
154
160
|
valueFormatter: Table.DefaultValueFormatter,
|
|
155
161
|
},
|
|
156
162
|
{ field: 'status', valueFormatter: Table.DefaultValueFormatter },
|
|
163
|
+
{
|
|
164
|
+
field: 'marks',
|
|
165
|
+
cellDataType: 'number',
|
|
166
|
+
cellEditor: 'dsNumberCellEditor',
|
|
167
|
+
cellEditorParams: {
|
|
168
|
+
min: 0,
|
|
169
|
+
max: 100,
|
|
170
|
+
step: 1,
|
|
171
|
+
disableSpinners: false,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
157
174
|
{
|
|
158
175
|
field: 'active',
|
|
159
176
|
filter: { component: BooleanFilter, doesFilterPass: doesBooleanFilterPass },
|
|
@@ -168,6 +185,60 @@ const sampleColumnDefs: (ColDef | ColGroupDef)[] = [
|
|
|
168
185
|
},
|
|
169
186
|
];
|
|
170
187
|
|
|
188
|
+
const verticalHeaderTextColumnDefs: (ColDef | ColGroupDef)[] = [
|
|
189
|
+
{
|
|
190
|
+
headerName: 'Details',
|
|
191
|
+
children: [
|
|
192
|
+
{
|
|
193
|
+
field: 'name',
|
|
194
|
+
headerName: 'Preferred Learner Name',
|
|
195
|
+
filter: 'agSetColumnFilter',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
field: 'email',
|
|
199
|
+
headerName: 'Primary Contact Email Address',
|
|
200
|
+
filter: 'agSetColumnFilter',
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
field: 'role',
|
|
204
|
+
headerName: 'Role',
|
|
205
|
+
filter: 'agSetColumnFilter',
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
field: 'dateOfBirth',
|
|
209
|
+
headerName: 'Date of Birth',
|
|
210
|
+
filter: 'agDateColumnFilter',
|
|
211
|
+
filterParams: { buttons: ['clear', 'apply'] },
|
|
212
|
+
valueFormatter: params =>
|
|
213
|
+
params.value instanceof Date
|
|
214
|
+
? params.value.toLocaleDateString()
|
|
215
|
+
: params.value,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
field: 'status',
|
|
222
|
+
headerName: 'Current Enrollment Status',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
field: 'marks',
|
|
226
|
+
headerName: 'Overall Score (%)',
|
|
227
|
+
cellDataType: 'number',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
field: 'active',
|
|
231
|
+
headerName: 'Active?',
|
|
232
|
+
cellDataType: 'boolean',
|
|
233
|
+
editable: false,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
field: 'time',
|
|
237
|
+
headerName: 'Most Recent Activity Timestamp',
|
|
238
|
+
editable: false,
|
|
239
|
+
},
|
|
240
|
+
];
|
|
241
|
+
|
|
171
242
|
const footerContent = [
|
|
172
243
|
<Button key={0} variant="primary">
|
|
173
244
|
Button 1
|
|
@@ -201,6 +272,28 @@ export const Default: Story = {
|
|
|
201
272
|
},
|
|
202
273
|
};
|
|
203
274
|
|
|
275
|
+
export const WithVerticalHeaderText: Story = {
|
|
276
|
+
parameters: {
|
|
277
|
+
docs: {
|
|
278
|
+
description: {
|
|
279
|
+
story:
|
|
280
|
+
'Use the `verticalHeaderText` prop when columns are narrow and header labels need to remain readable.',
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
args: {
|
|
285
|
+
rowData: sampleData,
|
|
286
|
+
columnDefs: verticalHeaderTextColumnDefs,
|
|
287
|
+
defaultColDef: {
|
|
288
|
+
...defaultColDef,
|
|
289
|
+
wrapHeaderText: true,
|
|
290
|
+
autoHeaderHeight: true,
|
|
291
|
+
},
|
|
292
|
+
domLayout: 'autoHeight',
|
|
293
|
+
verticalHeaderText: true,
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
|
|
204
297
|
export const WithFooter: Story = {
|
|
205
298
|
args: {
|
|
206
299
|
rowData: sampleData,
|
|
@@ -36,6 +36,20 @@ describe('Table', () => {
|
|
|
36
36
|
expect(screen.getByTestId('test-table')).toHaveClass('ds-table__container');
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
describe('verticalHeaderText', () => {
|
|
40
|
+
test('applies vertical header text class when enabled', () => {
|
|
41
|
+
const { container } = render(<Table verticalHeaderText />);
|
|
42
|
+
|
|
43
|
+
expect(container.querySelector('.ds-table')).toHaveClass('ds-table--vertical-header-text');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('does not apply vertical header text class by default', () => {
|
|
47
|
+
const { container } = render(<Table />);
|
|
48
|
+
|
|
49
|
+
expect(container.querySelector('.ds-table')).not.toHaveClass('ds-table--vertical-header-text');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
39
53
|
describe('TableHeader', () => {
|
|
40
54
|
test('renders header when headerContent is provided', () => {
|
|
41
55
|
render(<Table headerContent={<div>Header content</div>} />);
|
|
@@ -1406,6 +1420,247 @@ describe('Table', () => {
|
|
|
1406
1420
|
|
|
1407
1421
|
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ oldValue: 30, newValue: 35 }));
|
|
1408
1422
|
});
|
|
1423
|
+
|
|
1424
|
+
test('supports editing number fields with the number cell editor spinners', async () => {
|
|
1425
|
+
const onCellValueChanged = vi.fn();
|
|
1426
|
+
const columnDefs = [{
|
|
1427
|
+
field: 'age',
|
|
1428
|
+
headerName: 'Age',
|
|
1429
|
+
editable: true,
|
|
1430
|
+
cellDataType: 'number',
|
|
1431
|
+
cellEditor: 'dsNumberCellEditor',
|
|
1432
|
+
cellEditorParams: {
|
|
1433
|
+
min: 0,
|
|
1434
|
+
max: 100,
|
|
1435
|
+
step: 5,
|
|
1436
|
+
disableSpinners: false,
|
|
1437
|
+
},
|
|
1438
|
+
}];
|
|
1439
|
+
const rowData = [
|
|
1440
|
+
{ id: 1, name: 'John Doe', age: 30 },
|
|
1441
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1442
|
+
];
|
|
1443
|
+
|
|
1444
|
+
render(
|
|
1445
|
+
<Table
|
|
1446
|
+
columnDefs={columnDefs}
|
|
1447
|
+
rowData={rowData}
|
|
1448
|
+
onCellValueChanged={onCellValueChanged}
|
|
1449
|
+
/>,
|
|
1450
|
+
);
|
|
1451
|
+
|
|
1452
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1453
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1454
|
+
|
|
1455
|
+
const cell = screen.getByText('30');
|
|
1456
|
+
await userEvent.dblClick(cell);
|
|
1457
|
+
|
|
1458
|
+
const editorInput = await screen.findByRole('textbox');
|
|
1459
|
+
expect(editorInput).toHaveFocus();
|
|
1460
|
+
|
|
1461
|
+
await userEvent.click(screen.getByLabelText('Plus button'));
|
|
1462
|
+
await userEvent.keyboard('{Enter}');
|
|
1463
|
+
|
|
1464
|
+
await waitFor(() => {
|
|
1465
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ oldValue: 30, newValue: 35 }));
|
|
1469
|
+
});
|
|
1470
|
+
|
|
1471
|
+
test('clamps out-of-range number editor values when committing with enter', async () => {
|
|
1472
|
+
const onCellValueChanged = vi.fn();
|
|
1473
|
+
const columnDefs = [{
|
|
1474
|
+
field: 'age',
|
|
1475
|
+
headerName: 'Age',
|
|
1476
|
+
editable: true,
|
|
1477
|
+
cellDataType: 'number',
|
|
1478
|
+
cellEditor: 'dsNumberCellEditor',
|
|
1479
|
+
cellEditorParams: {
|
|
1480
|
+
min: 0,
|
|
1481
|
+
max: 100,
|
|
1482
|
+
step: 1,
|
|
1483
|
+
disableSpinners: true,
|
|
1484
|
+
},
|
|
1485
|
+
}];
|
|
1486
|
+
const rowData = [
|
|
1487
|
+
{ id: 1, name: 'John Doe', age: 30 },
|
|
1488
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1489
|
+
];
|
|
1490
|
+
|
|
1491
|
+
render(
|
|
1492
|
+
<Table
|
|
1493
|
+
columnDefs={columnDefs}
|
|
1494
|
+
rowData={rowData}
|
|
1495
|
+
onCellValueChanged={onCellValueChanged}
|
|
1496
|
+
/>,
|
|
1497
|
+
);
|
|
1498
|
+
|
|
1499
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1500
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1501
|
+
|
|
1502
|
+
const cell = screen.getByText('30');
|
|
1503
|
+
await userEvent.dblClick(cell);
|
|
1504
|
+
|
|
1505
|
+
const editorInput = await screen.findByRole('textbox');
|
|
1506
|
+
await userEvent.clear(editorInput);
|
|
1507
|
+
await userEvent.type(editorInput, '150');
|
|
1508
|
+
await userEvent.keyboard('{Enter}');
|
|
1509
|
+
|
|
1510
|
+
await waitFor(() => {
|
|
1511
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1512
|
+
});
|
|
1513
|
+
|
|
1514
|
+
const lastCellChangeEvent = onCellValueChanged.mock.calls.at(-1)?.[0];
|
|
1515
|
+
expect(lastCellChangeEvent?.oldValue).toBe(30);
|
|
1516
|
+
expect(lastCellChangeEvent?.newValue).toBe(100);
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
test('clamps below-min number editor values when committing with enter', async () => {
|
|
1520
|
+
const onCellValueChanged = vi.fn();
|
|
1521
|
+
const columnDefs = [{
|
|
1522
|
+
field: 'age',
|
|
1523
|
+
headerName: 'Age',
|
|
1524
|
+
editable: true,
|
|
1525
|
+
cellDataType: 'number',
|
|
1526
|
+
cellEditor: 'dsNumberCellEditor',
|
|
1527
|
+
cellEditorParams: {
|
|
1528
|
+
min: 0,
|
|
1529
|
+
max: 100,
|
|
1530
|
+
step: 1,
|
|
1531
|
+
disableSpinners: true,
|
|
1532
|
+
},
|
|
1533
|
+
}];
|
|
1534
|
+
const rowData = [
|
|
1535
|
+
{ id: 1, name: 'John Doe', age: 30 },
|
|
1536
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1537
|
+
];
|
|
1538
|
+
|
|
1539
|
+
render(
|
|
1540
|
+
<Table
|
|
1541
|
+
columnDefs={columnDefs}
|
|
1542
|
+
rowData={rowData}
|
|
1543
|
+
onCellValueChanged={onCellValueChanged}
|
|
1544
|
+
/>,
|
|
1545
|
+
);
|
|
1546
|
+
|
|
1547
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1548
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1549
|
+
|
|
1550
|
+
const cell = screen.getByText('30');
|
|
1551
|
+
await userEvent.dblClick(cell);
|
|
1552
|
+
|
|
1553
|
+
const editorInput = await screen.findByRole('textbox');
|
|
1554
|
+
await userEvent.clear(editorInput);
|
|
1555
|
+
await userEvent.type(editorInput, '-5');
|
|
1556
|
+
await userEvent.keyboard('{Enter}');
|
|
1557
|
+
|
|
1558
|
+
await waitFor(() => {
|
|
1559
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1560
|
+
});
|
|
1561
|
+
|
|
1562
|
+
const lastCellChangeEvent = onCellValueChanged.mock.calls.at(-1)?.[0];
|
|
1563
|
+
expect(lastCellChangeEvent?.oldValue).toBe(30);
|
|
1564
|
+
expect(lastCellChangeEvent?.newValue).toBe(0);
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
test('does not clamp in-progress number editor typing before commit', async () => {
|
|
1568
|
+
const onCellValueChanged = vi.fn();
|
|
1569
|
+
const columnDefs = [{
|
|
1570
|
+
field: 'age',
|
|
1571
|
+
headerName: 'Age',
|
|
1572
|
+
editable: true,
|
|
1573
|
+
cellDataType: 'number',
|
|
1574
|
+
cellEditor: 'dsNumberCellEditor',
|
|
1575
|
+
cellEditorParams: {
|
|
1576
|
+
min: 50,
|
|
1577
|
+
max: 100,
|
|
1578
|
+
step: 1,
|
|
1579
|
+
disableSpinners: true,
|
|
1580
|
+
},
|
|
1581
|
+
}];
|
|
1582
|
+
const rowData = [
|
|
1583
|
+
{ id: 1, name: 'John Doe', age: 60 },
|
|
1584
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1585
|
+
];
|
|
1586
|
+
|
|
1587
|
+
render(
|
|
1588
|
+
<Table
|
|
1589
|
+
columnDefs={columnDefs}
|
|
1590
|
+
rowData={rowData}
|
|
1591
|
+
onCellValueChanged={onCellValueChanged}
|
|
1592
|
+
/>,
|
|
1593
|
+
);
|
|
1594
|
+
|
|
1595
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1596
|
+
await waitFor(() => expect(screen.getByText('60')).toBeInTheDocument());
|
|
1597
|
+
|
|
1598
|
+
const cell = screen.getByText('60');
|
|
1599
|
+
await userEvent.dblClick(cell);
|
|
1600
|
+
|
|
1601
|
+
const editorInput = await screen.findByRole('textbox');
|
|
1602
|
+
await userEvent.clear(editorInput);
|
|
1603
|
+
await userEvent.type(editorInput, '6');
|
|
1604
|
+
expect(editorInput).toHaveValue('6');
|
|
1605
|
+
await userEvent.type(editorInput, '5');
|
|
1606
|
+
await userEvent.keyboard('{Enter}');
|
|
1607
|
+
|
|
1608
|
+
await waitFor(() => {
|
|
1609
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
const lastCellChangeEvent = onCellValueChanged.mock.calls.at(-1)?.[0];
|
|
1613
|
+
expect(lastCellChangeEvent?.oldValue).toBe(60);
|
|
1614
|
+
expect(lastCellChangeEvent?.newValue).toBe(65);
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1617
|
+
test('supports clearing number editor values and committing null', async () => {
|
|
1618
|
+
const onCellValueChanged = vi.fn();
|
|
1619
|
+
const columnDefs = [{
|
|
1620
|
+
field: 'age',
|
|
1621
|
+
headerName: 'Age',
|
|
1622
|
+
editable: true,
|
|
1623
|
+
cellDataType: 'number',
|
|
1624
|
+
cellEditor: 'dsNumberCellEditor',
|
|
1625
|
+
cellEditorParams: {
|
|
1626
|
+
min: 0,
|
|
1627
|
+
max: 100,
|
|
1628
|
+
step: 1,
|
|
1629
|
+
disableSpinners: true,
|
|
1630
|
+
},
|
|
1631
|
+
}];
|
|
1632
|
+
const rowData = [
|
|
1633
|
+
{ id: 1, name: 'John Doe', age: 30 },
|
|
1634
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1635
|
+
];
|
|
1636
|
+
|
|
1637
|
+
render(
|
|
1638
|
+
<Table
|
|
1639
|
+
columnDefs={columnDefs}
|
|
1640
|
+
rowData={rowData}
|
|
1641
|
+
onCellValueChanged={onCellValueChanged}
|
|
1642
|
+
/>,
|
|
1643
|
+
);
|
|
1644
|
+
|
|
1645
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1646
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1647
|
+
|
|
1648
|
+
const cell = screen.getByText('30');
|
|
1649
|
+
await userEvent.dblClick(cell);
|
|
1650
|
+
|
|
1651
|
+
const editorInput = await screen.findByRole('textbox');
|
|
1652
|
+
expect(screen.queryByLabelText('Plus button')).not.toBeInTheDocument();
|
|
1653
|
+
expect(screen.queryByLabelText('Minus button')).not.toBeInTheDocument();
|
|
1654
|
+
|
|
1655
|
+
await userEvent.clear(editorInput);
|
|
1656
|
+
await userEvent.keyboard('{Enter}');
|
|
1657
|
+
|
|
1658
|
+
await waitFor(() => {
|
|
1659
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ oldValue: 30, newValue: null }));
|
|
1663
|
+
});
|
|
1409
1664
|
});
|
|
1410
1665
|
|
|
1411
1666
|
/**
|
|
@@ -16,6 +16,7 @@ import { useTableSettings, type UseTableSettingsParams } from './useTableSetting
|
|
|
16
16
|
import { setAgGridLicenseKey } from 'Utils/setAgGridLicenseKey';
|
|
17
17
|
import { toggleRowSelectionInCurrentRange } from './toggleRowSelectionInCurrentRange';
|
|
18
18
|
import { defaultValueFormatter, DSDefaultColDef, shouldSuppressFocus } from './DSDefaultColDef';
|
|
19
|
+
import { NumberCellEditor } from './cellEditors/NumberCellEditor';
|
|
19
20
|
import { ButtonCellRenderer } from './cellRenderers/ButtonCellRenderer';
|
|
20
21
|
import { InlineTextCellRenderer } from './cellRenderers/InlineTextCellRenderer';
|
|
21
22
|
import { SelectDropdownCellRenderer } from './cellRenderers/SelectDropdownCellRenderer';
|
|
@@ -45,6 +46,7 @@ type TableProps<TData = any> = {
|
|
|
45
46
|
'enableSimultaneousRangeAndRowSelection'?: boolean;
|
|
46
47
|
'disableDragSelect'?: boolean;
|
|
47
48
|
'tableTheme'?: string;
|
|
49
|
+
'verticalHeaderText'?: boolean;
|
|
48
50
|
} & UseTableSettingsParams & AgGridReactProps<TData>;
|
|
49
51
|
|
|
50
52
|
setAgGridLicenseKey();
|
|
@@ -76,6 +78,8 @@ export const Table = (props: TableProps) => {
|
|
|
76
78
|
components = {},
|
|
77
79
|
tableTheme,
|
|
78
80
|
onCellFocused,
|
|
81
|
+
verticalHeaderText = false,
|
|
82
|
+
className: propsClassName,
|
|
79
83
|
...rest
|
|
80
84
|
} = props;
|
|
81
85
|
|
|
@@ -151,6 +155,7 @@ export const Table = (props: TableProps) => {
|
|
|
151
155
|
setGridApi(api);
|
|
152
156
|
onGridReady?.(event);
|
|
153
157
|
}}
|
|
158
|
+
className={classNames('ds-table', { 'ds-table--vertical-header-text': verticalHeaderText }, propsClassName)}
|
|
154
159
|
enableFilterHandlers
|
|
155
160
|
suppressPaginationPanel
|
|
156
161
|
onCellSelectionChanged={(event) => {
|
|
@@ -185,6 +190,7 @@ export const Table = (props: TableProps) => {
|
|
|
185
190
|
dsSelectDropdownCellRenderer: SelectDropdownCellRenderer,
|
|
186
191
|
dsBooleanFilter: BooleanFilter,
|
|
187
192
|
dsTimeFilter: TimeFilter,
|
|
193
|
+
dsNumberCellEditor: NumberCellEditor,
|
|
188
194
|
dsDateCellEditor: DateCellEditor,
|
|
189
195
|
dsCheckboxCellRenderer: CheckboxCellRenderer,
|
|
190
196
|
dsBooleanCellRenderer: BooleanCellRenderer,
|