@nova-design-system/nova-react 3.29.0 → 3.31.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 (86) hide show
  1. package/dist/cjs/{constants-BReL3Lsa-DpB_ghPF.js → constants-gOKBmbgZ-CSoZ8z-G.js} +5 -0
  2. package/dist/cjs/generated/components.server.js +35 -1
  3. package/dist/cjs/index-BRxlcrvE.js +10395 -0
  4. package/dist/cjs/index.js +7 -1
  5. package/dist/cjs/{nv-accordion-item.entry-D1o0gC5w.js → nv-accordion-item.entry-DWi_bEQx.js} +3 -3
  6. package/dist/cjs/{nv-accordion.entry-DYJtq9Az.js → nv-accordion.entry-Bgn2CRYE.js} +2 -2
  7. package/dist/cjs/{nv-alert.entry-CMtCdHmk.js → nv-alert.entry-tkzdxYo4.js} +8 -8
  8. package/dist/cjs/{nv-avatar.entry-B6e7aG4W.js → nv-avatar.entry-C6xqQhws.js} +8 -8
  9. package/dist/cjs/{nv-badge_2.entry-RD3j0bJM.js → nv-badge_2.entry-poMKx9km.js} +5 -5
  10. package/dist/cjs/{nv-breadcrumb.entry-DgpqY2fr.js → nv-breadcrumb.entry-BTBUN3X8.js} +2 -2
  11. package/dist/cjs/{nv-breadcrumbs.entry-CZgcDUw5.js → nv-breadcrumbs.entry-DXdb_Gzf.js} +2 -2
  12. package/dist/cjs/{nv-button.entry-DR9NaRxG.js → nv-button.entry-IYrV4dVV.js} +7 -7
  13. package/dist/cjs/{nv-buttongroup.entry-qO8r7WqG.js → nv-buttongroup.entry-DpMhGqzQ.js} +2 -2
  14. package/dist/cjs/{nv-calendar.entry-DRlv3Xph.js → nv-calendar.entry-Clp-qYdU.js} +9 -9
  15. package/dist/cjs/{nv-col.entry-B7utJttP.js → nv-col.entry-BgIpEdn6.js} +2 -2
  16. package/dist/cjs/{nv-datagrid.entry-digYmlnA.js → nv-datagrid.entry-CmnSlQEN.js} +8 -8
  17. package/dist/cjs/{nv-datagridcolumn.entry-UwaDi-Hr.js → nv-datagridcolumn.entry-CfaOqHSs.js} +2 -2
  18. package/dist/cjs/{nv-datetest.entry-BJtWaM2T.js → nv-datetest.entry-DL0WPAUH.js} +2 -2
  19. package/dist/cjs/{nv-datetimetest.entry-WaNPcHxh.js → nv-datetimetest.entry-DoABznI1.js} +1 -1
  20. package/dist/cjs/{nv-dialog.entry-BI_mFT2G.js → nv-dialog.entry-C95UrJIU.js} +3 -3
  21. package/dist/cjs/{nv-dialogfooter_2.entry-DU2ClBUR.js → nv-dialogfooter_2.entry-Ccml86Uw.js} +6 -6
  22. package/dist/cjs/{nv-drawer.entry-BKF4CyOt.js → nv-drawer.entry-cXCjYVTV.js} +3 -3
  23. package/dist/cjs/{nv-drawerfooter_2.entry-BSyUs7_4.js → nv-drawerfooter_2.entry-Dq0IblnH.js} +6 -6
  24. package/dist/cjs/{nv-fieldcheckbox.entry-l5FWToQi.js → nv-fieldcheckbox.entry-CnP54xin.js} +5 -5
  25. package/dist/cjs/{nv-fielddate.entry-BfviesNp.js → nv-fielddate.entry-DZY75-mm.js} +7 -7
  26. package/dist/cjs/{nv-fielddaterange.entry-BvDkPLXG.js → nv-fielddaterange.entry-DkouN3iA.js} +7 -7
  27. package/dist/cjs/{nv-fielddropdown.entry-B-nYjuMt.js → nv-fielddropdown.entry-Bc3bobvr.js} +101 -23
  28. package/dist/cjs/{nv-fielddropdownitem.entry-Dxqyb9DN.js → nv-fielddropdownitem.entry-CmdskK1s.js} +2 -2
  29. package/dist/cjs/{nv-fieldmultiselect.entry-CgO1RP0W.js → nv-fieldmultiselect.entry-Bc6YFUA_.js} +1 -1
  30. package/dist/cjs/{nv-fieldnumber.entry-stgdLi7x.js → nv-fieldnumber.entry-DwvjJfV9.js} +5 -5
  31. package/dist/cjs/{nv-fieldpassword.entry-CHA3JAUd.js → nv-fieldpassword.entry-CUI2Jm-V.js} +5 -5
  32. package/dist/cjs/{nv-fieldradio.entry-DHavVjB-.js → nv-fieldradio.entry-CnLCvLBh.js} +4 -4
  33. package/dist/cjs/{nv-fieldselect.entry-Dq5AsOFt.js → nv-fieldselect.entry-DunLjNwe.js} +45 -7
  34. package/dist/cjs/{nv-fieldslider.entry-SlF3BBUW.js → nv-fieldslider.entry-Cp-6fToR.js} +4 -4
  35. package/dist/cjs/{nv-fieldtext.entry-BYCc8SyD.js → nv-fieldtext.entry-FJk12WoX.js} +5 -5
  36. package/dist/cjs/{nv-fieldtextarea.entry-96JCOb9L.js → nv-fieldtextarea.entry-Ci9B5-DX.js} +4 -4
  37. package/dist/cjs/{nv-fieldtime.entry-BdzjxkaK.js → nv-fieldtime.entry-DCSdECWn.js} +44 -44
  38. package/dist/cjs/{nv-icon.entry-CSRxi6BH.js → nv-icon.entry-DB18IDaU.js} +9 -9
  39. package/dist/cjs/{nv-iconbutton_2.entry-BtDxwMFD.js → nv-iconbutton_2.entry-CNR_poEm.js} +3 -3
  40. package/dist/cjs/{nv-menu.entry-aX39SPH8.js → nv-menu.entry-CxXHuTq3.js} +2 -2
  41. package/dist/cjs/{nv-menuitem.entry-DzSp52G2.js → nv-menuitem.entry-44XD7nfl.js} +2 -2
  42. package/dist/cjs/{nv-notification-bullet.entry-D2yOXj46.js → nv-notification-bullet.entry-Cn2OELYw.js} +1 -1
  43. package/dist/cjs/{nv-notification.entry-BRXHbtT8.js → nv-notification.entry-C8beqUzI.js} +19 -19
  44. package/dist/cjs/{nv-notificationcontainer.entry-YNIM2_ah.js → nv-notificationcontainer.entry-DgEstiLX.js} +2 -2
  45. package/dist/cjs/{nv-pagination-nav.entry-uY1nT9aT.js → nv-pagination-nav.entry-CIZPwFeF.js} +2 -2
  46. package/dist/cjs/{nv-paginationtable.entry-mr5KYXVC.js → nv-paginationtable.entry-woQZpVmb.js} +19 -3
  47. package/dist/cjs/{nv-popover.entry-_9ARKM70.js → nv-popover.entry-rHUUSzR-.js} +2 -2
  48. package/dist/cjs/{nv-row.entry-BMQvcqlU.js → nv-row.entry-DaERf8TD.js} +2 -2
  49. package/dist/cjs/nv-sidebar.entry-BI9me_HP.js +355 -0
  50. package/dist/cjs/{nv-sidebarcontent.entry-llnRwVuj.js → nv-sidebarcontent.entry-CtARsnjy.js} +2 -2
  51. package/dist/cjs/{nv-sidebardivider.entry--rQv8d5T.js → nv-sidebardivider.entry-C15Xk11m.js} +2 -2
  52. package/dist/cjs/{nv-sidebarfooter.entry-C5R0sUI_.js → nv-sidebarfooter.entry-DFMMvv_Q.js} +2 -2
  53. package/dist/cjs/{nv-sidebargroup.entry-DdrpLbU7.js → nv-sidebargroup.entry-DeWOA-0I.js} +2 -2
  54. package/dist/cjs/{nv-sidebarheader.entry-HuBPIsyf.js → nv-sidebarheader.entry-HtvbpvnX.js} +2 -2
  55. package/dist/cjs/{nv-sidebarlogo.entry-TZNPoqnA.js → nv-sidebarlogo.entry-Zxbpttzr.js} +2 -2
  56. package/dist/cjs/{nv-sidebarnavitem.entry-BWc3mF5I.js → nv-sidebarnavitem.entry-u4rbC1el.js} +3 -3
  57. package/dist/cjs/{nv-sidebarnavsubitem.entry-DekANwO8.js → nv-sidebarnavsubitem.entry-UouMn7hU.js} +2 -2
  58. package/dist/cjs/{nv-split.entry-BOwOB8FW.js → nv-split.entry-CUMbQNQB.js} +2 -2
  59. package/dist/cjs/{nv-stack.entry-CaTiSLGN.js → nv-stack.entry-D_F42KTD.js} +2 -2
  60. package/dist/cjs/nv-statusindicator.entry-BdRvU3iW.js +42 -0
  61. package/dist/cjs/{nv-table.entry-LFZaS0Dy.js → nv-table.entry-CKVH76OE.js} +3 -3
  62. package/dist/cjs/{nv-tableheader.entry-CK50S8xW.js → nv-tableheader.entry-btKwE14F.js} +2 -2
  63. package/dist/cjs/nv-tag.entry-BxYC7Lgo.js +85 -0
  64. package/dist/cjs/{nv-timetest.entry-A9elKwkf.js → nv-timetest.entry-CxXMIDZ_.js} +2 -2
  65. package/dist/cjs/{nv-toggle.entry-BGu-6rEs.js → nv-toggle.entry-C4uo7RRt.js} +3 -3
  66. package/dist/cjs/{nv-togglebutton.entry-BzAcBOgN.js → nv-togglebutton.entry-DJ-s5lgO.js} +2 -2
  67. package/dist/cjs/{nv-togglebuttongroup.entry-CIWp7IXc.js → nv-togglebuttongroup.entry-DJOCL_Qx.js} +2 -2
  68. package/dist/cjs/{nv-tooltip.entry-CoGkSHpv.js → nv-tooltip.entry-DndZWT1U.js} +2 -2
  69. package/dist/components/NvDatatable/NvDatatable.js +178 -61
  70. package/dist/components/NvDatatable/expandState.js +8 -0
  71. package/dist/components/NvDatatable/expandState.test.js +41 -0
  72. package/dist/components/NvDatatable/paginationState.js +9 -0
  73. package/dist/components/NvDatatable/paginationState.test.js +84 -0
  74. package/dist/generated/components.js +25 -2
  75. package/dist/generated/components.server.js +33 -1
  76. package/dist/types/components/NvDatatable/NvDatatable.d.ts +1 -1
  77. package/dist/types/components/NvDatatable/expandState.d.ts +5 -0
  78. package/dist/types/components/NvDatatable/expandState.test.d.ts +1 -0
  79. package/dist/types/components/NvDatatable/paginationState.d.ts +9 -0
  80. package/dist/types/components/NvDatatable/paginationState.test.d.ts +1 -0
  81. package/dist/types/components/NvDatatable/types.d.ts +17 -1
  82. package/dist/types/generated/components.d.ts +14 -1
  83. package/dist/types/generated/components.server.d.ts +14 -1
  84. package/package.json +1 -1
  85. package/dist/cjs/index-DgKzi_Pd.js +0 -10208
  86. package/dist/cjs/nv-sidebar.entry-C4HTjJmz.js +0 -181
