@purpurds/table 6.12.5 → 7.1.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 (51) 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 +4019 -4021
  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.test.tsx +65 -0
  30. package/src/cell-types/body-text-cell.tsx +8 -9
  31. package/src/cell-types/lead-text-cell.test.tsx +65 -0
  32. package/src/cell-types/lead-text-cell.tsx +1 -1
  33. package/src/cell-types/link-cell.tsx +13 -6
  34. package/src/story-utils/column-def.ts +2 -0
  35. package/src/story-utils/table-data.tsx +10 -8
  36. package/src/table-action-bar.tsx +3 -2
  37. package/src/table-column-header-cell.tsx +16 -6
  38. package/src/table-export-drawer.test.tsx +2 -1
  39. package/src/table-export-drawer.tsx +5 -3
  40. package/src/table-kitchen-sink.test.tsx +5 -18
  41. package/src/table-row-cell.tsx +1 -30
  42. package/src/table-settings-drawer.test.tsx +4 -4
  43. package/src/table-settings-drawer.tsx +46 -41
  44. package/src/table-toolbar.test.tsx +3 -0
  45. package/src/table-toolbar.tsx +12 -7
  46. package/src/table.module.scss +51 -32
  47. package/src/table.stories.tsx +10 -9
  48. package/src/table.tsx +32 -26
  49. package/src/test-utils/helpers.ts +2 -1
  50. package/src/types.ts +1 -1
  51. package/src/use-truncated-hook.tsx +16 -60
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 && stickyFirstColumn && 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 = {
@@ -1,66 +1,22 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import { Cell, RowData } from "@tanstack/react-table";
1
+ import { useRef, useState } from "react";
3
2
 
4
- const useTruncatedTooltip = <TData extends RowData>(cell: Cell<TData, unknown> | undefined) => {
5
- const divRef = useRef<HTMLParagraphElement>(null);
6
- const [isTruncated, setIsTruncated] = useState(false);
7
- const timeoutIdRef = useRef<NodeJS.Timeout | undefined>(undefined);
3
+ const useTruncatedTooltip = () => {
8
4
  const [showPopover, setShowPopover] = useState(false);
9
-
10
- const debounce = (func: (...args: unknown[]) => void, delay: number) => {
11
- let timeoutId: NodeJS.Timeout;
12
- const debouncedFunction = (...args: unknown[]) => {
13
- clearTimeout(timeoutId);
14
- timeoutId = setTimeout(() => func.apply(this, args), delay);
15
- };
16
- return {
17
- debouncedFunction,
18
- get timeoutId() {
19
- return timeoutId;
20
- },
21
- };
22
- };
23
-
24
- useEffect(() => {
25
- if (!cell) {
26
- return;
27
- }
28
-
29
- const { debouncedFunction: checkTruncation, timeoutId } = debounce(() => {
30
- if (divRef.current && cell) {
31
- const pEl = divRef.current.querySelector("p");
32
- if (pEl) {
33
- setIsTruncated(pEl.scrollWidth > cell.column.getSize());
34
- }
35
- }
36
- }, 100); // Adjust debounce delay as needed
37
-
38
- checkTruncation();
39
- timeoutIdRef.current = timeoutId;
40
-
41
- return () => {
42
- clearTimeout(timeoutIdRef.current);
43
- };
44
- }, [cell]);
45
-
46
- useEffect(() => {
47
- if (divRef.current && cell) {
48
- if (divRef.current.textContent) {
49
- const pEl = divRef.current.querySelector("p");
50
- if (pEl) {
51
- setIsTruncated(pEl.scrollWidth > cell.column.getSize());
52
- }
53
- const aEl = divRef.current.querySelector("a");
54
- if (aEl) {
55
- setIsTruncated(aEl.scrollWidth > cell.column.getSize());
56
- }
57
- }
58
- }
59
- }, [divRef, cell, isTruncated]);
5
+ const containerRef = useRef<HTMLDivElement | null>(null);
60
6
 
61
7
  const onMouseEnter = () => {
62
- if (isTruncated) {
63
- setShowPopover(true);
8
+ if (containerRef.current) {
9
+ const contentElement: HTMLLinkElement | HTMLParagraphElement | null =
10
+ containerRef.current.querySelector("a, p"); // Find "a" or "p" tag
11
+ if (contentElement) {
12
+ const containerWidth = containerRef.current.clientWidth;
13
+ const contentWidth =
14
+ contentElement.tagName.toLowerCase() === "a"
15
+ ? contentElement.offsetWidth
16
+ : contentElement.scrollWidth;
17
+
18
+ setShowPopover(contentWidth > containerWidth);
19
+ }
64
20
  }
65
21
  };
66
22
 
@@ -68,7 +24,7 @@ const useTruncatedTooltip = <TData extends RowData>(cell: Cell<TData, unknown> |
68
24
  setShowPopover(false);
69
25
  };
70
26
 
71
- return { divRef, onMouseEnter, onMouseLeave, showPopover };
27
+ return { showPopover, containerRef, onMouseEnter, onMouseLeave };
72
28
  };
73
29
 
74
30
  export default useTruncatedTooltip;