@wallarm-org/design-system 1.9.0 → 1.10.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.
- package/dist/components/DropdownMenu/DropdownMenuContent.js +7 -2
- package/dist/components/Table/TableContext/TableProvider.js +3 -1
- package/dist/components/Table/TableContext/types.d.ts +2 -1
- package/dist/components/Table/TableSettingsMenu/TableSettingsMenu.js +45 -6
- package/dist/components/Table/TableSettingsMenu/TableSettingsMenuItem.js +4 -3
- package/dist/components/Table/TableSettingsMenu/TableSettingsMenuSearch.js +3 -0
- package/dist/components/Table/index.d.ts +1 -1
- package/dist/components/Table/types.d.ts +20 -0
- package/dist/metadata/components.json +13 -2
- package/package.json +1 -1
|
@@ -12,9 +12,14 @@ const DropdownMenuContent = ({ className, children, ref, ...props })=>{
|
|
|
12
12
|
const testId = useTestId('content');
|
|
13
13
|
const cascadeBase = useTestId();
|
|
14
14
|
const childArray = Children.toArray(children);
|
|
15
|
-
const
|
|
15
|
+
const isInputChild = (child)=>{
|
|
16
|
+
if (!/*#__PURE__*/ isValidElement(child)) return false;
|
|
17
|
+
if (child.type === DropdownMenuInput) return true;
|
|
18
|
+
return 'function' == typeof child.type && 'input' === child.type.dropdownMenuSlot;
|
|
19
|
+
};
|
|
20
|
+
const inputChildren = childArray.filter(isInputChild);
|
|
16
21
|
const footerChildren = childArray.filter((child)=>/*#__PURE__*/ isValidElement(child) && child.type === DropdownMenuFooter);
|
|
17
|
-
const menuChildren = childArray.filter((child)
|
|
22
|
+
const menuChildren = childArray.filter((child)=>!isInputChild(child) && !/*#__PURE__*/ (isValidElement(child) && child.type === DropdownMenuFooter));
|
|
18
23
|
return /*#__PURE__*/ jsx(Portal, {
|
|
19
24
|
children: /*#__PURE__*/ jsx(Menu.Positioner, {
|
|
20
25
|
children: /*#__PURE__*/ jsxs(Menu.Content, {
|
|
@@ -15,7 +15,7 @@ const toTanStackPinning = (p, pinnedLeft)=>({
|
|
|
15
15
|
end: p?.right ?? []
|
|
16
16
|
});
|
|
17
17
|
const TableProvider = (props)=>{
|
|
18
|
-
const { data, columns, isLoading = false, isLoadingPrevious = false, skeletonCount = TABLE_SKELETON_ROWS, children, getRowId, sorting: sortingProp, onSortingChange, manualSorting = false, rowSelection: rowSelectionProp, onRowSelectionChange, columnSizing: columnSizingProp, onColumnSizingChange, columnPinning: columnPinningProp, onColumnPinningChange, columnOrder: columnOrderProp, onColumnOrderChange, grouping: groupingProp, onGroupingChange, expanded: expandedProp, onExpandedChange, renderGroupRow, getSubRows, renderExpandedRow, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, defaultColumnVisibility, defaultColumnOrder, virtualized, estimateRowHeight, overscan = TABLE_VIRTUALIZATION_OVERSCAN, onEndReached, onEndReachedThreshold, onStartReached, onStartReachedThreshold, initialScrollToRowId, onMasterCellClick, activeRowId: activeRowIdProp, onSettingsOpenChange } = props;
|
|
18
|
+
const { data, columns, isLoading = false, isLoadingPrevious = false, skeletonCount = TABLE_SKELETON_ROWS, children, getRowId, sorting: sortingProp, onSortingChange, manualSorting = false, rowSelection: rowSelectionProp, onRowSelectionChange, columnSizing: columnSizingProp, onColumnSizingChange, columnPinning: columnPinningProp, onColumnPinningChange, columnOrder: columnOrderProp, onColumnOrderChange, grouping: groupingProp, onGroupingChange, expanded: expandedProp, onExpandedChange, renderGroupRow, getSubRows, renderExpandedRow, columnVisibility: columnVisibilityProp, onColumnVisibilityChange, defaultColumnVisibility, defaultColumnOrder, columnGroups, virtualized, estimateRowHeight, overscan = TABLE_VIRTUALIZATION_OVERSCAN, onEndReached, onEndReachedThreshold, onStartReached, onStartReachedThreshold, initialScrollToRowId, onMasterCellClick, activeRowId: activeRowIdProp, onSettingsOpenChange } = props;
|
|
19
19
|
const masterCellActiveRowId = activeRowIdProp ?? null;
|
|
20
20
|
const sortingEnabled = !!onSortingChange;
|
|
21
21
|
const selectionEnabled = !!onRowSelectionChange;
|
|
@@ -201,6 +201,7 @@ const TableProvider = (props)=>{
|
|
|
201
201
|
allLeafColumns,
|
|
202
202
|
defaultColumnVisibility,
|
|
203
203
|
defaultColumnOrder,
|
|
204
|
+
columnGroups,
|
|
204
205
|
setColumnOrder,
|
|
205
206
|
alwaysPinnedLeft,
|
|
206
207
|
masterColumnId,
|
|
@@ -236,6 +237,7 @@ const TableProvider = (props)=>{
|
|
|
236
237
|
allLeafColumns,
|
|
237
238
|
defaultColumnVisibility,
|
|
238
239
|
defaultColumnOrder,
|
|
240
|
+
columnGroups,
|
|
239
241
|
setColumnOrder,
|
|
240
242
|
alwaysPinnedLeft,
|
|
241
243
|
masterColumnId,
|
|
@@ -2,7 +2,7 @@ import type { ReactNode, RefObject } from 'react';
|
|
|
2
2
|
import type { Column, ColumnVisibilityState, ReactTable, Row, RowData } from '@tanstack/react-table';
|
|
3
3
|
import type { Virtualizer } from '@tanstack/react-virtual';
|
|
4
4
|
import type { DSTableFeatures } from '../lib';
|
|
5
|
-
import type { TableProps, TableVirtualized } from '../types';
|
|
5
|
+
import type { TableColumnGroup, TableProps, TableVirtualized } from '../types';
|
|
6
6
|
/**
|
|
7
7
|
* Union covers both virtualizer flavors used by the Table (`useVirtualizer`
|
|
8
8
|
* with HTMLElement scroll container and `useWindowVirtualizer` with Window).
|
|
@@ -29,6 +29,7 @@ export interface TableContextValue<T extends RowData> {
|
|
|
29
29
|
allLeafColumns: Column<DSTableFeatures, T, unknown>[];
|
|
30
30
|
defaultColumnVisibility?: ColumnVisibilityState;
|
|
31
31
|
defaultColumnOrder?: string[];
|
|
32
|
+
columnGroups?: TableColumnGroup[];
|
|
32
33
|
setColumnOrder: (order: string[]) => void;
|
|
33
34
|
lastSelectedRowIndexRef: RefObject<number | null>;
|
|
34
35
|
alwaysPinnedLeft: string[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cloneElement, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { createPortal } from "react-dom";
|
|
4
4
|
import { DndContext, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
|
|
@@ -24,7 +24,7 @@ const TableSettingsMenu = ({ 'data-testid': testIdProp, children, ...rest })=>{
|
|
|
24
24
|
const testId = useTestId('settings-menu', testIdProp);
|
|
25
25
|
const { anchorNode } = useTableSettingsMenuContext();
|
|
26
26
|
const ctx = useTableContext();
|
|
27
|
-
const { table, alwaysPinnedLeft, masterColumnId, onSettingsOpenChange } = ctx;
|
|
27
|
+
const { table, alwaysPinnedLeft, masterColumnId, columnGroups, onSettingsOpenChange } = ctx;
|
|
28
28
|
const hasTextDescription = table.getAllLeafColumns().some((col)=>col.columnDef.meta?.description?.type === 'text');
|
|
29
29
|
const [search, setSearch] = useState('');
|
|
30
30
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
@@ -59,6 +59,30 @@ const TableSettingsMenu = ({ 'data-testid': testIdProp, children, ...rest })=>{
|
|
|
59
59
|
allColumns,
|
|
60
60
|
search
|
|
61
61
|
]);
|
|
62
|
+
const labeledSections = useMemo(()=>{
|
|
63
|
+
if (!columnGroups) return null;
|
|
64
|
+
const byId = new Map(filteredColumns.map((col)=>[
|
|
65
|
+
col.id,
|
|
66
|
+
col
|
|
67
|
+
]));
|
|
68
|
+
const claimed = new Set();
|
|
69
|
+
const sections = columnGroups.map((group)=>{
|
|
70
|
+
const cols = group.columns.map((id)=>byId.get(id)).filter((col)=>null != col);
|
|
71
|
+
for (const col of cols)claimed.add(col.id);
|
|
72
|
+
return {
|
|
73
|
+
label: group.label,
|
|
74
|
+
cols
|
|
75
|
+
};
|
|
76
|
+
}).filter((section)=>section.cols.length > 0);
|
|
77
|
+
const ungrouped = filteredColumns.filter((col)=>!claimed.has(col.id));
|
|
78
|
+
return {
|
|
79
|
+
sections,
|
|
80
|
+
ungrouped
|
|
81
|
+
};
|
|
82
|
+
}, [
|
|
83
|
+
columnGroups,
|
|
84
|
+
filteredColumns
|
|
85
|
+
]);
|
|
62
86
|
const { pinnedColumns, unpinnedColumns } = useMemo(()=>{
|
|
63
87
|
const pinned = [];
|
|
64
88
|
const unpinned = [];
|
|
@@ -147,15 +171,30 @@ const TableSettingsMenu = ({ 'data-testid': testIdProp, children, ...rest })=>{
|
|
|
147
171
|
}),
|
|
148
172
|
/*#__PURE__*/ jsxs(DropdownMenuContent, {
|
|
149
173
|
ref: contentRef,
|
|
150
|
-
className: cn('min-w-256
|
|
151
|
-
style:
|
|
174
|
+
className: cn('min-w-256'),
|
|
175
|
+
style: {
|
|
176
|
+
maxHeight: 'min(430px, var(--available-height))',
|
|
152
177
|
width: lockedWidth
|
|
153
|
-
}
|
|
178
|
+
},
|
|
154
179
|
children: [
|
|
155
180
|
searchOverride ?? /*#__PURE__*/ jsx(TableSettingsMenuSearch, {}),
|
|
156
181
|
/*#__PURE__*/ jsx(VStack, {
|
|
157
182
|
gap: 1,
|
|
158
|
-
children: /*#__PURE__*/
|
|
183
|
+
children: labeledSections ? /*#__PURE__*/ jsxs(Fragment, {
|
|
184
|
+
children: [
|
|
185
|
+
labeledSections.sections.map((section)=>/*#__PURE__*/ jsxs("div", {
|
|
186
|
+
className: "flex flex-col gap-1",
|
|
187
|
+
children: [
|
|
188
|
+
/*#__PURE__*/ jsx(DropdownMenuLabel, {
|
|
189
|
+
sticky: true,
|
|
190
|
+
children: section.label
|
|
191
|
+
}),
|
|
192
|
+
section.cols.map(renderColumnItem)
|
|
193
|
+
]
|
|
194
|
+
}, section.label)),
|
|
195
|
+
labeledSections.ungrouped.map(renderColumnItem)
|
|
196
|
+
]
|
|
197
|
+
}) : /*#__PURE__*/ jsx(DndContext, {
|
|
159
198
|
sensors: sensors,
|
|
160
199
|
collisionDetection: closestCenter,
|
|
161
200
|
onDragEnd: handleDragEnd,
|
|
@@ -7,7 +7,8 @@ import { useTestId } from "../../../utils/testId.js";
|
|
|
7
7
|
import { Switch, SwitchControl, SwitchLabel } from "../../Switch/index.js";
|
|
8
8
|
import { useTableContext } from "../TableContext/index.js";
|
|
9
9
|
const TableSettingsMenuItem = ({ columnId, className, 'data-testid': testIdProp, ...rest })=>{
|
|
10
|
-
const { masterColumnId, columnDndEnabled, table } = useTableContext();
|
|
10
|
+
const { masterColumnId, columnDndEnabled, columnGroups, table } = useTableContext();
|
|
11
|
+
const grouped = !!columnGroups;
|
|
11
12
|
const slotTestId = useTestId('settings-menu-item');
|
|
12
13
|
const switchTestId = useTestId(`settings-menu-item-${columnId}`, testIdProp);
|
|
13
14
|
const column = table.getColumn(columnId);
|
|
@@ -29,10 +30,10 @@ const TableSettingsMenuItem = ({ columnId, className, 'data-testid': testIdProp,
|
|
|
29
30
|
ref: setNodeRef,
|
|
30
31
|
style: style,
|
|
31
32
|
"data-testid": slotTestId,
|
|
32
|
-
className: cn('relative flex items-center w-full rounded-6
|
|
33
|
+
className: cn('relative flex items-center w-full rounded-6 pr-8 py-6', grouped ? 'pl-8' : 'pl-20', 'hover:bg-states-primary-hover transition-colors', 'data-disabled:opacity-50 data-disabled:pointer-events-none'),
|
|
33
34
|
"data-disabled": isMasterColumn || void 0,
|
|
34
35
|
children: [
|
|
35
|
-
/*#__PURE__*/ jsx("span", {
|
|
36
|
+
!grouped && /*#__PURE__*/ jsx("span", {
|
|
36
37
|
className: cn('absolute left-4 top-1/2 -translate-y-1/2 cursor-grab active:cursor-grabbing text-icon-secondary hover:text-icon-primary data-disabled:opacity-50 data-disabled:pointer-events-none'),
|
|
37
38
|
...attributes,
|
|
38
39
|
...listeners,
|
|
@@ -14,4 +14,7 @@ const TableSettingsMenuSearch = ({ 'data-testid': testIdProp, placeholder = 'Sea
|
|
|
14
14
|
});
|
|
15
15
|
};
|
|
16
16
|
TableSettingsMenuSearch.displayName = 'TableSettingsMenuSearch';
|
|
17
|
+
Object.assign(TableSettingsMenuSearch, {
|
|
18
|
+
dropdownMenuSlot: 'input'
|
|
19
|
+
});
|
|
17
20
|
export { TableSettingsMenuSearch };
|
|
@@ -10,4 +10,4 @@ export { TableEmptyState } from './TableEmptyState';
|
|
|
10
10
|
export { TableScrollHandler, TableScrollHandlerLeft, type TableScrollHandlerLeftProps, type TableScrollHandlerProps, TableScrollHandlerRight, type TableScrollHandlerRightProps, } from './TableScrollHandler';
|
|
11
11
|
export { TableSettingsMenu, TableSettingsMenuItem, type TableSettingsMenuItemProps, type TableSettingsMenuProps, TableSettingsMenuReset, type TableSettingsMenuResetProps, TableSettingsMenuSearch, type TableSettingsMenuSearchProps, } from './TableSettingsMenu';
|
|
12
12
|
export { TableSortTrigger, type TableSortTriggerProps } from './TableSortTrigger';
|
|
13
|
-
export type { TableAccessorColumnDef, TableCellContext, TableColumnBase, TableColumnDef, TableColumnMeta, TableColumnPinningState, TableColumnSizingState, TableDisplayColumnDef, TableExpandedState, TableGroupingState, TableHandle, TableOnChangeFn, TableProps, TableRow, TableRowSelectionState, TableScrollToRowOptions, TableSortingState, TableUpdater, TableVirtualized, TableVisibilityState, } from './types';
|
|
13
|
+
export type { TableAccessorColumnDef, TableCellContext, TableColumnBase, TableColumnDef, TableColumnGroup, TableColumnMeta, TableColumnPinningState, TableColumnSizingState, TableDisplayColumnDef, TableExpandedState, TableGroupingState, TableHandle, TableOnChangeFn, TableProps, TableRow, TableRowSelectionState, TableScrollToRowOptions, TableSortingState, TableUpdater, TableVirtualized, TableVisibilityState, } from './types';
|
|
@@ -42,6 +42,19 @@ export type TableExpandedState = true | Record<string, boolean>;
|
|
|
42
42
|
export type TableGroupingState = string[];
|
|
43
43
|
/** Column visibility state: `{ [columnId]: boolean }` */
|
|
44
44
|
export type TableVisibilityState = Record<string, boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Labeled grouping for the column-settings menu. When `columnGroups` is passed
|
|
47
|
+
* to the Table, the settings menu renders these labeled sections (in the given
|
|
48
|
+
* order) instead of the default pinned/unpinned + drag-reorder list — the same
|
|
49
|
+
* flat, non-reorderable pattern the FilterInput field menu uses. `columns` are
|
|
50
|
+
* column ids; ids absent from every group fall into a trailing headerless
|
|
51
|
+
* section, and empty groups (or groups whose columns are all filtered out by
|
|
52
|
+
* search) render nothing.
|
|
53
|
+
*/
|
|
54
|
+
export interface TableColumnGroup {
|
|
55
|
+
label: string;
|
|
56
|
+
columns: string[];
|
|
57
|
+
}
|
|
45
58
|
/** State updater — value or functional update */
|
|
46
59
|
export type TableUpdater<T> = T | ((prev: T) => T);
|
|
47
60
|
/** Options for the imperative `scrollToRow` method */
|
|
@@ -190,6 +203,13 @@ export interface TableProps<T> extends TestableProps {
|
|
|
190
203
|
onColumnVisibilityChange?: TableOnChangeFn<TableVisibilityState>;
|
|
191
204
|
defaultColumnVisibility?: TableVisibilityState;
|
|
192
205
|
defaultColumnOrder?: string[];
|
|
206
|
+
/**
|
|
207
|
+
* Labeled sections for the column-settings menu. When set, the menu renders
|
|
208
|
+
* these groups (flat, non-reorderable — like the FilterInput field menu)
|
|
209
|
+
* instead of the default pinned/unpinned drag-reorder list. See
|
|
210
|
+
* {@link TableColumnGroup}.
|
|
211
|
+
*/
|
|
212
|
+
columnGroups?: TableColumnGroup[];
|
|
193
213
|
/**
|
|
194
214
|
* Fired when the built-in column-settings menu opens (`true`) or closes
|
|
195
215
|
* (`false`). The menu's open state otherwise lives inside the DS and is not
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
3
|
-
"generatedAt": "2026-08-
|
|
2
|
+
"version": "1.9.0",
|
|
3
|
+
"generatedAt": "2026-08-27T14:32:57.789Z",
|
|
4
4
|
"components": [
|
|
5
5
|
{
|
|
6
6
|
"name": "Accordion",
|
|
@@ -76989,6 +76989,12 @@
|
|
|
76989
76989
|
"type": "string[] | undefined",
|
|
76990
76990
|
"required": false
|
|
76991
76991
|
},
|
|
76992
|
+
{
|
|
76993
|
+
"name": "columnGroups",
|
|
76994
|
+
"type": "TableColumnGroup[] | undefined",
|
|
76995
|
+
"required": false,
|
|
76996
|
+
"description": "Labeled sections for the column-settings menu. When set, the menu renders\nthese groups (flat, non-reorderable — like the FilterInput field menu)\ninstead of the default pinned/unpinned drag-reorder list. See\n{@link TableColumnGroup}."
|
|
76997
|
+
},
|
|
76992
76998
|
{
|
|
76993
76999
|
"name": "virtualized",
|
|
76994
77000
|
"type": "TableVirtualized | undefined",
|
|
@@ -83091,6 +83097,11 @@
|
|
|
83091
83097
|
"code": "() => {\n const [columnVisibility, setColumnVisibility] = useState<TableVisibilityState>({});\n const [columnOrder, setColumnOrder] = useState<string[]>([]);\n\n return (\n <Table\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n columnVisibility={columnVisibility}\n onColumnVisibilityChange={setColumnVisibility}\n columnOrder={columnOrder}\n onColumnOrderChange={setColumnOrder}\n defaultColumnVisibility={{}}\n defaultColumnOrder={securityColumnIds}\n />\n );\n}",
|
|
83092
83098
|
"description": "The gear at the end of the header — search the columns, show and hide them, reorder them,\nreset — writing the same visibility and order state as the header menu."
|
|
83093
83099
|
},
|
|
83100
|
+
{
|
|
83101
|
+
"name": "SettingsMenuGrouped",
|
|
83102
|
+
"code": "() => {\n const [columnVisibility, setColumnVisibility] = useState<TableVisibilityState>({});\n\n const columnGroups: TableColumnGroup[] = [\n {\n label: 'Threat classification',\n columns: ['objectName', 'status', 'firstDetected', 'lastSeen'],\n },\n { label: 'Request features', columns: ['sourceIp', 'parameter', 'endpoint', 'requests'] },\n ];\n\n return (\n <Table\n data={securityEvents}\n columns={securityColumns}\n getRowId={row => row.id}\n columnVisibility={columnVisibility}\n onColumnVisibilityChange={setColumnVisibility}\n columnGroups={columnGroups}\n />\n );\n}",
|
|
83103
|
+
"description": "`columnGroups` turns the settings menu into labeled, non-reorderable sections\n(the FilterInput field-menu pattern): no drag handle, columns rendered under\ntheir group header in the given order. Column ids absent from every group\n(`cweId` here) fall into a trailing headerless section."
|
|
83104
|
+
},
|
|
83094
83105
|
{
|
|
83095
83106
|
"name": "Virtualization",
|
|
83096
83107
|
"code": "() => {\n const largeData = useMemo(() => createLargeSecurityEvents(1000), []);\n\n return (\n <Table\n className='h-500'\n data={largeData}\n columns={securityColumns}\n getRowId={row => row.id}\n virtualized='container'\n />\n );\n}",
|