@@ -1,13 +1,15 @@
1
1
  import React, { useMemo, useState, useEffect, useRef, useCallback, } from 'react';
2
- import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, useReactTable, } from '@tanstack/react-table';
3
- import { NvTable, NvTableheader, NvPaginationtable, NvFieldcheckbox, } from '../../generated/components';
2
+ import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, getFilteredRowModel, getExpandedRowModel, useReactTable, } from '@tanstack/react-table';
3
+ import { NvTable, NvTableheader, NvPaginationtable, NvFieldcheckbox, NvIconbutton, } from '../../generated/components';
4
4
  import { defineCustomElement as defineNvPaginationtable } from '@nova-design-system/nova-webcomponents/dist/components/nv-paginationtable.js';
5
5
  if (typeof window !== 'undefined' &&
6
6
  !customElements.get('nv-paginationtable')) {
7
7
  defineNvPaginationtable();
8
8
  }
9
9
  import { applyRowSelection, applySelectAllSelection, shouldIgnoreSelectAllEvent, } from './selectionState';
10
- function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filtering, selection, renderPagination, renderFiltering, onRowClick, stickyHeader, ...htmlProps }) {
10
+ import { shouldShowExpandedContent } from './expandState';
11
+ import { resolvePaginationRowCount } from './paginationState';
12
+ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filtering, selection, renderPagination, renderFiltering, onRowClick, expandable, stickyHeader, ...htmlProps }) {
11
13
  const areSetsEqual = useCallback((a, b) => {
12
14
  if (a.size !== b.size) {
13
15
  return false;
@@ -24,14 +26,22 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
24
26
  pageIndex: 0,
25
27
  pageSize: pagination?.initialPageSize || 10,
26
28
  });
27
- const [sortingState, setSortingState] = useState(sorting?.sortState || []);
29
+ const [sortingState, setSortingState] = useState(sorting?.sortState ?? sorting?.initialSortState ?? []);
28
30
  const [globalFilterState, setGlobalFilterState] = useState(filtering?.filterState);
29
- const [selectedRowIdsState, setSelectedRowIdsState] = useState(() => new Set(selection?.selectedRowIds ?? []));
30
- const selectedRowIdsRef = useRef(new Set(selection?.selectedRowIds ?? []));
31
+ const [selectedRowIdsState, setSelectedRowIdsState] = useState(() => new Set(selection?.selectedRowIds ?? selection?.initialSelectedRowIds ?? []));
32
+ const selectedRowIdsRef = useRef(new Set(selection?.selectedRowIds ?? selection?.initialSelectedRowIds ?? []));
31
33
  const selectionMode = selection?.mode ?? 'none';
32
34
  const selectionEnabled = selectionMode !== 'none';
33
35
  const selectAllEnabled = selection?.enableSelectAll ?? true;
34
36
  const rowClickEnabled = typeof onRowClick === 'function';
37
+ const hasExpandable = !!expandable;
38
+ const isCollapsible = expandable?.collapsible ?? false;
39
+ const [expandedState, setExpandedState] = useState(expandable?.expandedState ?? {});
40
+ useEffect(() => {
41
+ if (expandable?.expandedState !== undefined) {
42
+ setExpandedState(expandable.expandedState);
43
+ }
44
+ }, [expandable?.expandedState]);
35
45
  const getSelectionRowId = useCallback((row) => selection?.getRowId?.(row) ?? '', [selection]);
36
46
  const lastRowRef = useRef(null);
37
47
  const selectAllIntentRef = useRef(false);
@@ -140,29 +150,66 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
140
150
  }
141
151
  return columnDef;
142
152
  });
