Skip to content

Commit 06a1ba8

Browse files
lunalzmopensearch-changeset-bot[bot]angle943
authored
[Explore Vis] Add customized column order support for table visualization (#10898)
* Fix issue about column order Signed-off-by: Luna Liu <[email protected]> * Changeset file for PR #10898 created/updated * Fix issue Signed-off-by: Luna Liu <[email protected]> * Fix issue Signed-off-by: Luna Liu <[email protected]> * fix table_vis_config.test.ts Signed-off-by: Justin Kim <[email protected]> * Fix issue Signed-off-by: Luna Liu <[email protected]> --------- Signed-off-by: Luna Liu <[email protected]> Signed-off-by: Justin Kim <[email protected]> Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Co-authored-by: Justin Kim <[email protected]>
1 parent a43d4e6 commit 06a1ba8

File tree

7 files changed

+121
-6
lines changed

7 files changed

+121
-6
lines changed

changelogs/fragments/10898.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
feat:
2+
- Add customized column order toggle for table visualization ([#10898](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/10898))

src/plugins/explore/public/components/visualizations/table/table_vis.tsx

Lines changed: 101 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6+
import React, { useCallback, useEffect, useMemo, useState } from 'react';
77
import { EuiDataGrid, EuiDataGridCellValueElementProps, EuiDataGridColumn } from '@elastic/eui';
88
import { VisColumn, VisFieldType } from '../types';
99
import { defaultTableChartStyles, CellTypeConfig, TableChartStyle } from './table_vis_config';
@@ -21,15 +21,108 @@ interface TableVisProps {
2121
styleOptions?: TableChartStyle;
2222
pageSizeOptions?: number[];
2323
showStyleSelector?: boolean;
24+
onStyleChange?: (updatedStyle: Partial<TableChartStyle>) => void;
25+
disableActions?: boolean;
2426
}
2527

2628
export const TableVis = React.memo(
27-
({ rows, columns, styleOptions, pageSizeOptions, showStyleSelector }: TableVisProps) => {
28-
const sortedColumns = useMemo(() => [...columns].sort((a, b) => a.id - b.id), [columns]);
29+
({
30+
rows,
31+
columns,
32+
styleOptions,
33+
pageSizeOptions,
34+
showStyleSelector,
35+
onStyleChange,
36+
disableActions,
37+
}: TableVisProps) => {
38+
const sortedColumns = useMemo(() => {
39+
const baseColumns = [...columns].sort((a, b) => a.id - b.id);
40+
41+
// If user has a saved column order, use it
42+
if (styleOptions?.visibleColumns && styleOptions.visibleColumns.length > 0) {
43+
const userOrder = styleOptions.visibleColumns;
44+
const orderedColumns: VisColumn[] = [];
45+
46+
// First, add columns in user-specified order
47+
userOrder.forEach((columnName) => {
48+
const foundColumn = baseColumns.find((col) => col.name === columnName);
49+
if (foundColumn) {
50+
orderedColumns.push(foundColumn);
51+
}
52+
});
53+
54+
// Then add any new columns that weren't in the saved order
55+
baseColumns.forEach((col) => {
56+
if (!userOrder.includes(col.name)) {
57+
orderedColumns.push(col);
58+
}
59+
});
60+
61+
return orderedColumns;
62+
}
63+
64+
return baseColumns;
65+
}, [columns, styleOptions?.visibleColumns]);
66+
2967
const [visibleColumns, setVisibleColumns] = useState(() =>
3068
sortedColumns.map(({ column }) => column)
3169
);
3270

71+
// Update visibleColumns when sortedColumns changes, preserving user's visibility choices
72+
useEffect(() => {
73+
// Apply hiddenColumns from saved configuration if available
74+
const hiddenColumns = styleOptions?.hiddenColumns || [];
75+
const visibleColumnsFromConfig = sortedColumns.filter(
76+
(col) => !hiddenColumns.includes(col.name)
77+
);
78+
79+
setVisibleColumns(visibleColumnsFromConfig.map((col) => col.column));
80+
}, [sortedColumns, styleOptions?.hiddenColumns]);
81+
82+
// Handle user column order changes - directly save any reorder/hide operations
83+
const handleColumnVisibilityChange = useCallback(
84+
(updatedVisibleColumns: string[]) => {
85+
const previousVisibleColumns = visibleColumns;
86+
setVisibleColumns(updatedVisibleColumns);
87+
88+
// Always save user changes when onStyleChange is available
89+
if (onStyleChange) {
90+
const allColumns = sortedColumns.map((col) => col.column);
91+
92+
// Check if this is a reordering or visibility change
93+
const isReordering =
94+
updatedVisibleColumns.length === previousVisibleColumns.length &&
95+
updatedVisibleColumns.every((col) => previousVisibleColumns.includes(col));
96+
97+
// Calculate hidden columns
98+
const newHiddenColumns = allColumns.filter((col) => !updatedVisibleColumns.includes(col));
99+
100+
if (isReordering) {
101+
// This is a reordering operation - update column order
102+
const finalUserOrder: string[] = [];
103+
104+
finalUserOrder.push(...updatedVisibleColumns);
105+
106+
onStyleChange({
107+
visibleColumns: finalUserOrder
108+
.map((id) => sortedColumns.find((col) => col.column === id)?.name ?? '')
109+
.filter(Boolean),
110+
});
111+
} else {
112+
// This is a visibility change - save hidden columns and update order if needed
113+
const updates: Partial<TableChartStyle> = {
114+
hiddenColumns: newHiddenColumns
115+
.map((id) => sortedColumns.find((col) => col.column === id)?.name ?? '')
116+
.filter(Boolean),
117+
};
118+
119+
onStyleChange(updates);
120+
}
121+
}
122+
},
123+
[onStyleChange, visibleColumns, sortedColumns]
124+
);
125+
33126
const pageSize = styleOptions?.pageSize ?? defaultTableChartStyles.pageSize;
34127
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize });
35128
const [filters, setFilters] = useState<Record<string, FilterConfig>>({});
@@ -69,6 +162,7 @@ export const TableVis = React.memo(
69162
return sortedColumns.map((col) => ({
70163
id: col.column,
71164
displayAsText: col.name,
165+
actions: disableActions ? false : undefined,
72166
display: (
73167
<TableColumnHeader
74168
col={col}
@@ -88,6 +182,7 @@ export const TableVis = React.memo(
88182
filters,
89183
columnUniques,
90184
setFilters,
185+
disableActions,
91186
]);
92187

93188
const onChangeItemsPerPage = useCallback((newPageSize: number) => {
@@ -240,7 +335,7 @@ export const TableVis = React.memo(
240335
className="tableVis__dataGrid"
241336
aria-label="Table visualization"
242337
columns={dataGridColumns}
243-
columnVisibility={{ visibleColumns, setVisibleColumns }}
338+
columnVisibility={{ visibleColumns, setVisibleColumns: handleColumnVisibilityChange }}
244339
rowCount={filteredRows.length}
245340
pagination={{
246341
...pagination,
@@ -252,7 +347,8 @@ export const TableVis = React.memo(
252347
renderFooterCellValue={styleOptions?.showFooter ? renderFooterCellValue : undefined}
253348
toolbarVisibility={{
254349
showFullScreenSelector: false,
255-
showStyleSelector: showStyleSelector ?? true,
350+
showStyleSelector: disableActions ? false : showStyleSelector ?? true,
351+
showColumnSelector: disableActions ? false : true,
256352
}}
257353
gridStyle={{ rowHover: 'highlight' }}
258354
leadingControlColumns={[]}

src/plugins/explore/public/components/visualizations/table/table_vis_config.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,15 @@ describe('table_vis_config', () => {
5252
expect(defaults).toEqual({
5353
pageSize: 10,
5454
globalAlignment: 'left',
55+
hiddenColumns: [],
5556
showColumnFilter: false,
5657
showFooter: false,
5758
footerCalculations: [],
5859
cellTypes: [],
5960
thresholds: [],
6061
baseColor: '#000000',
6162
dataLinks: [],
63+
visibleColumns: [],
6264
});
6365
});
6466
});

src/plugins/explore/public/components/visualizations/table/table_vis_config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export interface TableChartStyleOptions {
3030
thresholds?: Threshold[];
3131
baseColor?: string;
3232
dataLinks?: DataLink[];
33+
visibleColumns?: string[];
34+
hiddenColumns?: string[];
3335
}
3436

3537
export type TableChartStyle = Required<TableChartStyleOptions>;
@@ -44,6 +46,8 @@ export const defaultTableChartStyles: TableChartStyle = {
4446
thresholds: [],
4547
baseColor: '#000000',
4648
dataLinks: [],
49+
visibleColumns: [],
50+
hiddenColumns: [],
4751
};
4852

4953
export const createTableConfig = (): VisualizationType<'table'> => ({

src/plugins/explore/public/components/visualizations/visualization_builder.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ export class VisualizationBuilder {
394394
searchContext,
395395
ExpressionRenderer,
396396
onSelectTimeRange,
397+
onStyleChange: this.updateStyles.bind(this),
397398
});
398399
}
399400

src/plugins/explore/public/components/visualizations/visualization_render.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ interface Props {
2626
searchContext?: ExecutionContextSearch;
2727
ExpressionRenderer?: ExpressionsStart['ReactExpressionRenderer'];
2828
onSelectTimeRange?: (timeRange?: TimeRange) => void;
29+
onStyleChange?: (updatedStyle: Partial<TableChartStyle>) => void;
2930
}
3031

3132
const defaultStyleOptions: TableChartStyle = {
@@ -45,6 +46,7 @@ export const VisualizationRender = ({
4546
searchContext,
4647
ExpressionRenderer,
4748
onSelectTimeRange,
49+
onStyleChange,
4850
}: Props) => {
4951
const visualizationData = useObservable(data$);
5052
const visConfig = useObservable(config$);
@@ -124,7 +126,13 @@ export const VisualizationRender = ({
124126

125127
if (visConfig?.type === 'table') {
126128
return (
127-
<TableVis styleOptions={visConfig.styles as TableChartStyle} rows={rows} columns={columns} />
129+
<TableVis
130+
styleOptions={visConfig.styles as TableChartStyle}
131+
rows={rows}
132+
columns={columns}
133+
onStyleChange={onStyleChange}
134+
disableActions={false}
135+
/>
128136
);
129137
}
130138

@@ -139,6 +147,7 @@ export const VisualizationRender = ({
139147
styleOptions={defaultStyleOptions}
140148
pageSizeOptions={PAGE_SIZE_OPTIONS}
141149
showStyleSelector={false}
150+
disableActions={false}
142151
/>
143152
);
144153
}

src/plugins/explore/public/embeddable/explore_embeddable_component.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export const ExploreEmbeddableComponent = ({ searchProps }: ExploreEmbeddablePro
112112
columns={searchProps.tableData?.columns ?? []}
113113
rows={searchProps.tableData?.rows ?? []}
114114
styleOptions={searchProps.styleOptions as TableChartStyle}
115+
disableActions={true}
115116
/>
116117
);
117118
}

0 commit comments

Comments
 (0)