Skip to content

Commit ccad898

Browse files
committed
chore: Add doc-string, make enableColumnFiltering toggerable
1 parent 44e64a9 commit ccad898

File tree

3 files changed

+113
-43
lines changed

3 files changed

+113
-43
lines changed

pages/table/grouped-columns-feat-combination.page.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,7 @@ type DemoContext = React.Context<
709709
empty: boolean;
710710
cellVerticalAlign: string;
711711
sortingDisabled: boolean;
712+
enableColumnFiltering: boolean;
712713
}>
713714
>;
714715

@@ -736,6 +737,7 @@ export default function GroupedColumnsBugBash() {
736737
empty = false,
737738
cellVerticalAlign = 'middle',
738739
sortingDisabled = false,
740+
enableColumnFiltering = true,
739741
},
740742
setUrlParams,
741743
} = useContext(AppContext as DemoContext);
@@ -941,6 +943,12 @@ export default function GroupedColumnsBugBash() {
941943
>
942944
RTL
943945
</Toggle>
946+
<Toggle
947+
checked={enableColumnFiltering}
948+
onChange={({ detail }) => setUrlParams({ enableColumnFiltering: detail.checked })}
949+
>
950+
Column filtering
951+
</Toggle>
944952
</SpaceBetween>
945953
</SpaceBetween>
946954

@@ -1011,6 +1019,10 @@ export default function GroupedColumnsBugBash() {
10111019
preferences={
10121020
<CollectionPreferences
10131021
{...collectionPreferencesProps}
1022+
contentDisplayPreference={{
1023+
...collectionPreferencesProps.contentDisplayPreference!,
1024+
enableColumnFiltering,
1025+
}}
10141026
preferences={preferences}
10151027
onConfirm={({ detail }) => setPreferences(detail)}
10161028
/>

src/test-utils/dom/collection-preferences/content-display-preference.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,15 @@ export class ContentDisplayOptionWrapper extends ComponentWrapper {
4545
*
4646
* The children are the leaf-level `ContentDisplayOptionWrapper`s inside the group's
4747
* nested `InternalList` — i.e. they already carry a drag handle and visibility toggle.
48+
*
49+
* @param option.group When `true`, returns only group items. When `false`, returns only leaf column items.
50+
* When omitted, returns all child items regardless of type.
4851
*/
49-
findChildrenOptions(): Array<ContentDisplayOptionWrapper> | null {
52+
findChildrenOptions(
53+
option: {
54+
group?: boolean;
55+
} = {}
56+
): Array<ContentDisplayOptionWrapper> | null {
5057
// Group items wrap their content in <div data-item-type="group">.
5158
// If that wrapper is absent this is a leaf column.
5259
const groupWrapper = this.getListItem().findContent().find('[data-item-type="group"]');
@@ -58,9 +65,20 @@ export class ContentDisplayOptionWrapper extends ComponentWrapper {
5865
if (!nestedList) {
5966
return null;
6067
}
61-
return new ListWrapper(nestedList.getElement())
62-
.findItems()
63-
.map(item => new ContentDisplayOptionWrapper(item.getElement()));
68+
const list = new ListWrapper(nestedList.getElement());
69+
70+
if (option.group === true) {
71+
return list
72+
.findAll(`li:has([data-item-type="group"])`)
73+
.map(item => new ContentDisplayOptionWrapper(item.getElement()));
74+
}
75+
if (option.group === false) {
76+
return list
77+
.findAll(`li:has([data-item-type="column"])`)
78+
.map(item => new ContentDisplayOptionWrapper(item.getElement()));
79+
}
80+
81+
return list.findItems().map(item => new ContentDisplayOptionWrapper(item.getElement()));
6482
}
6583
}
6684

src/test-utils/dom/table/index.ts

Lines changed: 79 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ export default class TableWrapper extends ComponentWrapper {
3030
return this.findByClassName(styles['thead-active'])!;
3131
}
3232

33+
/**
34+
* Returns the header slot of the table.
35+
*/
3336
findHeaderSlot(): ElementWrapper | null {
3437
return this.findByClassName(styles['header-controls']);
3538
}
@@ -42,70 +45,68 @@ export default class TableWrapper extends ComponentWrapper {
4245
return this.findHeaderSlot();
4346
}
4447

48+
/**
49+
* Returns the footer slot of the table.
50+
*/
4551
findFooterSlot(): ElementWrapper | null {
4652
return this.containerWrapper.findFooter();
4753
}
4854

4955
/**
50-
* Returns all column header cells in the last header row (leaf columns).
51-
* For tables without column grouping this is equivalent to querying `tr > *`.
56+
* Returns column header cells from the table's header region.
57+
*
58+
* By default, returns all cells in the last header row (leaf columns).
59+
* For tables without column grouping this is equivalent to querying all `<th>` elements.
5260
* For tables with column grouping this returns only the leaf-level column headers,
5361
* not the group header cells above them.
5462
*
55-
* Pass `{ level }` to target a specific header row (1-based). Level 1 is the
56-
* topmost row (group headers); the last level is always the leaf-column row.
57-
*
58-
* Pass `{ groupId }` to return only the leaf column headers that are direct
59-
* children of the specified group. This uses the `data-column-group-id` attribute
60-
* set on each leaf `<th>` by the renderer.
63+
* Both `level` and `groupId` can be combined to narrow the result set.
6164
*
62-
* @param option.level 1-based index of the header row to query. Defaults to the last row.
65+
* @param option.level 1-based index of the header row to query. Level 1 is the topmost row
66+
* (group headers); the last level is always the leaf-column row. Defaults to the last row.
6367
* @param option.groupId ID of the parent group whose direct child columns to return.
68+
* Uses the `data-column-group-id` attribute set on each leaf `<th>` by the renderer.
6469
*/
6570
findColumnHeaders(
6671
option: {
6772
groupId?: string;
6873
level?: number;
6974
} = {}
7075
): Array<ElementWrapper> {
71-
if (option.groupId !== undefined) {
72-
return this.findActiveTHead().findAll(`tr:last-child > th[data-column-group-id="${option.groupId}"]`);
76+
const { groupId, level } = option;
77+
const parts: string[] = [];
78+
79+
// Row selector
80+
if (level !== undefined) {
81+
parts.push(`tr:nth-child(${level}) > `);
82+
} else if (groupId === undefined) {
83+
parts.push('tr:last-child > ');
7384
}
74-
if (option.level !== undefined) {
75-
return this.findActiveTHead().findAll(`tr:nth-child(${option.level}) > *`);
85+
86+
// Element selector
87+
if (groupId !== undefined) {
88+
parts.push(`th[data-column-group-id="${groupId}"]`);
89+
} else {
90+
parts.push('*');
7691
}
77-
return this.findActiveTHead().findAll('tr:last-child > *');
92+
93+
return this.findActiveTHead().findAll(parts.join(''));
7894
}
7995

8096
/**
81-
* Returns the element the user clicks when resizing a column or group header.
82-
* Targets the leaf-column header row by default.
97+
* Returns the element the user clicks when resizing a column.
8398
*
84-
* Pass `{ level }` to target a specific header row (1-based).
99+
* Targets leaf-column headers (`scope="col"`), which reliably identifies
100+
* individual columns regardless of which `<tr>` they appear in. In grouped
101+
* tables, leaf columns may span multiple rows and sit in an earlier `<tr>`
102+
* rather than the last one.
85103
*
86-
* Pass `{ groupId }` to return the resizer of the group header cell with that ID.
87-
* When `groupId` is provided, `columnIndex` is ignored.
88-
*
89-
* @param columnIndex 1-based index of the column containing the resizer (ignored when groupId is set).
90-
* @param option.level 1-based index of the header row to query. Defaults to the last row.
91-
* @param option.groupId ID of the group header whose resizer to return.
104+
* @param columnIndex 1-based index of the leaf column containing the resizer.
92105
*/
93-
findColumnResizer(
94-
columnIndex: number,
95-
option: {
96-
groupId?: string;
97-
level?: number;
98-
} = {}
99-
): ElementWrapper | null {
100-
if (option.groupId !== undefined) {
101-
// Use a CSS :has() selector to locate the colgroup <th> containing the group focus marker,
102-
// then find the resizer inside it.
103-
return this.findActiveTHead().find(
104-
`th[scope="colgroup"]:has([data-focus-id="group-header-${option.groupId}"]) .${resizerStyles.resizer}`
105-
);
106-
}
107-
const rowSelector = option.level !== undefined ? `tr:nth-child(${option.level})` : 'tr:last-child';
108-
return this.findActiveTHead().find(`${rowSelector} th:nth-child(${columnIndex}) .${resizerStyles.resizer}`);
106+
findColumnResizer(columnIndex: number): ElementWrapper | null {
107+
const leafHeaders = this.findActiveTHead().findAll('th[scope="col"]');
108+
const header = leafHeaders[columnIndex - 1];
109+
return header?.find(`.${resizerStyles.resizer}`) ?? null;
109110
}
110111

111112
/**
@@ -130,10 +131,16 @@ export default class TableWrapper extends ComponentWrapper {
130131
return this.findBodyCell(rowIndex, columnIndex)?.find(`.${testUtilStyles['body-cell-counter']}`) ?? null;
131132
}
132133

134+
/**
135+
* Returns all table rows within the table body.
136+
*/
133137
findRows(): Array<ElementWrapper> {
134138
return this.findNativeTable().findAllByClassName(styles.row);
135139
}
136140

141+
/**
142+
* Returns all selected table rows.
143+
*/
137144
findSelectedRows(): Array<ElementWrapper> {
138145
return this.findAllByClassName(styles['row-selected']);
139146
}
@@ -146,10 +153,16 @@ export default class TableWrapper extends ComponentWrapper {
146153
return this.findEmptySlot();
147154
}
148155

156+
/**
157+
* Returns the empty state slot of the table.
158+
*/
149159
findEmptySlot(): ElementWrapper | null {
150160
return this.findByClassName(styles.empty);
151161
}
152162

163+
/**
164+
* Returns the loading text element of the table.
165+
*/
153166
findLoadingText(): ElementWrapper | null {
154167
return this.findByClassName(styles.loading);
155168
}
@@ -187,26 +200,44 @@ export default class TableWrapper extends ComponentWrapper {
187200
return this.findNativeTable().find(`tbody tr:nth-child(${rowIndex}) .${selectionStyles.root}`);
188201
}
189202

203+
/**
204+
* Returns the "select all" checkbox or radio button in the table header.
205+
*/
190206
findSelectAllTrigger(): ElementWrapper | null {
191207
return this.findActiveTHead().find(`.${selectionStyles.root}`);
192208
}
193209

210+
/**
211+
* Returns the text filter component used in the table.
212+
*/
194213
findTextFilter(): TextFilterWrapper | null {
195214
return this.findComponent(`.${styles['tools-filtering']}`, TextFilterWrapper);
196215
}
197216

217+
/**
218+
* Returns the property filter component used in the table.
219+
*/
198220
findPropertyFilter(): PropertyFilterWrapper | null {
199221
return this.findComponent(`.${styles['tools-filtering']}`, PropertyFilterWrapper);
200222
}
201223

224+
/**
225+
* Returns the filter slot of the table.
226+
*/
202227
findFilterSlot(): ElementWrapper | null {
203228
return this.findComponent(`.${styles['tools-filtering']}`, ElementWrapper);
204229
}
205230

231+
/**
232+
* Returns the collection preferences component used in the table.
233+
*/
206234
findCollectionPreferences(): CollectionPreferencesWrapper | null {
207235
return this.findComponent(`.${styles['tools-preferences']}`, CollectionPreferencesWrapper);
208236
}
209237

238+
/**
239+
* Returns the pagination component used in the table.
240+
*/
210241
findPagination(): PaginationWrapper | null {
211242
return this.findComponent(`.${styles['tools-pagination']}`, PaginationWrapper);
212243
}
@@ -221,6 +252,9 @@ export default class TableWrapper extends ComponentWrapper {
221252
return this.findBodyCell(rowIndex, columnIndex)?.findByClassName(bodyCellStyles['body-cell-editor']) ?? null;
222253
}
223254

255+
/**
256+
* Returns the currently active inline editing cell.
257+
*/
224258
findEditingCell(): ElementWrapper | null {
225259
return this.findNativeTable().findByClassName(bodyCellStyles['body-cell-edit-active']);
226260
}
@@ -229,10 +263,16 @@ export default class TableWrapper extends ComponentWrapper {
229263
return this.findEditingCell()?.findByClassName(bodyCellStyles['body-cell-editor-controls']) ?? null;
230264
}
231265

266+
/**
267+
* Returns the save button of the currently active inline editing cell.
268+
*/
232269
findEditingCellSaveButton(): ElementWrapper | null {
233270
return this._findEditingCellControls()?.find('button[type="submit"]') ?? null;
234271
}
235272

273+
/**
274+
* Returns the cancel button of the currently active inline editing cell.
275+
*/
236276
findEditingCellCancelButton(): ElementWrapper | null {
237277
return this._findEditingCellControls()?.find('button:first-child') ?? null;
238278
}

0 commit comments

Comments
 (0)