143
- if (!selectionEnabled) {
144
- return dataColumns;
145
- }
146
- const selectionColumn = {
147
- id: '__selection__',
148
- header: '',
149
- size: selection?.checkboxColumnWidth ?? 48,
150
- enableResizing: false,
151
- enableSorting: false,
152
- cell: () => null,
153
- };
154
- return [selectionColumn, ...dataColumns];
155
- }, [columns, sorting, selectionEnabled, selection?.checkboxColumnWidth]);
153
+ const prefixColumns = [];
154
+ const isCombinedColumn = selectionEnabled && isCollapsible;
155
+ if (isCombinedColumn) {
156
+ prefixColumns.push({
157
+ id: '__selection_expand__',
158
+ header: '',
159
+ size: (selection?.checkboxColumnWidth ?? 48) +
160
+ (expandable?.toggleColumnWidth ?? 48),
161
+ enableResizing: false,
162
+ enableSorting: false,
163
+ cell: () => null,
164
+ });
165
+ }
166
+ else {
167
+ if (selectionEnabled) {
168
+ prefixColumns.push({
169
+ id: '__selection__',
170
+ header: '',
171
+ size: selection?.checkboxColumnWidth ?? 48,
172
+ enableResizing: false,
173
+ enableSorting: false,
174
+ cell: () => null,
175
+ });
176
+ }
177
+ if (isCollapsible) {
178
+ prefixColumns.push({
179
+ id: '__expand__',
180
+ header: '',
181
+ size: expandable?.toggleColumnWidth ?? 48,
182
+ enableResizing: false,
183
+ enableSorting: false,
184
+ cell: () => null,
185
+ });
186
+ }
187
+ }
188
+ return [...prefixColumns, ...dataColumns];
189
+ }, [
190
+ columns,
191
+ sorting,
192
+ selectionEnabled,
193
+ selection?.checkboxColumnWidth,
194
+ isCollapsible,
195
+ expandable?.toggleColumnWidth,
196
+ ]);
156
197
  const tableConfig = useMemo(() => {
198
+ const tableState = {};
199
+ if (sorting) {
200
+ tableState.sorting = sorting.sortState || sortingState;
201
+ }
202
+ if (filtering) {
203
+ tableState.globalFilter = globalFilterState;
204
+ }
205
+ if (isCollapsible) {
206
+ tableState.expanded = expandable?.expandedState ?? expandedState;
207
+ }
157
208
  const baseConfig = {
158
209
  data: rows,
159
210
  columns: tableColumns,
160
211
  getCoreRowModel: getCoreRowModel(),
161
212
  ...(sorting && {
162
- state: {
163
- sorting: sorting.sortState || sortingState,
164
- ...(filtering && { globalFilter: globalFilterState }),
165
- },
166
213
  onSortingChange: mode === 'server'
167
214
  ? (updater) => {
168
215
  const newSort = typeof updater === 'function'
@@ -182,10 +229,6 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
182
229
  getSortedRowModel: mode === 'client' ? getSortedRowModel() : undefined,
183
230
  }),
184
231
  ...(filtering && {
185
- state: {
186
- ...(sorting && { sorting: sorting.sortState || sortingState }),
187
- globalFilter: globalFilterState,
188
- },
189
232
  onGlobalFilterChange: (updater) => {
190
233
  const newFilter = typeof updater === 'function'
191
234
  ? updater(globalFilterState)
@@ -198,6 +241,20 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
198
241
  globalFilterFn: filtering.globalFilterFn,
199
242
  getFilteredRowModel: mode === 'client' ? getFilteredRowModel() : undefined,
200
243
  }),
244
+ ...(isCollapsible && {
245
+ getExpandedRowModel: getExpandedRowModel(),
246
+ getRowCanExpand: expandable?.getRowCanExpand
247
+ ? (row) => expandable.getRowCanExpand(row.original)
248
+ : () => true,
249
+ onExpandedChange: (updater) => {
250
+ const newExpanded = typeof updater === 'function'
251
+ ? updater(expandedState)
252
+ : updater;
253
+ setExpandedState(newExpanded);
254
+ expandable?.onExpandedChange?.(newExpanded);
255
+ },
256
+ }),
257
+ ...(Object.keys(tableState).length > 0 && { state: tableState }),
201
258
  };
202
259
  if (!pagination) {
203
260
  return baseConfig;
@@ -243,6 +300,9 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
243
300
  sortingState,
244
301
  filtering,
245
302
  globalFilterState,
303
+ isCollapsible,
304
+ expandable,
305
+ expandedState,
246
306
  mode,
247
307
  ]);
248
308
  const table = useReactTable(tableConfig);
@@ -436,9 +496,14 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
436
496
  return null;
437
497
  }
438
498
  const pageCount = table.getPageCount();
439
- const rowCount = mode === 'server' && !pagination.infinite
440
- ? pagination.totalRowCount || rows.length
441
- : rows.length;
499
+ const rowCount = resolvePaginationRowCount({
500
+ mode,
501
+ isInfinite: !!pagination.infinite,
502
+ hasFiltering: !!filtering,
503
+ totalRowCount: pagination.totalRowCount,
504
+ rowsLength: rows.length,
505
+ filteredRowsLength: table.getFilteredRowModel().rows.length,
506
+ });
442
507
  const api = {
443
508
  pageIndex: tablePaginationState.pageIndex,
444
509
  pageSize: tablePaginationState.pageSize,
@@ -557,23 +622,32 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
557
622
  }
558
623
  } }));
