Skip to content

Commit a8dc5e1

Browse files
authored
[FEATURE] add header and row actions to table (#557)
Signed-off-by: Gabriel Bernal <gbernal@redhat.com>
1 parent d4de825 commit a8dc5e1

6 files changed

Lines changed: 113 additions & 14 deletions

File tree

table/schemas/table.cue

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ spec: close({
2626
defaultColumnHeight?: "auto" | number
2727
defaultColumnHidden?: bool
2828
pagination?: bool
29-
enableFiltering?: bool
30-
columnSettings?: [...#columnSettings]
31-
cellSettings?: [...#cellSettings]
32-
transforms?: [...common.#transform]
29+
enableFiltering?: bool
30+
columnSettings?: [...#columnSettings]
31+
cellSettings?: [...#cellSettings]
32+
transforms?: [...common.#transform]
33+
selection?: common.#selection
34+
actions?: common.#actions
3335
})
3436

3537
#columnSettings: {

table/src/Table.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@
1212
// limitations under the License.
1313

1414
import { PanelPlugin } from '@perses-dev/plugin-system';
15-
import { createInitialTableOptions, TableOptions } from './models';
1615
import {
1716
getTablePanelQueryOptions,
17+
TableCellsEditor,
18+
TableColumnsEditor,
1819
TablePanel,
1920
TableProps,
20-
TableColumnsEditor,
2121
TableSettingsEditor,
22-
TableCellsEditor,
2322
TableTransformsEditor,
2423
} from './components';
24+
import { TableItemSelectionActionsEditor } from './components/TableItemSelectionActionsEditor';
25+
import { createInitialTableOptions, TableOptions } from './models';
2526

2627
/**
2728
* The core TimeSeriesTable panel plugin for Perses.
@@ -35,6 +36,7 @@ export const Table: PanelPlugin<TableOptions, TableProps> = {
3536
{ label: 'Column Settings', content: TableColumnsEditor },
3637
{ label: 'Cell Settings', content: TableCellsEditor },
3738
{ label: 'Transforms', content: TableTransformsEditor },
39+
{ label: 'Item Actions', content: TableItemSelectionActionsEditor },
3840
],
3941
createInitialOptions: createInitialTableOptions,
4042
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright The Perses Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
import { ActionOptions, ItemSelectionActionsEditor, SelectionOptions } from '@perses-dev/plugin-system';
15+
import { ReactElement } from 'react';
16+
import { TableSettingsEditorProps } from '../models';
17+
18+
export function TableItemSelectionActionsEditor({ value, onChange }: TableSettingsEditorProps): ReactElement {
19+
function handleActionsChange(actions: ActionOptions | undefined): void {
20+
onChange({ ...value, actions: actions });
21+
}
22+
23+
function handleSelectionChange(selection: SelectionOptions | undefined): void {
24+
onChange({ ...value, selection: selection });
25+
}
26+
27+
return (
28+
<ItemSelectionActionsEditor
29+
actionOptions={value.actions}
30+
onChangeActions={handleActionsChange}
31+
selectionOptions={value.selection}
32+
onChangeSelection={handleSelectionChange}
33+
/>
34+
);
35+
}

table/src/components/TablePanel.tsx

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,21 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14+
import { Box, Theme, Typography, useTheme } from '@mui/material';
15+
import { Table, TableCellConfigs, TableColumnConfig, useSelection } from '@perses-dev/components';
16+
import { formatValue, Labels, QueryDataType, TimeSeries, TimeSeriesData, transformData } from '@perses-dev/core';
17+
import { useSelectionItemActions } from '@perses-dev/dashboards';
1418
import {
19+
ActionOptions,
1520
PanelData,
1621
PanelProps,
1722
replaceVariablesInString,
1823
useAllVariableValues,
1924
VariableStateMap,
2025
} from '@perses-dev/plugin-system';
21-
import { Table, TableCellConfigs, TableColumnConfig } from '@perses-dev/components';
22-
import { ReactElement, useEffect, useMemo, useState } from 'react';
23-
import { formatValue, Labels, QueryDataType, TimeSeries, TimeSeriesData, transformData } from '@perses-dev/core';
24-
import { PaginationState, SortingState, ColumnFiltersState } from '@tanstack/react-table';
25-
import { useTheme, Theme, Typography, Box } from '@mui/material';
26-
import { ColumnSettings, TableOptions, evaluateConditionalFormatting } from '../models';
26+
import { ColumnFiltersState, PaginationState, RowSelectionState, SortingState } from '@tanstack/react-table';
27+
import { ReactElement, useCallback, useEffect, useMemo, useRef, useState } from 'react';
28+
import { ColumnSettings, evaluateConditionalFormatting, TableOptions } from '../models';
2729
import { EmbeddedPanel } from './EmbeddedPanel';
2830

2931
function generateCellContentConfig(
@@ -211,6 +213,50 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps
211213
const theme = useTheme();
212214
const allVariables = useAllVariableValues();
213215

216+
const selectionEnabled = spec.selection?.enabled ?? false;
217+
const { selectionMap, setSelection, clearSelection } = useSelection<Record<string, unknown>, string>();
218+
219+
const itemActionsConfig = spec.actions ? (spec.actions as ActionOptions) : undefined;
220+
const itemActionsListConfig =
221+
itemActionsConfig?.enabled && itemActionsConfig.displayWithItem ? itemActionsConfig.actionsList : [];
222+
223+
const { getItemActionButtons, confirmDialog, actionButtons } = useSelectionItemActions({
224+
actions: itemActionsListConfig,
225+
variableState: allVariables,
226+
});
227+
228+
const filteredDataRef = useRef<Array<Record<string, unknown>>>([]);
229+
230+
// Convert selectionMap to TanStack's RowSelectionState format
231+
const rowSelection = useMemo((): RowSelectionState => {
232+
const result: RowSelectionState = {};
233+
selectionMap.forEach((_, id) => {
234+
result[id] = true;
235+
});
236+
return result;
237+
}, [selectionMap]);
238+
239+
const handleRowSelectionChange = useCallback(
240+
(newRowSelection: RowSelectionState) => {
241+
const newSelection: Array<{ id: string; item: Record<string, unknown> }> = [];
242+
for (const [id, isSelected] of Object.entries(newRowSelection)) {
243+
if (isSelected) {
244+
const index = parseInt(id, 10);
245+
if (filteredDataRef.current[index] !== undefined) {
246+
newSelection.push({ id, item: filteredDataRef.current[index] });
247+
}
248+
}
249+
}
250+
251+
if (newSelection.length === 0) {
252+
clearSelection();
253+
} else {
254+
setSelection(newSelection);
255+
}
256+
},
257+
[setSelection, clearSelection]
258+
);
259+
214260
// TODO: handle other query types
215261
const queryMode = getTablePanelQueryOptions(spec).mode;
216262
const rawData: Array<Record<string, unknown>> = useMemo(() => {
@@ -452,6 +498,9 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps
452498
return filtered;
453499
}, [data, columnFilters, spec.enableFiltering]);
454500

501+
// Keep ref in sync with filtered data for use in selection handler
502+
filteredDataRef.current = filteredData;
503+
455504
const [pagination, setPagination] = useState<PaginationState | undefined>(
456505
spec.pagination ? { pageIndex: 0, pageSize: 10 } : undefined
457506
);
@@ -486,6 +535,7 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps
486535

487536
return (
488537
<>
538+
{confirmDialog}
489539
{spec.enableFiltering && (
490540
<div
491541
style={{
@@ -592,6 +642,11 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps
592642
onSortingChange={setSorting}
593643
pagination={pagination}
594644
onPaginationChange={setPagination}
645+
checkboxSelection={selectionEnabled}
646+
rowSelection={rowSelection}
647+
onRowSelectionChange={handleRowSelectionChange}
648+
getItemActions={({ id, data }) => getItemActionButtons({ id, data: data as Record<string, unknown> })}
649+
hasItemActions={actionButtons && actionButtons.length > 0}
595650
/>
596651
</>
597652
);

table/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export * from './ColumnsEditor';
1616
export * from './EmbeddedPanel';
1717
export * from './TableCellsEditor';
1818
export * from './TableColumnsEditor';
19+
export * from './TableItemSelectionActionsEditor';
1920
export * from './TablePanel';
2021
export * from './TableSettingsEditor';
2122
export * from './TableTransformsEditor';

table/src/models/table-model.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
import { Definition, FormatOptions, Transform, UnknownSpec } from '@perses-dev/core';
1515
import { TableDensity, TableCellConfig } from '@perses-dev/components';
16-
import { OptionsEditorProps } from '@perses-dev/plugin-system';
16+
import { ActionOptions, OptionsEditorProps, SelectionOptions } from '@perses-dev/plugin-system';
1717
import React from 'react';
1818
import { TextField, Stack, MenuItem, Typography } from '@mui/material';
1919

@@ -138,6 +138,10 @@ export interface TableOptions {
138138
pagination?: boolean;
139139
// Enable filtering for individual columns.
140140
enableFiltering?: boolean;
141+
// Enable row selection.
142+
selection?: SelectionOptions;
143+
// Customize actions available for selected rows.
144+
actions?: ActionOptions;
141145
// Customize column display and order them by their index in the array.
142146
columnSettings?: ColumnSettings[];
143147
// Customize cell display based on their value.

0 commit comments

Comments
 (0)