@purpurds/table 6.12.5 → 7.0.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.
Files changed (48) hide show
  1. package/dist/LICENSE.txt +16 -16
  2. package/dist/cell-types/body-text-cell.d.ts.map +1 -1
  3. package/dist/cell-types/link-cell.d.ts.map +1 -1
  4. package/dist/story-utils/column-def.d.ts.map +1 -1
  5. package/dist/story-utils/table-data.d.ts +2 -4
  6. package/dist/story-utils/table-data.d.ts.map +1 -1
  7. package/dist/styles.css +1 -1
  8. package/dist/table-action-bar.d.ts.map +1 -1
  9. package/dist/table-export-drawer.d.ts +3 -2
  10. package/dist/table-export-drawer.d.ts.map +1 -1
  11. package/dist/table-row-cell.d.ts.map +1 -1
  12. package/dist/table-settings-drawer.d.ts +2 -1
  13. package/dist/table-settings-drawer.d.ts.map +1 -1
  14. package/dist/table-toolbar.d.ts +6 -4
  15. package/dist/table-toolbar.d.ts.map +1 -1
  16. package/dist/table.cjs.js +63 -63
  17. package/dist/table.cjs.js.map +1 -1
  18. package/dist/table.d.ts +2 -1
  19. package/dist/table.d.ts.map +1 -1
  20. package/dist/table.es.js +3930 -3932
  21. package/dist/table.es.js.map +1 -1
  22. package/dist/test-utils/helpers.d.ts +2 -1
  23. package/dist/test-utils/helpers.d.ts.map +1 -1
  24. package/dist/types.d.ts +1 -1
  25. package/dist/types.d.ts.map +1 -1
  26. package/dist/use-truncated-hook.d.ts +3 -5
  27. package/dist/use-truncated-hook.d.ts.map +1 -1
  28. package/package.json +23 -23
  29. package/src/cell-types/body-text-cell.tsx +7 -8
  30. package/src/cell-types/link-cell.tsx +13 -6
  31. package/src/story-utils/column-def.ts +2 -0
  32. package/src/story-utils/table-data.tsx +10 -8
  33. package/src/table-action-bar.tsx +3 -2
  34. package/src/table-column-header-cell.tsx +16 -6
  35. package/src/table-export-drawer.test.tsx +1 -0
  36. package/src/table-export-drawer.tsx +5 -3
  37. package/src/table-kitchen-sink.test.tsx +5 -18
  38. package/src/table-row-cell.tsx +1 -30
  39. package/src/table-settings-drawer.test.tsx +4 -0
  40. package/src/table-settings-drawer.tsx +46 -41
  41. package/src/table-toolbar.test.tsx +3 -0
  42. package/src/table-toolbar.tsx +12 -7
  43. package/src/table.module.scss +27 -18
  44. package/src/table.stories.tsx +10 -9
  45. package/src/table.tsx +32 -26
  46. package/src/test-utils/helpers.ts +2 -1
  47. package/src/types.ts +1 -1
  48. package/src/use-truncated-hook.tsx +16 -60
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useId } from "react";
2
2
  import { Button } from "@purpurds/button";
3
3
  import { Drawer } from "@purpurds/drawer";
4
4
  import { Heading } from "@purpurds/heading";
@@ -30,6 +30,7 @@ export type TableSettingsDrawerCopyProps = {
30
30
  };
31
31
 