559
624
  }, [getSelectionRowId, isRowSelected, setRowSelection]);
625
+ const renderExpandToggle = useCallback((row, rowIndex) => {
626
+ const isExpanded = row.getIsExpanded();
627
+ const canExpand = row.getCanExpand();
628
+ const expandedContentId = `datatable-expanded-content-${rowIndex}`;
629
+ if (!canExpand) {
630
+ return null;
631
+ }
632
+ return (React.createElement(NvIconbutton, { "data-testid": `datatable-row-${rowIndex}-expand-toggle`, "aria-expanded": isExpanded ? 'true' : 'false', "aria-controls": expandedContentId, "aria-label": isExpanded
633
+ ? `Collapse row ${rowIndex + 1}`
634
+ : `Expand row ${rowIndex + 1}`, onClick: (e) => {
635
+ e.stopPropagation();
636
+ row.toggleExpanded();
637
+ }, name: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm", emphasis: "lower" }));
638
+ }, []);
560
639
  return (React.createElement(React.Fragment, null,
561
640
  filteringAPI && renderFiltering && renderFiltering(filteringAPI),
562
641
  React.createElement(NvTable, { ...htmlProps },
563
642
  React.createElement("table", { "data-row-click-enabled": rowClickEnabled ? 'true' : 'false' },
564
643
  React.createElement("thead", { "data-sticky-top": stickyHeader ? 'true' : undefined }, table.getHeaderGroups().map((headerGroup) => (React.createElement("tr", { key: headerGroup.id }, headerGroup.headers.map((header) => {
565
644
  const isSelectionHeader = header.id === '__selection__';
645
+ const isExpandHeader = header.id === '__expand__';
646
+ const isCombinedHeader = header.id === '__selection_expand__';
647
+ const isSpecialHeader = isSelectionHeader || isExpandHeader || isCombinedHeader;
566
648
  const canSort = header.column.getCanSort();
567
649
  const sortDirection = header.column.getIsSorted();
568
- return (React.createElement("th", { key: header.id, "data-testid": isSelectionHeader
569
- ? 'datatable-header-selection'
570
- : `datatable-header-${header.id}`, style: {
571
- width: header.column.columnDef.size + 'px',
572
- }, "data-no-resize": isSelectionHeader
573
- ? true
574
- : header.column.columnDef.enableResizing
575
- ? null
576
- : true }, isSelectionHeader ? (selectionMode === 'multiple' && selectAllEnabled ? (React.createElement("div", { className: "flex items-center justify-center" },
650
+ const renderSelectAllCheckbox = selectionMode === 'multiple' && selectAllEnabled ? (React.createElement("div", { className: "flex items-center justify-center" },
577
651
  React.createElement(NvFieldcheckbox, { "data-testid": "datatable-select-all-checkbox", checked: selectAllState === 'checked', indeterminate: selectAllState === 'indeterminate', label: "Select all rows", hideLabel: true, onPointerDownCapture: () => {
578
652
  if (selectAllIntentResetTimerRef.current) {
579
653
  clearTimeout(selectAllIntentResetTimerRef.current);
@@ -591,7 +665,20 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
591
665
  finally {
592
666
  selectAllIntentRef.current = false;
593
667
  }
594
- } }))) : null) : (React.createElement(NvTableheader, { sortable: canSort ? true : undefined, sortDirection: sortDirection || (canSort ? 'none' : undefined), onSortDirectionChanged: canSort
668
+ } }))) : null;
669
+ return (React.createElement("th", { key: header.id, "data-testid": isSelectionHeader
670
+ ? 'datatable-header-selection'
671
+ : isExpandHeader
672
+ ? 'datatable-header-expand'
673
+ : isCombinedHeader
674
+ ? 'datatable-header-selection-expand'
675
+ : `datatable-header-${header.id}`, style: {
676
+ width: header.column.columnDef.size + 'px',
677
+ }, "data-no-resize": isSpecialHeader
678
+ ? true
679
+ : header.column.columnDef.enableResizing
680
+ ? null
681
+ : true }, isSelectionHeader ? (renderSelectAllCheckbox) : isCombinedHeader ? (renderSelectAllCheckbox) : isExpandHeader ? null : (React.createElement(NvTableheader, { sortable: canSort ? true : undefined, sortDirection: sortDirection || (canSort ? 'none' : undefined), onSortDirectionChanged: canSort
595
682
  ? header.column.getToggleSortingHandler()
596
683
  : undefined }, header.isPlaceholder
597
684
  ? null
@@ -600,28 +687,58 @@ function NvDatatable({ mode = 'client', columns, rows, pagination, sorting, filt
600
687
  React.createElement("tbody", null, tableRows.map((row, index) => {
601
688
  const isLastRow = isInfiniteScroll && index === tableRows.length - 1;
602
689
  const stableRowKey = getSelectionRowId(row.original) || row.id;
603
- return (React.createElement("tr", { key: stableRowKey, "data-testid": `datatable-row-${index}`, ref: isLastRow ? lastRowRef : undefined, className: rowClickEnabled ? 'nv-datatable-row-clickable' : undefined, tabIndex: rowClickEnabled ? 0 : undefined, onClick: rowClickEnabled
604
- ? () => onRowClick?.({ row: row.original, rowIndex: index })
605
- : undefined, onKeyDown: rowClickEnabled
606
- ? (event) => {
607
- if (event.currentTarget !== event.target) {
608
- return;
609
- }
610
- if (event.key === 'Enter' || event.key === ' ') {
611
- event.preventDefault();
612
- onRowClick?.({
613
- row: row.original,
614
- rowIndex: index,
615
- });
690
+ const expandedContent = hasExpandable
691
+ ? expandable.renderExpandedContent({
692
+ row: row.original,
693
+ rowIndex: index,
694
+ })
695
+ : null;
696
+ const hasContent = expandedContent != null;
697
+ const showExpanded = shouldShowExpandedContent({
698
+ hasContent,
699
+ isCollapsible,
700
+ isExpanded: isCollapsible ? row.getIsExpanded() : false,
701
+ });
702
+ const expandedContentId = `datatable-expanded-content-${index}`;
703
+ const totalColumnCount = row.getVisibleCells().length;
704
+ return (React.createElement(React.Fragment, { key: stableRowKey },
705
+ React.createElement("tr", { "data-testid": `datatable-row-${index}`, "data-selected": isRowSelected(row.original) ? '' : undefined, ref: isLastRow ? lastRowRef : undefined, className: rowClickEnabled ? 'nv-datatable-row-clickable' : undefined, tabIndex: rowClickEnabled ? 0 : undefined, onClick: rowClickEnabled
706
+ ? () => onRowClick?.({
707
+ row: row.original,
708
+ rowIndex: index,
709
+ })
710
+ : undefined, onKeyDown: rowClickEnabled
711
+ ? (event) => {
712
+ if (event.currentTarget !== event.target) {
713
+ return;
714
+ }
715
+ if (event.key === 'Enter' || event.key === ' ') {
716
+ event.preventDefault();
717
+ onRowClick?.({
718
+ row: row.original,
719
+ rowIndex: index,
720
+ });
721
+ }
616
722
  }
617
- }
618
- : undefined }, row.getVisibleCells().map((cell) => (React.createElement("td", { key: cell.id, "data-testid": cell.column.id === '__selection__'
619
- ? 'datatable-cell-selection'
620
- : `datatable-cell-${cell.column.id}`, onClick: cell.column.id === '__selection__'
621
- ? (event) => event.stopPropagation()
622
- : undefined }, cell.column.id === '__selection__'
623
- ? renderRowSelectionCheckbox(row.original, index)
624
- : flexRender(cell.column.columnDef.cell, cell.getContext()))))));
723
+ : undefined }, row.getVisibleCells().map((cell) => {
724
+ const isCombinedCell = cell.column.id === '__selection_expand__';
725
+ const isSelectionCell = cell.column.id === '__selection__';
726
+ const isExpandCell = cell.column.id === '__expand__';
727
+ return (React.createElement("td", { key: cell.id, "data-testid": isSelectionCell
728
+ ? 'datatable-cell-selection'
729
+ : isExpandCell
730
+ ? 'datatable-cell-expand'
731
+ : isCombinedCell
732
+ ? 'datatable-cell-selection-expand'
733
+ : `datatable-cell-${cell.column.id}`, onClick: isSelectionCell || isExpandCell || isCombinedCell
734
+ ? (event) => event.stopPropagation()
735
+ : undefined }, isSelectionCell ? (renderRowSelectionCheckbox(row.original, index)) : isExpandCell ? (renderExpandToggle(row, index)) : isCombinedCell ? (React.createElement("div", { className: "flex items-center justify-center gap-1" },
736
+ renderRowSelectionCheckbox(row.original, index),
737
+ renderExpandToggle(row, index))) : (flexRender(cell.column.columnDef.cell, cell.getContext()))));
738
+ })),
739
+ showExpanded && (React.createElement("tr", { className: "nv-datatable-expanded-row", "data-testid": `datatable-expanded-row-${index}`, id: expandedContentId, role: "region", "aria-label": `Expanded content for row ${index + 1}` },
740
+ React.createElement("td", { colSpan: totalColumnCount },
741
+ React.createElement("div", { className: "nv-datatable-expanded-content" }, expandedContent))))));
625
742
  })))),
626
743
  paginationAPI &&
627
744
  (renderPagination
@@ -0,0 +1,8 @@
1
+ export function shouldShowExpandedContent(params) {
2
+ const { hasContent, isCollapsible, isExpanded } = params;
3
+ if (!hasContent)
4
+ return false;
5
+ if (!isCollapsible)
6
+ return true;
7
+ return isExpanded;
8
+ }
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { shouldShowExpandedContent } from './expandState';
3
+ describe('expandState', () => {
4
+ describe('shouldShowExpandedContent', () => {
5
+ it('returns false when there is no content', () => {
6
+ expect(shouldShowExpandedContent({
7
+ hasContent: false,
8
+ isCollapsible: false,
9
+ isExpanded: false,
10
+ })).toBe(false);
11
+ });
12
+ it('returns false when there is no content even if expanded', () => {
13
+ expect(shouldShowExpandedContent({
14
+ hasContent: false,
15
+ isCollapsible: true,
16
+ isExpanded: true,
17
+ })).toBe(false);
18
+ });
19
+ it('returns true when has content and not collapsible', () => {
20
+ expect(shouldShowExpandedContent({
21
+ hasContent: true,
22
+ isCollapsible: false,
23
+ isExpanded: false,
24
+ })).toBe(true);
25
+ });
26
+ it('returns false when collapsible and not expanded', () => {
27
+ expect(shouldShowExpandedContent({
28
+ hasContent: true,
29
+ isCollapsible: true,
30
+ isExpanded: false,
31
+ })).toBe(false);
32
+ });
33
+ it('returns true when collapsible and expanded', () => {
34
+ expect(shouldShowExpandedContent({
35
+ hasContent: true,
36
+ isCollapsible: true,
37
+ isExpanded: true,
38
+ })).toBe(true);
39
+ });
40
+ });
41
+ });
@@ -0,0 +1,9 @@
1
+ export function resolvePaginationRowCount(opts) {
2
+ if (opts.mode === 'server' && !opts.isInfinite) {
3
+ return opts.totalRowCount || opts.rowsLength;
4
+ }
5
+ if (opts.mode === 'client' && opts.hasFiltering) {
6
+ return opts.filteredRowsLength;
7
+ }
8
+ return opts.rowsLength;
9
+ }
@@ -0,0 +1,84 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolvePaginationRowCount } from './paginationState';
3
+ describe('resolvePaginationRowCount', () => {
4
+ it('server non-infinite returns totalRowCount when provided', () => {
5
+ expect(resolvePaginationRowCount({
6
+ mode: 'server',
7
+ isInfinite: false,
8
+ hasFiltering: false,
9
+ totalRowCount: 123,
10
+ rowsLength: 10,
11
+ filteredRowsLength: 10,
12
+ })).toBe(123);
13
+ });
14
+ it('server non-infinite falls back to rows.length when totalRowCount is missing', () => {
15
+ expect(resolvePaginationRowCount({
16
+ mode: 'server',
17
+ isInfinite: false,
18
+ hasFiltering: false,
19
+ totalRowCount: undefined,
20
+ rowsLength: 10,
21
+ filteredRowsLength: 10,
22
+ })).toBe(10);
23
+ });
24
+ it('server non-infinite falls back to rows.length when totalRowCount is 0', () => {
25
+ expect(resolvePaginationRowCount({
26
+ mode: 'server',
27
+ isInfinite: false,
28
+ hasFiltering: false,
29
+ totalRowCount: 0,
30
+ rowsLength: 5,
31
+ filteredRowsLength: 5,
32
+ })).toBe(5);
33
+ });
34
+ it('server infinite bypasses the server branch and uses rows.length', () => {
35
+ expect(resolvePaginationRowCount({
36
+ mode: 'server',
37
+ isInfinite: true,
38
+ hasFiltering: false,
39
+ totalRowCount: 999,
40
+ rowsLength: 42,
41
+ filteredRowsLength: 42,
42
+ })).toBe(42);
43
+ });
44
+ it('client with filtering returns the filtered row count', () => {
45
+ expect(resolvePaginationRowCount({
46
+ mode: 'client',
47
+ isInfinite: false,
48
+ hasFiltering: true,
49
+ totalRowCount: undefined,
50
+ rowsLength: 100,
51
+ filteredRowsLength: 7,
52
+ })).toBe(7);
53
+ });
54
+ it('client with filtering returns 0 when all rows are filtered out', () => {
55
+ expect(resolvePaginationRowCount({
56
+ mode: 'client',
57
+ isInfinite: false,
58
+ hasFiltering: true,
59
+ totalRowCount: undefined,
60
+ rowsLength: 100,
61
+ filteredRowsLength: 0,
62
+ })).toBe(0);
63
+ });
64
+ it('client without filtering returns rows.length', () => {
65
+ expect(resolvePaginationRowCount({
66
+ mode: 'client',
67
+ isInfinite: false,
68
+ hasFiltering: false,
69
+ totalRowCount: undefined,
70
+ rowsLength: 25,
71
+ filteredRowsLength: 25,
72
+ })).toBe(25);
73
+ });
74
+ it('client with filtering takes precedence over rows.length even when filtered count is larger', () => {
75
+ expect(resolvePaginationRowCount({
76
+ mode: 'client',
77
+ isInfinite: false,
78
+ hasFiltering: true,
79
+ totalRowCount: undefined,
80
+ rowsLength: 3,
81
+ filteredRowsLength: 10,
82
+ })).toBe(10);
83
+ });
84
+ });
@@ -58,8 +58,10 @@ import { NvSidebarnavitem as NvSidebarnavitemElement, defineCustomElement as def
58
58
  import { NvSidebarnavsubitem as NvSidebarnavsubitemElement, defineCustomElement as defineNvSidebarnavsubitem } from "@nova-design-system/nova-webcomponents/dist/components/nv-sidebarnavsubitem.js";
59
59
  import { NvSplit as NvSplitElement, defineCustomElement as defineNvSplit } from "@nova-design-system/nova-webcomponents/dist/components/nv-split.js";
60
60
  import { NvStack as NvStackElement, defineCustomElement as defineNvStack } from "@nova-design-system/nova-webcomponents/dist/components/nv-stack.js";
61
+ import { NvStatusindicator as NvStatusindicatorElement, defineCustomElement as defineNvStatusindicator } from "@nova-design-system/nova-webcomponents/dist/components/nv-statusindicator.js";
61
62
  import { NvTable as NvTableElement, defineCustomElement as defineNvTable } from "@nova-design-system/nova-webcomponents/dist/components/nv-table.js";
62
63
  import { NvTableheader as NvTableheaderElement, defineCustomElement as defineNvTableheader } from "@nova-design-system/nova-webcomponents/dist/components/nv-tableheader.js";
64
+ import { NvTag as NvTagElement, defineCustomElement as defineNvTag } from "@nova-design-system/nova-webcomponents/dist/components/nv-tag.js";
63
65
  import { NvTimetest as NvTimetestElement, defineCustomElement as defineNvTimetest } from "@nova-design-system/nova-webcomponents/dist/components/nv-timetest.js";
64
66
  import { NvToggle as NvToggleElement, defineCustomElement as defineNvToggle } from "@nova-design-system/nova-webcomponents/dist/components/nv-toggle.js";
65
67
  import { NvTogglebutton as NvTogglebuttonElement, defineCustomElement as defineNvTogglebutton } from "@nova-design-system/nova-webcomponents/dist/components/nv-togglebutton.js";
@@ -276,6 +278,7 @@ export const NvFielddropdown = createComponent({
276
278
  events: {
277
279
  onValueChanged: 'valueChanged',
278
280
  onFilterTextChanged: 'filterTextChanged',
281
+ onCleared: 'cleared',
279
282
  onOpenChanged: 'openChanged',
280
283
  onDropdownItemSelected: 'dropdownItemSelected'
281
284
  },
@@ -330,7 +333,10 @@ export const NvFieldselect = createComponent({
330
333
  tagName: 'nv-fieldselect',
331
334
  elementClass: NvFieldselectElement,
332
335
  react: React,
333
- events: { onValueChanged: 'valueChanged' },
336
+ events: {
337
+ onValueChanged: 'valueChanged',
338
+ onCleared: 'cleared'
339
+ },
334
340
  defineCustomElement: defineNvFieldselect
335
341
  });
336
342
  export const NvFieldslider = createComponent({
@@ -452,7 +458,10 @@ export const NvSidebar = createComponent({
452
458
  tagName: 'nv-sidebar',
453
459
  elementClass: NvSidebarElement,
454
460
  react: React,
455
- events: { onOpenChanged: 'openChanged' },
461
+ events: {
462
+ onOpenChanged: 'openChanged',
463
+ onWidthChanged: 'widthChanged'
464
+ },
456
465
  defineCustomElement: defineNvSidebar
457
466
  });
458
467
  export const NvSidebarcontent = createComponent({
@@ -525,6 +534,13 @@ export const NvStack = createComponent({
525
534
  events: {},
526
535
  defineCustomElement: defineNvStack
527
536
  });
537
+ export const NvStatusindicator = createComponent({
538
+ tagName: 'nv-statusindicator',
539
+ elementClass: NvStatusindicatorElement,
540
+ react: React,
541
+ events: {},
542
+ defineCustomElement: defineNvStatusindicator
543
+ });
528
544
  export const NvTable = createComponent({
529
545
  tagName: 'nv-table',
530
546
  elementClass: NvTableElement,
@@ -539,6 +555,13 @@ export const NvTableheader = createComponent({
539
555
  events: { onSortDirectionChanged: 'sortDirectionChanged' },
540
556
  defineCustomElement: defineNvTableheader
541
557
  });
558
+ export const NvTag = createComponent({
559
+ tagName: 'nv-tag',
560
+ elementClass: NvTagElement,
561
+ react: React,
562
+ events: { onDismissed: 'dismissed' },
563
+ defineCustomElement: defineNvTag
564
+ });
542
565
  export const NvTimetest = createComponent({
543
566
  tagName: 'nv-timetest',
544
567
  elementClass: NvTimetestElement,
@@ -396,6 +396,7 @@ export const NvFielddropdown = createComponent({
396
396
  maxHeight: 'max-height',
397
397
  emptyResult: 'empty-result',
398
398
  filterable: 'filterable',
399
+ clearable: 'clearable',
399
400
  openOnSelect: 'open-on-select',
400
401
  controlledFilter: 'controlled-filter',
401
402
  debounceDelay: 'debounce-delay',
@@ -575,6 +576,7 @@ export const NvFieldselect = createComponent({
575
576
  errorDescription: 'error-description',
576
577
  success: 'success',
577
578
  multiple: 'multiple',
579
+ clearable: 'clearable',
578
580
  value: 'value',
579
581
  displayValue: 'display-value',
580
582
  autofocus: 'autofocus',
@@ -879,7 +881,11 @@ export const NvSidebar = createComponent({
879
881
  open: 'open',
880
882
  activePath: 'active-path',
881
883
  notificationIntention: 'notification-intention',
882
- notificationEmphasis: 'notification-emphasis'
884
+ notificationEmphasis: 'notification-emphasis',
885
+ resizable: 'resizable',
886
+ minWidth: 'min-width',
887
+ maxWidth: 'max-width',
888
+ width: 'width'
883
889
  },
884
890
  hydrateModule: import('@nova-design-system/nova-webcomponents/hydrate'),
885
891
  clientModule: clientComponents.NvSidebar,
@@ -974,6 +980,17 @@ export const NvStack = createComponent({
974
980
  clientModule: clientComponents.NvStack,
975
981
  serializeShadowRoot
976
982
  });
983
+ export const NvStatusindicator = createComponent({
984
+ tagName: 'nv-statusindicator',
985
+ properties: {
986
+ status: 'status',
987
+ emphasis: 'emphasis',
988
+ label: 'label'
989
+ },
990
+ hydrateModule: import('@nova-design-system/nova-webcomponents/hydrate'),
991
+ clientModule: clientComponents.NvStatusindicator,
992
+ serializeShadowRoot
993
+ });
977
994
  export const NvTable = createComponent({
978
995
  tagName: 'nv-table',
979
996
  properties: {},
@@ -991,6 +1008,21 @@ export const NvTableheader = createComponent({
991
1008
  clientModule: clientComponents.NvTableheader,
992
1009
  serializeShadowRoot
993
1010
  });
1011
+ export const NvTag = createComponent({
1012
+ tagName: 'nv-tag',
1013
+ properties: {
1014
+ color: 'color',
1015
+ label: 'label',
1016
+ dismissible: 'dismissible',
1017
+ href: 'href',
1018
+ target: 'target',
1019
+ rel: 'rel',
1020
+ disabled: 'disabled'
1021
+ },
1022
+ hydrateModule: import('@nova-design-system/nova-webcomponents/hydrate'),
1023
+ clientModule: clientComponents.NvTag,
1024
+ serializeShadowRoot
1025
+ });
994
1026
  export const NvTimetest = createComponent({
995
1027
  tagName: 'nv-timetest',
996
1028
  properties: { lastChange: 'last-change' },
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { NvDatatableProps } from './types';
3
- declare function NvDatatable<T>({ mode, columns, rows, pagination, sorting, filtering, selection, renderPagination, renderFiltering, onRowClick, stickyHeader, ...htmlProps }: NvDatatableProps<T>): React.JSX.Element;
3
+ declare function NvDatatable<T>({ mode, columns, rows, pagination, sorting, filtering, selection, renderPagination, renderFiltering, onRowClick, expandable, stickyHeader, ...htmlProps }: NvDatatableProps<T>): React.JSX.Element;
4
4
  declare namespace NvDatatable {
5
5
  var displayName: string;
6
6
  }
@@ -0,0 +1,5 @@
1
+ export declare function shouldShowExpandedContent(params: {
2
+ hasContent: boolean;
3
+ isCollapsible: boolean;
4
+ isExpanded: boolean;
5
+ }): boolean;
@@ -0,0 +1,9 @@
1
+ export interface ResolvePaginationRowCountOptions {
2
+ mode: 'client' | 'server';
3
+ isInfinite: boolean;
4
+ hasFiltering: boolean;
5
+ totalRowCount: number | undefined;
6
+ rowsLength: number;
7
+ filteredRowsLength: number;
8
+ }
9
+ export declare function resolvePaginationRowCount(opts: ResolvePaginationRowCountOptions): number;