@arbor-education/design-system.components 0.3.0 → 0.3.2
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 +14 -0
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts +1 -0
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.d.ts.map +1 -1
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js +19 -12
- package/dist/components/formField/inputs/selectDropdown/SelectDropdown.js.map +1 -1
- package/dist/components/table/DSDefaultColDef.d.ts +5 -0
- package/dist/components/table/DSDefaultColDef.d.ts.map +1 -0
- package/dist/components/table/DSDefaultColDef.js +32 -0
- package/dist/components/table/DSDefaultColDef.js.map +1 -0
- package/dist/components/table/Table.d.ts +6 -6
- package/dist/components/table/Table.d.ts.map +1 -1
- package/dist/components/table/Table.js +43 -22
- package/dist/components/table/Table.js.map +1 -1
- package/dist/components/table/Table.stories.d.ts +3 -0
- package/dist/components/table/Table.stories.d.ts.map +1 -1
- package/dist/components/table/Table.stories.js +265 -24
- package/dist/components/table/Table.stories.js.map +1 -1
- package/dist/components/table/Table.test.js +362 -7
- package/dist/components/table/Table.test.js.map +1 -1
- package/dist/components/table/cellColorStyles.d.ts +34 -0
- package/dist/components/table/cellColorStyles.d.ts.map +1 -0
- package/dist/components/table/cellColorStyles.js +52 -0
- package/dist/components/table/cellColorStyles.js.map +1 -0
- package/dist/components/table/cellRenderers/ButtonCellRenderer.d.ts.map +1 -1
- package/dist/components/table/cellRenderers/ButtonCellRenderer.js +15 -2
- package/dist/components/table/cellRenderers/ButtonCellRenderer.js.map +1 -1
- package/dist/components/table/cellRenderers/DefaultCellRenderer.d.ts +3 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.js +11 -0
- package/dist/components/table/cellRenderers/DefaultCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.d.ts +7 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.js +7 -0
- package/dist/components/table/cellRenderers/InlineTextCellRenderer.js.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellEditor.d.ts +8 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellEditor.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellEditor.js +19 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellEditor.js.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.d.ts +8 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.d.ts.map +1 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js +18 -0
- package/dist/components/table/cellRenderers/SelectDropdownCellRenderer.js.map +1 -0
- package/dist/components/table/pagination/TableSettingsDropdown.js +4 -5
- package/dist/components/table/pagination/TableSettingsDropdown.js.map +1 -1
- package/dist/components/table/theme/baseThemeParams.d.ts +14 -0
- package/dist/components/table/theme/baseThemeParams.d.ts.map +1 -0
- package/dist/components/table/{tableTheme.js → theme/baseThemeParams.js} +3 -7
- package/dist/components/table/theme/baseThemeParams.js.map +1 -0
- package/dist/components/table/theme/defaultTheme.d.ts +2 -0
- package/dist/components/table/theme/defaultTheme.d.ts.map +1 -0
- package/dist/components/table/theme/defaultTheme.js +9 -0
- package/dist/components/table/theme/defaultTheme.js.map +1 -0
- package/dist/components/table/theme/tidyTheme.d.ts +2 -0
- package/dist/components/table/theme/tidyTheme.d.ts.map +1 -0
- package/dist/components/table/theme/tidyTheme.js +10 -0
- package/dist/components/table/theme/tidyTheme.js.map +1 -0
- package/dist/components/table/useTableSettings.d.ts +5 -2
- package/dist/components/table/useTableSettings.d.ts.map +1 -1
- package/dist/components/table/useTableSettings.js +11 -3
- package/dist/components/table/useTableSettings.js.map +1 -1
- package/dist/index.css +14 -0
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/focusFirstFocusableElement.d.ts +2 -0
- package/dist/utils/focusFirstFocusableElement.d.ts.map +1 -0
- package/dist/utils/focusFirstFocusableElement.js +12 -0
- package/dist/utils/focusFirstFocusableElement.js.map +1 -0
- package/dist/utils/focusFirstFocusableElement.test.d.ts +2 -0
- package/dist/utils/focusFirstFocusableElement.test.d.ts.map +1 -0
- package/dist/utils/focusFirstFocusableElement.test.js +109 -0
- package/dist/utils/focusFirstFocusableElement.test.js.map +1 -0
- package/package.json +1 -1
- package/src/components/formField/inputs/selectDropdown/SelectDropdown.tsx +20 -14
- package/src/components/table/DSDefaultColDef.ts +37 -0
- package/src/components/table/Table.stories.tsx +284 -28
- package/src/components/table/Table.test.tsx +543 -7
- package/src/components/table/Table.tsx +55 -23
- package/src/components/table/cellColorStyles.ts +70 -0
- package/src/components/table/cellRenderers/ButtonCellRenderer.tsx +16 -2
- package/src/components/table/cellRenderers/DefaultCellRenderer.tsx +23 -0
- package/src/components/table/cellRenderers/InlineTextCellRenderer.tsx +10 -0
- package/src/components/table/cellRenderers/SelectDropdownCellEditor.tsx +43 -0
- package/src/components/table/cellRenderers/SelectDropdownCellRenderer.tsx +37 -0
- package/src/components/table/pagination/TableSettingsDropdown.tsx +5 -5
- package/src/components/table/table.scss +16 -0
- package/src/components/table/{tableTheme.ts → theme/baseThemeParams.ts} +2 -7
- package/src/components/table/theme/defaultTheme.ts +9 -0
- package/src/components/table/theme/tidyTheme.ts +10 -0
- package/src/components/table/useTableSettings.ts +15 -3
- package/src/index.ts +2 -0
- package/src/utils/focusFirstFocusableElement.test.ts +150 -0
- package/src/utils/focusFirstFocusableElement.ts +21 -0
- package/dist/components/table/tableTheme.d.ts +0 -2
- package/dist/components/table/tableTheme.d.ts.map +0 -1
- package/dist/components/table/tableTheme.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, expectTypeOf, test, vi } from 'vitest';
|
|
2
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
+
import { act, render, screen, waitFor } from '@testing-library/react';
|
|
3
3
|
import { Table, TABLE_SPACING } from './Table';
|
|
4
4
|
import '@testing-library/jest-dom/vitest';
|
|
5
5
|
import { BulkActionsDropdown } from 'Components/table/BulkActionsDropdown';
|
|
@@ -7,6 +7,7 @@ import { HideColumnsDropdown } from 'Components/table/HideColumnsDropdown';
|
|
|
7
7
|
import { TableSettingsDropdown } from './pagination/TableSettingsDropdown';
|
|
8
8
|
import userEvent from '@testing-library/user-event';
|
|
9
9
|
import type { GridApi } from 'ag-grid-enterprise';
|
|
10
|
+
import * as focusFirstFocusableElementModule from 'Utils/focusFirstFocusableElement';
|
|
10
11
|
|
|
11
12
|
describe('Table', () => {
|
|
12
13
|
test('renders table container', () => {
|
|
@@ -594,7 +595,7 @@ describe('Table', () => {
|
|
|
594
595
|
});
|
|
595
596
|
});
|
|
596
597
|
|
|
597
|
-
test('Cell colours checkbox is
|
|
598
|
+
test('Cell colours checkbox is checked by default', async () => {
|
|
598
599
|
render(
|
|
599
600
|
<Table
|
|
600
601
|
headerContent={<TableSettingsDropdown />}
|
|
@@ -605,11 +606,11 @@ describe('Table', () => {
|
|
|
605
606
|
|
|
606
607
|
await waitFor(() => {
|
|
607
608
|
const cellColoursCheckbox = screen.getByLabelText('Cell colours') as HTMLInputElement;
|
|
608
|
-
expect(cellColoursCheckbox.checked).toBe(
|
|
609
|
+
expect(cellColoursCheckbox.checked).toBe(true);
|
|
609
610
|
});
|
|
610
611
|
});
|
|
611
612
|
|
|
612
|
-
test('can toggle Cell colours checkbox
|
|
613
|
+
test('can toggle Cell colours checkbox off', async () => {
|
|
613
614
|
render(
|
|
614
615
|
<Table
|
|
615
616
|
headerContent={<TableSettingsDropdown />}
|
|
@@ -622,11 +623,11 @@ describe('Table', () => {
|
|
|
622
623
|
await userEvent.click(cellColoursCheckbox);
|
|
623
624
|
|
|
624
625
|
await waitFor(() => {
|
|
625
|
-
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(
|
|
626
|
+
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(false);
|
|
626
627
|
});
|
|
627
628
|
});
|
|
628
629
|
|
|
629
|
-
test('can toggle Cell colours checkbox
|
|
630
|
+
test('can toggle Cell colours checkbox on after turning it off', async () => {
|
|
630
631
|
render(
|
|
631
632
|
<Table
|
|
632
633
|
headerContent={<TableSettingsDropdown />}
|
|
@@ -640,7 +641,7 @@ describe('Table', () => {
|
|
|
640
641
|
await userEvent.click(cellColoursCheckbox);
|
|
641
642
|
|
|
642
643
|
await waitFor(() => {
|
|
643
|
-
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(
|
|
644
|
+
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(true);
|
|
644
645
|
});
|
|
645
646
|
});
|
|
646
647
|
|
|
@@ -960,6 +961,215 @@ describe('Table', () => {
|
|
|
960
961
|
expect(onColumnBordersChanged).toHaveBeenCalledWith(false);
|
|
961
962
|
});
|
|
962
963
|
});
|
|
964
|
+
|
|
965
|
+
test('applies both foreground and background colours when enabled', async () => {
|
|
966
|
+
const columnDefs = [
|
|
967
|
+
{
|
|
968
|
+
field: 'status',
|
|
969
|
+
headerName: 'Status',
|
|
970
|
+
},
|
|
971
|
+
];
|
|
972
|
+
|
|
973
|
+
const rowData = [
|
|
974
|
+
{
|
|
975
|
+
status: {
|
|
976
|
+
foregroundColor: 'rgb(255, 255, 255)',
|
|
977
|
+
backgroundColor: 'rgb(0, 0, 255)',
|
|
978
|
+
},
|
|
979
|
+
},
|
|
980
|
+
];
|
|
981
|
+
|
|
982
|
+
const { container } = render(
|
|
983
|
+
<Table
|
|
984
|
+
columnDefs={columnDefs}
|
|
985
|
+
rowData={rowData}
|
|
986
|
+
headerContent={<TableSettingsDropdown />}
|
|
987
|
+
/>,
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
991
|
+
|
|
992
|
+
// Check that both colours are applied by default
|
|
993
|
+
await waitFor(() => {
|
|
994
|
+
const cells = container.querySelectorAll('.ag-cell');
|
|
995
|
+
const statusCell = Array.from(cells).find(cell =>
|
|
996
|
+
cell.getAttribute('col-id') === 'status',
|
|
997
|
+
) as HTMLElement;
|
|
998
|
+
expect(statusCell).toBeDefined();
|
|
999
|
+
expect(statusCell.style.color).toBe('rgb(255, 255, 255)');
|
|
1000
|
+
expect(statusCell.style.backgroundColor).toBe('rgb(0, 0, 255)');
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test('re-applies colours when cell colours is re-enabled', async () => {
|
|
1005
|
+
const columnDefs = [
|
|
1006
|
+
{
|
|
1007
|
+
field: 'status',
|
|
1008
|
+
headerName: 'Status',
|
|
1009
|
+
},
|
|
1010
|
+
];
|
|
1011
|
+
|
|
1012
|
+
const rowData = [
|
|
1013
|
+
{ status: { backgroundColor: 'rgb(255, 165, 0)' } },
|
|
1014
|
+
];
|
|
1015
|
+
|
|
1016
|
+
const { container } = render(
|
|
1017
|
+
<Table
|
|
1018
|
+
columnDefs={columnDefs}
|
|
1019
|
+
rowData={rowData}
|
|
1020
|
+
headerContent={<TableSettingsDropdown />}
|
|
1021
|
+
/>,
|
|
1022
|
+
);
|
|
1023
|
+
|
|
1024
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1025
|
+
|
|
1026
|
+
// Disable and then re-enable cell colours
|
|
1027
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1028
|
+
const cellColoursCheckbox = screen.getByLabelText('Cell colours');
|
|
1029
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1030
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1031
|
+
|
|
1032
|
+
// Check that background colour is re-applied
|
|
1033
|
+
await waitFor(() => {
|
|
1034
|
+
const cells = container.querySelectorAll('.ag-cell');
|
|
1035
|
+
const statusCell = Array.from(cells).find(cell =>
|
|
1036
|
+
cell.getAttribute('col-id') === 'status',
|
|
1037
|
+
) as HTMLElement;
|
|
1038
|
+
expect(statusCell).toBeDefined();
|
|
1039
|
+
expect(statusCell.style.backgroundColor).toBe('rgb(255, 165, 0)');
|
|
1040
|
+
});
|
|
1041
|
+
});
|
|
1042
|
+
|
|
1043
|
+
test('cell colours are safe for cells with nullish/empty values', async () => {
|
|
1044
|
+
const columnDefs = [
|
|
1045
|
+
{
|
|
1046
|
+
field: 'status',
|
|
1047
|
+
headerName: 'Status',
|
|
1048
|
+
},
|
|
1049
|
+
];
|
|
1050
|
+
|
|
1051
|
+
const rowData = [
|
|
1052
|
+
{ },
|
|
1053
|
+
];
|
|
1054
|
+
await expect(async () => {
|
|
1055
|
+
render(
|
|
1056
|
+
<Table
|
|
1057
|
+
headerContent={<TableSettingsDropdown />}
|
|
1058
|
+
columnDefs={columnDefs}
|
|
1059
|
+
rowData={rowData}
|
|
1060
|
+
/>,
|
|
1061
|
+
);
|
|
1062
|
+
}).not.toThrow();
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
test('onCellColorsChanged gets called when cell colours toggle', async () => {
|
|
1066
|
+
const onCellColorsChanged = vi.fn();
|
|
1067
|
+
|
|
1068
|
+
render(
|
|
1069
|
+
<Table
|
|
1070
|
+
headerContent={<TableSettingsDropdown />}
|
|
1071
|
+
onCellColorsChanged={onCellColorsChanged}
|
|
1072
|
+
/>,
|
|
1073
|
+
);
|
|
1074
|
+
|
|
1075
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1076
|
+
|
|
1077
|
+
const cellColoursCheckbox = screen.getByLabelText('Cell colours');
|
|
1078
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1079
|
+
|
|
1080
|
+
await waitFor(() => {
|
|
1081
|
+
expect(onCellColorsChanged).toHaveBeenCalledExactlyOnceWith(false);
|
|
1082
|
+
});
|
|
1083
|
+
|
|
1084
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1085
|
+
|
|
1086
|
+
await waitFor(() => {
|
|
1087
|
+
expect(onCellColorsChanged).toHaveBeenCalledTimes(2);
|
|
1088
|
+
expect(onCellColorsChanged).toHaveBeenNthCalledWith(2, true);
|
|
1089
|
+
});
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
test('onCellColorsChanged is called when reset button is clicked', async () => {
|
|
1093
|
+
const onCellColorsChanged = vi.fn();
|
|
1094
|
+
|
|
1095
|
+
render(
|
|
1096
|
+
<Table
|
|
1097
|
+
headerContent={<TableSettingsDropdown />}
|
|
1098
|
+
onCellColorsChanged={onCellColorsChanged}
|
|
1099
|
+
/>,
|
|
1100
|
+
);
|
|
1101
|
+
|
|
1102
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1103
|
+
|
|
1104
|
+
const cellColoursCheckbox = screen.getByLabelText('Cell colours');
|
|
1105
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1106
|
+
|
|
1107
|
+
await waitFor(() => {
|
|
1108
|
+
expect(onCellColorsChanged).toHaveBeenCalledWith(false);
|
|
1109
|
+
});
|
|
1110
|
+
|
|
1111
|
+
const resetButton = screen.getByRole('button', { name: /Reset Table Settings/i });
|
|
1112
|
+
await userEvent.click(resetButton);
|
|
1113
|
+
|
|
1114
|
+
await waitFor(() => {
|
|
1115
|
+
expect(onCellColorsChanged).toHaveBeenCalledWith(true);
|
|
1116
|
+
});
|
|
1117
|
+
});
|
|
1118
|
+
|
|
1119
|
+
test('reset button resets cell colours to default (on)', async () => {
|
|
1120
|
+
render(
|
|
1121
|
+
<Table
|
|
1122
|
+
headerContent={<TableSettingsDropdown />}
|
|
1123
|
+
/>,
|
|
1124
|
+
);
|
|
1125
|
+
|
|
1126
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1127
|
+
|
|
1128
|
+
// Turn off cell colours
|
|
1129
|
+
const cellColoursCheckbox = screen.getByLabelText('Cell colours');
|
|
1130
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1131
|
+
|
|
1132
|
+
await waitFor(() => {
|
|
1133
|
+
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(false);
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
// Click reset button
|
|
1137
|
+
const resetButton = screen.getByRole('button', { name: /Reset Table Settings/i });
|
|
1138
|
+
await userEvent.click(resetButton);
|
|
1139
|
+
|
|
1140
|
+
await waitFor(() => {
|
|
1141
|
+
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(true);
|
|
1142
|
+
});
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1145
|
+
test('cell colours setting persists when dropdown is closed and reopened', async () => {
|
|
1146
|
+
render(
|
|
1147
|
+
<Table
|
|
1148
|
+
headerContent={<TableSettingsDropdown />}
|
|
1149
|
+
/>,
|
|
1150
|
+
);
|
|
1151
|
+
|
|
1152
|
+
// Open dropdown and toggle cell colours
|
|
1153
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1154
|
+
|
|
1155
|
+
const cellColoursCheckbox = screen.getByLabelText('Cell colours');
|
|
1156
|
+
await userEvent.click(cellColoursCheckbox);
|
|
1157
|
+
|
|
1158
|
+
await waitFor(() => {
|
|
1159
|
+
expect((cellColoursCheckbox as HTMLInputElement).checked).toBe(false);
|
|
1160
|
+
});
|
|
1161
|
+
|
|
1162
|
+
// Close dropdown
|
|
1163
|
+
await userEvent.keyboard('{Escape}');
|
|
1164
|
+
|
|
1165
|
+
// Reopen dropdown
|
|
1166
|
+
await userEvent.click(screen.getByRole('button', { name: /Table settings/i }));
|
|
1167
|
+
|
|
1168
|
+
// Check setting persisted
|
|
1169
|
+
await waitFor(() => {
|
|
1170
|
+
expect((screen.getByLabelText('Cell colours') as HTMLInputElement).checked).toBe(false);
|
|
1171
|
+
});
|
|
1172
|
+
});
|
|
963
1173
|
});
|
|
964
1174
|
|
|
965
1175
|
describe('ButtonCellRenderer', () => {
|
|
@@ -1025,4 +1235,330 @@ describe('Table', () => {
|
|
|
1025
1235
|
expect(handleClick2).toHaveBeenCalledTimes(1);
|
|
1026
1236
|
});
|
|
1027
1237
|
});
|
|
1238
|
+
|
|
1239
|
+
describe('SelectDropdownCellRenderer', () => {
|
|
1240
|
+
const options = [{ label: 'Option 1', value: 'option1' }, { label: 'Option 2', value: 'option2' }];
|
|
1241
|
+
const columnDefs = [{
|
|
1242
|
+
field: 'selectField',
|
|
1243
|
+
headerName: 'Select Field',
|
|
1244
|
+
cellRenderer: 'dsSelectDropdownCellRenderer',
|
|
1245
|
+
cellRendererParams: { options: options, placeholder: 'Placeholder Text' },
|
|
1246
|
+
}];
|
|
1247
|
+
|
|
1248
|
+
test('renders with placeholder', () => {
|
|
1249
|
+
const rowData = [{ selectField: null }];
|
|
1250
|
+
render(
|
|
1251
|
+
<Table
|
|
1252
|
+
columnDefs={columnDefs}
|
|
1253
|
+
rowData={rowData}
|
|
1254
|
+
/>,
|
|
1255
|
+
);
|
|
1256
|
+
expect(screen.getByText('Placeholder Text')).toBeInTheDocument();
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
test('renders with option', () => {
|
|
1260
|
+
const rowData = [{ selectField: 'option1' }];
|
|
1261
|
+
render(
|
|
1262
|
+
<Table
|
|
1263
|
+
columnDefs={columnDefs}
|
|
1264
|
+
rowData={rowData}
|
|
1265
|
+
/>,
|
|
1266
|
+
);
|
|
1267
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
test('renders with value not in options as raw string', () => {
|
|
1271
|
+
const rowData = [{ selectField: 'unknown-value' }];
|
|
1272
|
+
render(
|
|
1273
|
+
<Table
|
|
1274
|
+
columnDefs={columnDefs}
|
|
1275
|
+
rowData={rowData}
|
|
1276
|
+
/>,
|
|
1277
|
+
);
|
|
1278
|
+
expect(screen.getByText('unknown-value')).toBeInTheDocument();
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
test('renders with empty string shows placeholder', () => {
|
|
1282
|
+
const rowData = [{ selectField: '' }];
|
|
1283
|
+
render(
|
|
1284
|
+
<Table
|
|
1285
|
+
columnDefs={columnDefs}
|
|
1286
|
+
rowData={rowData}
|
|
1287
|
+
/>,
|
|
1288
|
+
);
|
|
1289
|
+
expect(screen.getByText('Placeholder Text')).toBeInTheDocument();
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
test('renders dropdown button variant in cell', () => {
|
|
1293
|
+
const rowData = [{ selectField: 'option1' }];
|
|
1294
|
+
const { container } = render(
|
|
1295
|
+
<Table
|
|
1296
|
+
columnDefs={columnDefs}
|
|
1297
|
+
rowData={rowData}
|
|
1298
|
+
/>,
|
|
1299
|
+
);
|
|
1300
|
+
const dropdownButton = container.querySelector('.ds-table__select-dropdown-cell.ds-button--dropdown');
|
|
1301
|
+
expect(dropdownButton).toBeInTheDocument();
|
|
1302
|
+
});
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
describe('SelectDropdownCellEditor', () => {
|
|
1306
|
+
const options = [
|
|
1307
|
+
{ label: 'Option 1', value: 'option1' },
|
|
1308
|
+
{ label: 'Option 2', value: 'option2' },
|
|
1309
|
+
{ label: 'Option 3', value: 'option3' },
|
|
1310
|
+
];
|
|
1311
|
+
const editableSelectColumnDefs = [{
|
|
1312
|
+
field: 'selectField',
|
|
1313
|
+
headerName: 'Select Field',
|
|
1314
|
+
cellRenderer: 'dsSelectDropdownCellRenderer',
|
|
1315
|
+
cellEditor: 'dsSelectDropdownCellEditor',
|
|
1316
|
+
cellRendererParams: { options, placeholder: 'Placeholder Text' },
|
|
1317
|
+
cellEditorParams: { options, placeholder: 'Placeholder Text' },
|
|
1318
|
+
editable: true,
|
|
1319
|
+
}];
|
|
1320
|
+
|
|
1321
|
+
async function renderAndStartEditing(rowData: { selectField: string }[]) {
|
|
1322
|
+
let gridApi: GridApi | null = null;
|
|
1323
|
+
render(
|
|
1324
|
+
<Table
|
|
1325
|
+
columnDefs={editableSelectColumnDefs}
|
|
1326
|
+
rowData={rowData}
|
|
1327
|
+
onGridReady={(e) => {
|
|
1328
|
+
gridApi = e.api;
|
|
1329
|
+
}}
|
|
1330
|
+
/>,
|
|
1331
|
+
);
|
|
1332
|
+
await waitFor(() => expect(gridApi).toBeTruthy());
|
|
1333
|
+
await act(async () => {
|
|
1334
|
+
gridApi!.startEditingCell({ rowIndex: 0, colKey: 'selectField' });
|
|
1335
|
+
});
|
|
1336
|
+
const editorWrapper = await waitFor(() => document.querySelector('.ds-table__select-dropdown-editor'));
|
|
1337
|
+
return editorWrapper!.querySelector('button')!;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
test('opens editor with dropdown; selecting option updates cell', async () => {
|
|
1341
|
+
const trigger = await renderAndStartEditing([{ selectField: 'option1' }]);
|
|
1342
|
+
expect(trigger).toHaveTextContent('Option 1');
|
|
1343
|
+
await userEvent.click(trigger);
|
|
1344
|
+
await userEvent.click(screen.getByText('Option 2'));
|
|
1345
|
+
await waitFor(() => expect(screen.getByText('Option 2')).toBeInTheDocument());
|
|
1346
|
+
});
|
|
1347
|
+
|
|
1348
|
+
test('Escape closes dropdown without changing value', async () => {
|
|
1349
|
+
const trigger = await renderAndStartEditing([{ selectField: 'option1' }]);
|
|
1350
|
+
await userEvent.click(trigger);
|
|
1351
|
+
await waitFor(() => expect(screen.getByText('Option 2')).toBeInTheDocument());
|
|
1352
|
+
await userEvent.keyboard('{Escape}');
|
|
1353
|
+
expect(screen.queryByText('Option 2')).not.toBeInTheDocument();
|
|
1354
|
+
expect(screen.getByText('Option 1')).toBeInTheDocument();
|
|
1355
|
+
});
|
|
1356
|
+
});
|
|
1357
|
+
|
|
1358
|
+
describe('Cell Editing', () => {
|
|
1359
|
+
describe('with literal data values', () => {
|
|
1360
|
+
test('supports editing text fields', async () => {
|
|
1361
|
+
const onCellValueChanged = vi.fn();
|
|
1362
|
+
const columnDefs = [{
|
|
1363
|
+
field: 'name',
|
|
1364
|
+
headerName: 'Name',
|
|
1365
|
+
editable: true,
|
|
1366
|
+
}];
|
|
1367
|
+
const rowData = [
|
|
1368
|
+
{ id: 1, name: 'John Doe' },
|
|
1369
|
+
{ id: 2, name: 'Jane Smith' },
|
|
1370
|
+
];
|
|
1371
|
+
|
|
1372
|
+
render(
|
|
1373
|
+
<Table
|
|
1374
|
+
columnDefs={columnDefs}
|
|
1375
|
+
rowData={rowData}
|
|
1376
|
+
onCellValueChanged={onCellValueChanged}
|
|
1377
|
+
/>,
|
|
1378
|
+
);
|
|
1379
|
+
|
|
1380
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1381
|
+
await waitFor(() => expect(screen.getByText('John Doe')).toBeInTheDocument());
|
|
1382
|
+
|
|
1383
|
+
const cell = screen.getByText('John Doe');
|
|
1384
|
+
await userEvent.dblClick(cell);
|
|
1385
|
+
|
|
1386
|
+
await userEvent.keyboard('John Updated{Enter}');
|
|
1387
|
+
|
|
1388
|
+
await waitFor(() => {
|
|
1389
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ newValue: 'John Updated', oldValue: 'John Doe' }));
|
|
1393
|
+
});
|
|
1394
|
+
|
|
1395
|
+
test('supports editing number fields', async () => {
|
|
1396
|
+
const onCellValueChanged = vi.fn();
|
|
1397
|
+
const columnDefs = [{
|
|
1398
|
+
field: 'age',
|
|
1399
|
+
headerName: 'Age',
|
|
1400
|
+
editable: true,
|
|
1401
|
+
cellDataType: 'number',
|
|
1402
|
+
}];
|
|
1403
|
+
const rowData = [
|
|
1404
|
+
{ id: 1, name: 'John Doe', age: 30 },
|
|
1405
|
+
{ id: 2, name: 'Jane Smith', age: 25 },
|
|
1406
|
+
];
|
|
1407
|
+
|
|
1408
|
+
render(
|
|
1409
|
+
<Table
|
|
1410
|
+
columnDefs={columnDefs}
|
|
1411
|
+
rowData={rowData}
|
|
1412
|
+
onCellValueChanged={onCellValueChanged}
|
|
1413
|
+
/>,
|
|
1414
|
+
);
|
|
1415
|
+
|
|
1416
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1417
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1418
|
+
|
|
1419
|
+
const cell = screen.getByText('30');
|
|
1420
|
+
await userEvent.dblClick(cell);
|
|
1421
|
+
|
|
1422
|
+
await userEvent.keyboard('35{Enter}');
|
|
1423
|
+
|
|
1424
|
+
await waitFor(() => {
|
|
1425
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ oldValue: 30, newValue: 35 }));
|
|
1429
|
+
});
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
/**
|
|
1433
|
+
* Note: when editing object values, the table will just insert the new value and
|
|
1434
|
+
* override the object. It will be the consumer's responsibility within the data layer
|
|
1435
|
+
* to do any processing on cell updates to maintain or alter other fields on the data
|
|
1436
|
+
* as necessary (e.g. cell colours).
|
|
1437
|
+
*/
|
|
1438
|
+
describe('with object data values (value field)', () => {
|
|
1439
|
+
test('supports editing text fields with object values', async () => {
|
|
1440
|
+
const onCellValueChanged = vi.fn();
|
|
1441
|
+
const columnDefs = [{
|
|
1442
|
+
field: 'name',
|
|
1443
|
+
headerName: 'Name',
|
|
1444
|
+
editable: true,
|
|
1445
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1446
|
+
}];
|
|
1447
|
+
const rowData = [
|
|
1448
|
+
{ id: 1, name: { value: 'John Doe' } },
|
|
1449
|
+
{ id: 2, name: { value: 'Jane Smith' } },
|
|
1450
|
+
];
|
|
1451
|
+
|
|
1452
|
+
render(
|
|
1453
|
+
<Table
|
|
1454
|
+
columnDefs={columnDefs}
|
|
1455
|
+
rowData={rowData}
|
|
1456
|
+
onCellValueChanged={onCellValueChanged}
|
|
1457
|
+
defaultColDef={{
|
|
1458
|
+
cellEditorParams: {
|
|
1459
|
+
useFormatter: true,
|
|
1460
|
+
},
|
|
1461
|
+
}}
|
|
1462
|
+
/>,
|
|
1463
|
+
);
|
|
1464
|
+
|
|
1465
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1466
|
+
await waitFor(() => expect(screen.getByText('John Doe')).toBeInTheDocument());
|
|
1467
|
+
|
|
1468
|
+
const cell = screen.getByText('John Doe');
|
|
1469
|
+
await userEvent.dblClick(cell);
|
|
1470
|
+
|
|
1471
|
+
await userEvent.keyboard('John Updated{Enter}');
|
|
1472
|
+
|
|
1473
|
+
await waitFor(() => {
|
|
1474
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ newValue: 'John Updated', oldValue: { value: 'John Doe' } }));
|
|
1478
|
+
});
|
|
1479
|
+
|
|
1480
|
+
test('supports editing number fields with object values', async () => {
|
|
1481
|
+
const onCellValueChanged = vi.fn();
|
|
1482
|
+
const columnDefs = [{
|
|
1483
|
+
field: 'age',
|
|
1484
|
+
headerName: 'Age',
|
|
1485
|
+
editable: true,
|
|
1486
|
+
cellDataType: 'number',
|
|
1487
|
+
valueFormatter: Table.DefaultValueFormatter,
|
|
1488
|
+
}];
|
|
1489
|
+
const rowData = [
|
|
1490
|
+
{ id: 1, name: { value: 'John Doe' }, age: { value: 30 } },
|
|
1491
|
+
{ id: 2, name: { value: 'Jane Smith' }, age: { value: 25 } },
|
|
1492
|
+
];
|
|
1493
|
+
|
|
1494
|
+
render(
|
|
1495
|
+
<Table
|
|
1496
|
+
columnDefs={columnDefs}
|
|
1497
|
+
rowData={rowData}
|
|
1498
|
+
onCellValueChanged={onCellValueChanged}
|
|
1499
|
+
defaultColDef={{
|
|
1500
|
+
cellEditorParams: {
|
|
1501
|
+
useFormatter: true,
|
|
1502
|
+
},
|
|
1503
|
+
}}
|
|
1504
|
+
/>,
|
|
1505
|
+
);
|
|
1506
|
+
|
|
1507
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1508
|
+
await waitFor(() => expect(screen.getByText('30')).toBeInTheDocument());
|
|
1509
|
+
|
|
1510
|
+
const cells = screen.getByText('30');
|
|
1511
|
+
await userEvent.dblClick(cells);
|
|
1512
|
+
|
|
1513
|
+
await userEvent.keyboard('35{Enter}');
|
|
1514
|
+
|
|
1515
|
+
await waitFor(() => {
|
|
1516
|
+
expect(onCellValueChanged).toHaveBeenCalled();
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
expect(onCellValueChanged).toHaveBeenLastCalledWith(expect.objectContaining({ newValue: 35, oldValue: { value: 30 } }));
|
|
1520
|
+
});
|
|
1521
|
+
});
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
describe('supressCellFocusAndFocusFirstElement', () => {
|
|
1525
|
+
const handleClick = vi.fn();
|
|
1526
|
+
const columnDefs = [{
|
|
1527
|
+
field: 'action',
|
|
1528
|
+
headerName: 'Action',
|
|
1529
|
+
cellRenderer: 'dsButtonCellRenderer',
|
|
1530
|
+
cellRendererParams: {
|
|
1531
|
+
supressCellFocusAndFocusFirstElement: true,
|
|
1532
|
+
},
|
|
1533
|
+
}];
|
|
1534
|
+
const rowData = [{
|
|
1535
|
+
action: {
|
|
1536
|
+
children: 'Im a lovely button',
|
|
1537
|
+
onClick: handleClick,
|
|
1538
|
+
},
|
|
1539
|
+
}];
|
|
1540
|
+
|
|
1541
|
+
test('setting supressCellFocusAndFocusFirstElement to true should sadd the ds-table__cell--supress-focus class', async () => {
|
|
1542
|
+
render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1543
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1544
|
+
});
|
|
1545
|
+
|
|
1546
|
+
test('setting supressCellFocusAndFocusFirstElement to true should suppress cell focus and focus the first element', async () => {
|
|
1547
|
+
const focusFirstFocusableElementSpy = vi.spyOn(
|
|
1548
|
+
focusFirstFocusableElementModule,
|
|
1549
|
+
'focusFirstFocusableElement',
|
|
1550
|
+
);
|
|
1551
|
+
render(<Table columnDefs={columnDefs} rowData={rowData} />);
|
|
1552
|
+
await waitFor(() => expect(screen.getByRole('grid')).toBeInTheDocument());
|
|
1553
|
+
await waitFor(() => expect(screen.getByText('Im a lovely button')).toBeInTheDocument());
|
|
1554
|
+
// first tab should focus the table heading
|
|
1555
|
+
await userEvent.tab();
|
|
1556
|
+
expect(screen.getByRole('columnheader')).toHaveFocus();
|
|
1557
|
+
// second tab should focus the button bypassing the cell
|
|
1558
|
+
await userEvent.tab();
|
|
1559
|
+
expect(focusFirstFocusableElementSpy).toHaveBeenCalledTimes(1);
|
|
1560
|
+
const button = screen.getByText('Im a lovely button');
|
|
1561
|
+
expect(button).toHaveFocus();
|
|
1562
|
+
});
|
|
1563
|
+
});
|
|
1028
1564
|
});
|