32
32
  export type TableSettingsDrawerProps<TData> = {
33
+ id: string;
33
34
  getAllColumns: () => Column<TData, unknown>[];
34
35
  onResetSettings: () => void;
35
36
  setDrawerIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
@@ -52,6 +53,7 @@ const cx = c.bind(styles);
52
53
  const rootTestId = "purpur-table-settings-drawer";
53
54
 
54
55
  export const TableSettingsDrawer = <TData extends RowData>({
56
+ id,
55
57
  columnFiltersEnabled,
56
58
  copy,
57
59
  isDrawerOpen,
@@ -69,12 +71,12 @@ export const TableSettingsDrawer = <TData extends RowData>({
69
71
  setRowSelectionEnabled,
70
72
  }: TableSettingsDrawerProps<TData>) => {
71
73
  return (
72
- <div id="purpur-table-settings-drawer">
74
+ <div id={id}>
73
75
  <Drawer data-testid={rootTestId} open={isDrawerOpen} onOpenChange={setDrawerIsOpen}>
74
76
  <Drawer.Content
75
77
  data-testid={`${rootTestId}-content`}
76
78
  zIndex={6}
77
- closeButtonText={copy.buttons.closeDrawer}
79
+ closeButtonAriaLabel={copy.buttons.closeDrawer}
78
80
  title={copy.title}
79
81
  footerContent={
80
82
  <FooterContent text={copy.buttons.resetSettings} onClick={onResetSettings} />
@@ -147,45 +149,48 @@ const GeneralSettings = ({
147
149
  enableRowSelection,
148
150
  rowSelectionEnabled,
149
151
  setRowSelectionEnabled,
150
- }: GeneralSettingsProps) => (
151
- <div
152
- data-testid={`${rootTestId}-general-settings`}
153
- className={cx(`${rootClassName}__general-settings`)}
154
- aria-labelledby="general-settings"
155
- >
156
- <Heading id="general-settings" variant="title-100" tag="h3">
157
- {copy.header}
158
- </Heading>
159
- {columnFiltersEnabled && (
152
+ }: GeneralSettingsProps) => {
153
+ const uid = useId();
154
+ return (
155
+ <div
156
+ data-testid={`${rootTestId}-general-settings`}
157
+ className={cx(`${rootClassName}__general-settings`)}
158
+ aria-labelledby={`${uid}-general-settings`}
159
+ >
160
+ <Heading id={`${uid}-general-settings`} variant="title-100" tag="h3">
161
+ {copy.header}
162
+ </Heading>
163
+ {columnFiltersEnabled && (
164
+ <ToggleWrapper
165
+ id={`${uid}-show-filters`}
166
+ checked={showColumnFilters}
167
+ label={copy.toggles.showFilters}
168
+ onChange={setShowColumnFiltersEnabled}
169
+ />
170
+ )}
160
171
  <ToggleWrapper
161
- id="show-filters"
162
- checked={showColumnFilters}
163
- label={copy.toggles.showFilters}
164
- onChange={setShowColumnFiltersEnabled}
172
+ id={`${uid}-lock-first-column`}
173
+ checked={stickyFirstColumn}
174
+ label={copy.toggles.lockFirstcolumn}
175
+ onChange={setStickyFirstColumn}
165
176
  />
166
- )}
167
- <ToggleWrapper
168
- id="lock-first-column"
169
- checked={stickyFirstColumn}
170
- label={copy.toggles.lockFirstcolumn}
171
- onChange={setStickyFirstColumn}
172
- />
173
- <ToggleWrapper
174
- id="sticky-header"
175
- checked={stickyHeaders}
176
- label={copy.toggles.stickyHeader}
177
- onChange={setStickyHeaders}
178
- />
179
- {enableRowSelection && copy.toggles.rowSelection && setRowSelectionEnabled && (
180
177
  <ToggleWrapper
181
- id="row-selection"
182
- checked={rowSelectionEnabled || false}
183
- label={copy.toggles.rowSelection}
184
- onChange={setRowSelectionEnabled}
178
+ id={`${uid}-sticky-header`}
179
+ checked={stickyHeaders}
180
+ label={copy.toggles.stickyHeader}
181
+ onChange={setStickyHeaders}
185
182
  />
186
- )}
187
- </div>
188
- );
183
+ {enableRowSelection && copy.toggles.rowSelection && setRowSelectionEnabled && (
184
+ <ToggleWrapper
185
+ id={`${uid}-row-selection`}
186
+ checked={rowSelectionEnabled || false}
187
+ label={copy.toggles.rowSelection}
188
+ onChange={setRowSelectionEnabled}
189
+ />
190
+ )}
191
+ </div>
192
+ );
193
+ };
189
194
 
190
195
  const VisibleColumns = <TData extends RowData>({
191
196
  header,
@@ -204,14 +209,14 @@ const VisibleColumns = <TData extends RowData>({
204
209
  const isDisabled = (column: Column<TData, unknown>) => {
205
210
  return !column.getCanHide() || (visible.length === 1 && visible.includes(column));
206
211
  };
207
-
212
+ const uid = useId();
208
213
  return (
209
214
  <section
210
215
  data-testid={`${rootTestId}-visible-columns`}
211
216
  className={cx(`${rootClassName}__visible-columns`)}
212
- aria-labelledby="visible-columns"
217
+ aria-labelledby={`${uid}-visible-columns`}
213
218
  >
214
- <Heading id="visible-columns" variant="title-100" tag="h3">
219
+ <Heading id={`${uid}-visible-columns`} variant="title-100" tag="h3">
215
220
  {header}
216
221
  </Heading>
217
222
  {filteredColumns.map((column) => (
@@ -20,6 +20,9 @@ describe("Data table - Tool bar", () => {
20
20
  // Needs an element with id purpur-table for the ally tests to not fail with "ARIA attributes must conform to valid values (aria-valid-attr-value)" rule
21
21
  <div id="purpur-table">
22
22
  <TableToolbar
23
+ aria-controls="purpur-table"
24
+ settingsDrawerAriaControls="purpur-table-settings-drawer"
25
+ exportDrawerAriaControls="purpur-table-export-drawer"
23
26
  onResetColumnFilters={resetColumnFiltersMock}
24
27
  onSetDrawerIsOpen={setDrawerIsOpenMock}
25
28
  onToggleExpand={toggleExpandMock}
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { type Dispatch, type HTMLAttributes, type SetStateAction } from "react";
2
2
  import { Button } from "@purpurds/button";
3
3
  import { IconDownload } from "@purpurds/icon/download";
4
4
  import { IconMaximize } from "@purpurds/icon/maximize";
@@ -28,8 +28,8 @@ export type TableToolbarCopyProps = {
28
28
  };
29
29
  };
30
30
 
31
- export type TableToolbarProps = {
32
- onSetDrawerIsOpen: React.Dispatch<React.SetStateAction<boolean>>;
31
+ export type TableToolbarProps = HTMLAttributes<HTMLDivElement> & {
32
+ onSetDrawerIsOpen: Dispatch<SetStateAction<boolean>>;
33
33
  onResetColumnFilters: () => void;
34
34
  onToggleExpand?: () => void;
35
35
  onExportData?: () => void;
@@ -41,6 +41,8 @@ export type TableToolbarProps = {
41
41
  hasExportsDrawer: boolean;
42
42
  showFilters: boolean;
43
43
  disableClearFilters: boolean;
44
+ settingsDrawerAriaControls: string;
45
+ exportDrawerAriaControls: string;
44
46
  };
45
47
 
46
48
  const rootClassName = "purpur-table-toolbar";
@@ -48,6 +50,9 @@ const cx = c.bind(styles);
48
50
  const rootTestId = "purpur-table-toolbar";
49
51
 
50
52
  export const TableToolbar = ({
53
+ ["aria-controls"]: ariaControls,
54
+ settingsDrawerAriaControls,
55
+ exportDrawerAriaControls,
51
56
  onExportData,
52
57
  onResetColumnFilters,
53
58
  onSetDrawerIsOpen,
@@ -76,7 +81,7 @@ export const TableToolbar = ({
76
81
  onClick={onResetColumnFilters}
77
82
  type="button"
78
83
  aria-label={copy?.ariaLabels?.clearFilters}
79
- aria-controls="purpur-table"
84
+ aria-controls={ariaControls}
80
85
  disabled={disableClearFilters}
81
86
  >
82
87
  {copy.buttons.clearFilters}
@@ -94,7 +99,7 @@ export const TableToolbar = ({
94
99
  aria-label={copy?.ariaLabels?.export}
95
100
  {...(hasExportsDrawer && {
96
101
  "aria-expanded": isExportDrawerOpen,
97
- "aria-controls": "purpur-table-export-drawer",
102
+ "aria-controls": exportDrawerAriaControls,
98
103
  "aria-haspopup": "dialog",
99
104
  })}
100
105
  >
@@ -110,7 +115,7 @@ export const TableToolbar = ({
110
115
  type="button"
111
116
  aria-label={copy?.ariaLabels?.settings}
112
117
  aria-expanded={isSettingsDrawerOpen}
113
- aria-controls="purpur-table-settings-drawer"
118
+ aria-controls={settingsDrawerAriaControls}
114
119
  aria-haspopup="dialog"
115
120
  >
116
121
  <IconSettings size="xs" />
@@ -124,7 +129,7 @@ export const TableToolbar = ({
124
129
  onClick={() => onToggleExpand()}
125
130
  type="button"
126
131
  aria-label={copy?.ariaLabels?.expand}
127
- aria-controls="purpur-table"
132
+ aria-controls={ariaControls}
128
133
  >
129
134
  <IconMaximize size="xs" />
130
135
  {copy.buttons.expand}
@@ -8,7 +8,6 @@
8
8
  flex-direction: column;
9
9
  gap: var(--purpur-spacing-200);
10
10
  z-index: 1;
11
- max-width: fit-content;
12
11
  background-color: inherit;
13
12
  }
14
13
 
@@ -38,6 +37,10 @@
38
37
  border-collapse: separate;
39
38
  border-spacing: 0;
40
39
  position: relative;
40
+
41
+ &--full-width {
42
+ width: 100%;
43
+ }
41
44
  }
42
45
 
43
46
  .purpur-table-header {
@@ -72,11 +75,16 @@
72
75
  }
73
76
 
74
77
  .purpur-table-column-header-cell {
75
- padding: var(--purpur-spacing-150) var(--purpur-spacing-150) var(--purpur-spacing-150)
78
+ padding: var(--purpur-spacing-100) var(--purpur-spacing-150) var(--purpur-spacing-100)
76
79
  var(--purpur-spacing-300);
77
80
  border-bottom: var(--table-border);
78
81
  text-align: left;
79
82
 
83
+ &:has([role="checkbox"]) {
84
+ padding: calc(var(--purpur-spacing-25) + var(--purpur-spacing-50)) var(--purpur-spacing-150)
85
+ calc(var(--purpur-spacing-25) + var(--purpur-spacing-50)) var(--purpur-spacing-300);
86
+ }
87
+
80
88
  &__checkbox {
81
89
  vertical-align: middle;
82
90
  }
@@ -93,7 +101,7 @@
93
101
  display: flex;
94
102
  flex-direction: column;
95
103
  align-items: flex-start;
96
- gap: var(--purpur-spacing-100);
104
+ gap: var(--purpur-spacing-50);
97
105
  }
98
106
 
99
107
  &__title {
@@ -101,6 +109,10 @@
101
109
  align-items: center;
102
110
  gap: var(--purpur-spacing-50);
103
111
  align-self: stretch;
112
+
113
+ &--default {
114
+ padding: var(--purpur-spacing-100) var(--purpur-spacing-0);
115
+ }
104
116
  }
105
117
 
106
118
  &__sortable-title {
@@ -110,9 +122,9 @@
110
122
  &__filter-wrapper {
111
123
  display: flex;
112
124
  padding-right: var(--purpur-spacing-150);
125
+ padding-bottom: var(--purpur-spacing-150);
113
126
  flex-direction: column;
114
127
  align-items: flex-start;
115
- gap: var(--purpur-spacing-100);
116
128
  align-self: stretch;
117
129
  }
118
130
  }
@@ -194,7 +206,7 @@
194
206
  }
195
207
 
196
208
  .purpur-table-row-cell {
197
- height: var(--purpur-spacing-800);
209
+ height: calc(var(--purpur-spacing-600) + var(--purpur-spacing-150));
198
210
  padding: var(--purpur-spacing-0) var(--purpur-spacing-300);
199
211
  align-items: center;
200
212
  align-self: stretch;
@@ -263,30 +275,27 @@
263
275
  text-align: right;
264
276
  }
265
277
 
278
+ &__truncate-wrapper {
279
+ width: 100%;
280
+ display: grid;
281
+ grid-template-columns: 1fr;
282
+ }
283
+
266
284
  &__truncate {
267
285
  position: relative;
268
286
  max-width: 100%;
269
287
  width: 100%;
270
288
 
271
- p,
272
- span,
273
- div {
274
- display: block;
275
- }
289
+ display: block;
290
+ overflow: hidden;
291
+ white-space: nowrap;
292
+ text-overflow: ellipsis;
276
293
 
277
294
  a {
278
295
  display: inline;
279
- }
280
-
281
- a,
282
- p,
283
- span,
284
- div {
285
296
  overflow: hidden;
286
297
  white-space: nowrap;
287
298
  text-overflow: ellipsis;
288
- max-width: 100%;
289
- width: inherit;
290
299
  }
291
300
  }
292
301
 
@@ -1,7 +1,5 @@
1
1
  /* eslint-disable react-hooks/rules-of-hooks */
2
2
 
3
-
4
-
5
3
  import React, { useEffect, useState } from "react";
6
4
  import { Grid } from "@purpurds/grid";
7
5
  import { IllustrativeIconSearchQuestionDuocolor } from "@purpurds/illustrative-icon/search-question-duocolor";
@@ -154,6 +152,10 @@ const meta = {
154
152
  options: ["primary", "secondary"],
155
153
  description: "Style variant for the table",
156
154
  },
155
+ fullWidth: {
156
+ control: "boolean",
157
+ description: "Full width of the table",
158
+ },
157
159
  },
158
160
  } satisfies Meta<typeof Table<TableData>>;
159
161
 
@@ -245,7 +247,7 @@ const commonSettingsDrawerCopy = {
245
247
  const commonExportDrawerCopy = {
246
248
  title: "Export table",
247
249
  bodyText: "Choose the format you want to export the table in.",
248
- closeButtonText: "Close drawer",
250
+ closeButtonAriaLabel: "Close drawer",
249
251
  link: "Export as",
250
252
  };
251
253
 
@@ -277,6 +279,7 @@ export const Showcase: StoryTableData = {
277
279
  data: generatedTableData,
278
280
  columns: columnDef,
279
281
  loading: true,
282
+ fullWidth: true,
280
283
  },
281
284
  parameters: {
282
285
  docs: {
@@ -345,8 +348,7 @@ manages filtering, sorting and pagination internally.
345
348
  nextButtonAriaLabel: "Go to next page",
346
349
  nextButtonText: "Next",
347
350
  outOfLabel: "of",
348
- pageSelectorListBoxLabel: "Select a page",
349
- pageSelectorNoOptionsText: "Page does not exist",
351
+ pageSelectorLabel: "Select a page",
350
352
  previousButtonAriaLabel: "Go to previous page",
351
353
  previousButtonText: "Previous",
352
354
  stepNumberPrefix: "Go to page",
@@ -389,6 +391,7 @@ manages filtering, sorting and pagination internally.
389
391
  onPrimaryButtonClick={handlePrimaryButtonClick}
390
392
  onSecondaryButtonClick={() => console.log("Secondary button clicked")}
391
393
  rowSelectionAriaLabels={args.rowSelectionAriaLabels}
394
+ fullWidth={args.fullWidth}
392
395
  />
393
396
  );
394
397
  },
@@ -730,8 +733,7 @@ This example demonstrates pagination functionality in the Table.
730
733
  nextButtonAriaLabel: "Go to next page",
731
734
  nextButtonText: "Next",
732
735
  outOfLabel: "of",
733
- pageSelectorListBoxLabel: "Select a page",
734
- pageSelectorNoOptionsText: "Page does not exist",
736
+ pageSelectorLabel: "Select a page",
735
737
  previousButtonAriaLabel: "Go to previous page",
736
738
  previousButtonText: "Previous",
737
739
  stepNumberPrefix: "Go to page",
@@ -1150,8 +1152,7 @@ sorting, and filtering are managed by a server API instead of in the browser.
1150
1152
  nextButtonAriaLabel: "Go to next page",
1151
1153
  nextButtonText: "Next",
1152
1154
  outOfLabel: "of",
1153
- pageSelectorListBoxLabel: "Select a page",
1154
- pageSelectorNoOptionsText: "Page does not exist",
1155
+ pageSelectorLabel: "Select a page",
1155
1156
  previousButtonAriaLabel: "Go to previous page",
1156
1157
  previousButtonText: "Previous",
1157
1158
  stepNumberPrefix: "Go to page",
package/src/table.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { ReactElement, useEffect, useRef, useState } from "react";
1
+ import React, { ReactElement, useEffect, useId, useRef, useState } from "react";
2
2
  import type {
3
3
  ColumnDef,
4
4
  ColumnFiltersState,
@@ -70,6 +70,7 @@ export type TableProps<TData extends RowData> = {
70
70
  columns: CoreOptions<TData>["columns"];
71
71
  data: TData[];
72
72
  paginationComponent?: ReactElement<PaginationProps>;
73
+ fullWidth?: boolean;
73
74
  onRowsCountChange?: (rowsCount: number) => void;
74
75
  } & (WithToolbarProps | WithoutToolbarProps) &
75
76
  (WithSortingProps | WithoutSortingProps) &
@@ -94,6 +95,7 @@ export const Table = <TData extends RowData>({
94
95
  enableToolbar: enableToolbar,
95
96
  exportDrawerCopy,
96
97
  exportFormats,
98
+ fullWidth = true,
97
99
  loading,
98
100
  paginationComponent,
99
101
  rowSelectionAriaLabels,
@@ -123,10 +125,11 @@ export const Table = <TData extends RowData>({
123
125
  );
124
126
  const [stickyFirstColumn, setStickyFirstColumn] = useState(true);
125
127
  const [stickyHeaders, setStickyHeaders] = useState(true);
126
- const [rowSelectionEnabled, setRowSelectionEnabled] = useState(true);
127
128
  const prevShowOnlySelectedRows = useRef(showOnlySelectedRows);
128
129
  const [isScrolled, setIsScrolled] = useState(false);
129
130
  const tableContainerRef = useRef<HTMLTableElement>(null);
131
+ const uid = useId();
132
+ const [rowSelectionEnabled, setRowSelectionEnabled] = useState(true);
130
133
 
131
134
  const classes = cx([
132
135
  className,
@@ -252,7 +255,9 @@ export const Table = <TData extends RowData>({
252
255
  };
253
256
 
254
257
  const getStickyColumn = (index: number) => {
255
- return props.enableRowSelection ? index <= 1 : index === 0;
258
+ // When rowSelectionEnabled is true, the first two columns are sticky
259
+ // Otherwise, only the first column is sticky
260
+ return props.enableRowSelection && rowSelectionEnabled ? index <= 1 : index === 0;
256
261
  };
257
262
 
258
263
  const handleResetColumnFilters = () => {
@@ -275,11 +280,15 @@ export const Table = <TData extends RowData>({
275
280
  setShowColumnFiltersEnabled(true);
276
281
  setStickyFirstColumn(true);
277
282
  setStickyHeaders(true);
278
- setRowSelectionEnabled(true);
279
283
 
280
284
  tanstackTable.resetColumnVisibility();
281
285
  };
282
286
 
287
+ const showBorder = (index: number) => {
288
+ // Only show the border for sticky columns when the table is scrolled
289
+ return isScrolled && getStickyColumn(index);
290
+ };
291
+
283
292
  const tableRows = tanstackTable.getRowModel().rows;
284
293
  const emptyTable = tableRows.length === 0 && Boolean(emptyTableCopy);
285
294
 
@@ -294,8 +303,8 @@ export const Table = <TData extends RowData>({
294
303
  getStickyColumn={getStickyColumn}
295
304
  stickyFirstColumn={stickyFirstColumn}
296
305
  isScrolled={isScrolled}
297
- rowSelectionEnabled={rowSelectionEnabled}
298
306
  cellWidths={getColumnWidths()}
307
+ showBorder={showBorder}
299
308
  />
300
309
  ) : emptyTable && emptyTableCopy && emptyTableHeadingTag ? (
301
310
  <EmptyTable
@@ -318,11 +327,7 @@ export const Table = <TData extends RowData>({
318
327
  isLastCell={cellIndex === row.getVisibleCells().length - 1}
319
328
  stickyColumn={stickyFirstColumn && getStickyColumn(cellIndex)}
320
329
  isScrolled={isScrolled}
321
- showBorder={
322
- isScrolled &&
323
- ((props.enableRowSelection && cellIndex === 1) ||
324
- (!props.enableRowSelection && cellIndex === 0))
325
- }
330
+ showBorder={showBorder(cellIndex)}
326
331
  />
327
332
  ))}
328
333
  </TableRow>
@@ -330,9 +335,12 @@ export const Table = <TData extends RowData>({
330
335
  );
331
336
 
332
337
  return (
333
- <div id="purpur-table" className={classes}>
338
+ <div id={`${uid}-table`} className={classes}>
334
339
  {enableToolbar && (
335
340
  <TableToolbar
341
+ aria-controls={`${uid}-table`}
342
+ settingsDrawerAriaControls={`${uid}-settings-drawer`}
343
+ exportDrawerAriaControls={`${uid}-export-drawer`}
336
344
  onSetDrawerIsOpen={setSettingsDrawerIsOpen}
337
345
  onResetColumnFilters={handleResetColumnFilters}
338
346
  onToggleExpand={onToggleExpand}
@@ -353,7 +361,12 @@ export const Table = <TData extends RowData>({
353
361
  })}
354
362
  ref={tableContainerRef}
355
363
  >
356
- <table className={cx(`${rootClassName}__table`)}>
364
+ <table
365
+ className={cx([
366
+ `${rootClassName}__table`,
367
+ { [`${rootClassName}__table--full-width`]: fullWidth },
368
+ ])}
369
+ >
357
370
  <TableHeader columnFiltersEnabled={showColumnFiltersEnabled}>
358
371
  {tanstackTable.getHeaderGroups().map((headerGroup) => (
359
372
  <TableRow key={headerGroup.id}>
@@ -365,11 +378,7 @@ export const Table = <TData extends RowData>({
365
378
  stickyColumn={!emptyTable && stickyFirstColumn && getStickyColumn(index)}
366
379
  stickyHeaders={!emptyTable && stickyHeaders}
367
380
  isScrolled={isScrolled}
368
- showBorder={
369
- isScrolled &&
370
- ((props.enableRowSelection && index === 1) ||
371
- (!props.enableRowSelection && index === 0))
372
- }
381
+ showBorder={showBorder(index)}
373
382
  {...(props.enableSorting && sortingAriaLabels
374
383
  ? { enableSorting: props.enableSorting, sortingAriaLabels }
375
384
  : { enableSorting: false })}
@@ -396,6 +405,7 @@ export const Table = <TData extends RowData>({
396
405
  )}
397
406
  {enableToolbar && settingsDrawerCopy && (
398
407
  <TableSettingsDrawer
408
+ id={`${uid}-settings-drawer`}
399
409
  setDrawerIsOpen={setSettingsDrawerIsOpen}
400
410
  setShowColumnFiltersEnabled={setShowColumnFiltersEnabled}
401
411
  setStickyFirstColumn={setStickyFirstColumn}
@@ -415,6 +425,7 @@ export const Table = <TData extends RowData>({
415
425
  )}
416
426
  {Array.isArray(exportFormats) && exportDrawerCopy && (
417
427
  <TableExportDrawer
428
+ id={`${uid}-export-drawer`}
418
429
  isOpen={isExportDrawerOpen}
419
430
  setDrawerIsOpen={setExportDrawerIsOpen}
420
431
  exportFormats={exportFormats}
@@ -446,7 +457,7 @@ const EmptyTable = ({ variant, tag, title, description, colSpan, icon }: EmptyTa
446
457
  `${rootClassName}__empty-section--${variant}`,
447
458
  ])}
448
459
  >
449
- <div className={cx(`${rootClassName}__empty-section__icon`)}>{icon}</div>
460
+ {icon && <div className={cx(`${rootClassName}__empty-section__icon`)}>{icon}</div>}
450
461
  <div className={cx(`${rootClassName}__empty-section__texts`)}>
451
462
  <Heading data-testid="purpur-table-empty-table-title" variant="title-100" tag={tag}>
452
463
  {title}
@@ -460,21 +471,20 @@ const EmptyTable = ({ variant, tag, title, description, colSpan, icon }: EmptyTa
460
471
 
461
472
  type LoadingTableRowsProps = {
462
473
  rowCount: number;
463
- rowSelectionEnabled: boolean;
464
474
  isScrolled: boolean;
465
475
  stickyFirstColumn: boolean;
466
476
  getStickyColumn: (index: number) => boolean;
467
477
  cellWidths: (string | number)[];
478
+ showBorder: (index: number) => boolean;
468
479
  };
469
480
 
470
481
  const LoadingTableRows = ({
471
482
  rowCount,
472
- // cellCount,
473
483
  getStickyColumn,
474
484
  stickyFirstColumn,
475
485
  isScrolled,
476
- rowSelectionEnabled,
477
486
  cellWidths,
487
+ showBorder,
478
488
  }: LoadingTableRowsProps) => (
479
489
  <>
480
490
  {Array.from({ length: rowCount }, (_value, index) => index).map((row, rowIndex) => (
@@ -487,11 +497,7 @@ const LoadingTableRows = ({
487
497
  isLastCell={cellIndex === cellWidths.length - 1}
488
498
  stickyColumn={stickyFirstColumn && getStickyColumn(cellIndex)}
489
499
  isScrolled={isScrolled}
490
- showBorder={
491
- isScrolled &&
492
- ((rowSelectionEnabled && cellIndex === 1) ||
493
- (!rowSelectionEnabled && cellIndex === 0))
494
- }
500
+ showBorder={showBorder(cellIndex)}
495
501
  cellWidth={cellWidth}
496
502
  />
497
503
  ))}
@@ -145,6 +145,7 @@ export const Selectors = {
145
145
  PAGINATION: {
146
146
  ROOT: "purpur-pagination",
147
147
  PAGE_SIZE_SELECT: "purpur-pagination-page-size-selector-select-select",
148
+ PAGE_BUTTON: (pageNumber: number) => `purpur-pagination-pages-page-${pageNumber}-button`,
148
149
  },
149
150
  SKELETON: "purpur-table-cell-skeleton",
150
151
  };
@@ -164,7 +165,7 @@ export const copy = {
164
165
  },
165
166
  exportDrawer: {
166
167
  bodyText: "Choose the format you want to export the table in.",
167
- closeButtonText: "Close drawer",
168
+ closeButtonAriaLabel: "Close drawer",
168
169
  link: "Export as",
169
170
  title: "Export table",
170
171
  },
package/src/types.ts CHANGED
@@ -190,7 +190,7 @@ export type WithEmptyTableProps = {
190
190
  /**
191
191
  * Icon to display in the empty table state.
192
192
  */
193
- emptyTableIcon: React.ReactNode;
193
+ emptyTableIcon?: React.ReactNode;
194
194
  };
195
195
 
196
196
  export type WithoutEmptyTableProps = {