diff --git a/table/schemas/table.cue b/table/schemas/table.cue index 2736ec598..4cad0e007 100644 --- a/table/schemas/table.cue +++ b/table/schemas/table.cue @@ -27,6 +27,7 @@ spec: close({ defaultColumnHidden?: bool pagination?: bool enableFiltering?: bool + enableSorting?: bool columnSettings?: [...#columnSettings] cellSettings?: [...#cellSettings] transforms?: [...common.#transform] diff --git a/table/schemas/tests/valid/table-enable-sorting.json b/table/schemas/tests/valid/table-enable-sorting.json new file mode 100644 index 000000000..9be9c6f08 --- /dev/null +++ b/table/schemas/tests/valid/table-enable-sorting.json @@ -0,0 +1,8 @@ +{ + "kind": "Table", + "spec": { + "density": "standard", + "enableSorting": true, + "enableFiltering": true + } +} diff --git a/table/sdk/go/options.go b/table/sdk/go/options.go index 2d9487550..945b9e17b 100644 --- a/table/sdk/go/options.go +++ b/table/sdk/go/options.go @@ -59,6 +59,13 @@ func WithEnableFiltering(enabled bool) Option { } } +func WithEnableSorting(enabled bool) Option { + return func(builder *Builder) error { + builder.EnableSorting = enabled + return nil + } +} + func WithColumnSettings(settings []ColumnSettings) Option { return func(builder *Builder) error { builder.ColumnSettings = settings diff --git a/table/sdk/go/table.go b/table/sdk/go/table.go index 7d1ae0639..8aae1a048 100644 --- a/table/sdk/go/table.go +++ b/table/sdk/go/table.go @@ -167,6 +167,7 @@ type PluginSpec struct { DefaultColumnHidden bool `json:"defaultColumnHidden,omitempty" yaml:"defaultColumnHidden,omitempty"` Pagination bool `json:"pagination,omitempty" yaml:"pagination,omitempty"` EnableFiltering bool `json:"enableFiltering,omitempty" yaml:"enableFiltering,omitempty"` + EnableSorting bool `json:"enableSorting,omitempty" yaml:"enableSorting,omitempty"` ColumnSettings []ColumnSettings `json:"columnSettings,omitempty" yaml:"columnSettings,omitempty"` CellSettings []CellSettings `json:"cellSettings,omitempty" yaml:"cellSettings,omitempty"` Transforms []common.Transform `json:"transforms,omitempty" yaml:"transforms,omitempty"` diff --git a/table/src/components/ColumnsEditor/ColumnEditor.tsx b/table/src/components/ColumnsEditor/ColumnEditor.tsx index 9e5fdd2cf..d8d2a2b11 100644 --- a/table/src/components/ColumnsEditor/ColumnEditor.tsx +++ b/table/src/components/ColumnsEditor/ColumnEditor.tsx @@ -40,13 +40,30 @@ type OmittedMuiProps = 'children' | 'value' | 'onChange'; export interface ColumnEditorProps extends Omit { column: ColumnSettings; onChange: (column: ColumnSettings) => void; + defaultEnableSorting?: boolean; } -export function ColumnEditor({ column, onChange, ...others }: ColumnEditorProps): ReactElement { +export function ColumnEditor({ + column, + onChange, + defaultEnableSorting = false, + ...others +}: ColumnEditorProps): ReactElement { const [width, setWidth] = useState( column.width === undefined || column.width === 'auto' ? 100 : column.width ); + const enableSorting = column.enableSorting ?? defaultEnableSorting; + + function handleEnableSortingChange(checked: boolean): void { + if (checked === defaultEnableSorting) { + const { enableSorting: _ignored, ...rest } = column; + onChange(rest); + return; + } + onChange({ ...column, enableSorting: checked }); + } + return ( @@ -95,14 +112,14 @@ export function ColumnEditor({ column, onChange, ...others }: ColumnEditorProps) /> onChange({ ...column, enableSorting: e.target.checked })} - /> - } + control={ handleEnableSortingChange(e.target.checked)} />} /> - {column.enableSorting && ( + {column.enableSorting === undefined && ( + + Inherits from General Settings + + )} + {enableSorting && ( is inside a with gap, the negative margin of the grid is not applied. Therefore, let's wrap it in a div. */} {!isCollapsed && (
- +
)} diff --git a/table/src/components/ColumnsEditor/ColumnsEditor.tsx b/table/src/components/ColumnsEditor/ColumnsEditor.tsx index 3ffbe237e..f8f81c3b8 100644 --- a/table/src/components/ColumnsEditor/ColumnsEditor.tsx +++ b/table/src/components/ColumnsEditor/ColumnsEditor.tsx @@ -21,9 +21,10 @@ import { ColumnEditorContainer } from './ColumnEditorContainer'; export interface ColumnsEditorProps { columnSettings: ColumnSettings[]; onChange: (columnOptions: ColumnSettings[]) => void; + defaultEnableSorting?: boolean; } -export function ColumnsEditor({ columnSettings, onChange }: ColumnsEditorProps): ReactElement { +export function ColumnsEditor({ columnSettings, onChange, defaultEnableSorting }: ColumnsEditorProps): ReactElement { const [columnsCollapsed, setColumnsCollapsed] = useState(columnSettings.map(() => true)); function handleColumnChange(index: number, column: ColumnSettings): void { @@ -73,6 +74,7 @@ export function ColumnsEditor({ columnSettings, onChange }: ColumnsEditorProps): key={i} column={column} isCollapsed={columnsCollapsed[i] ?? true} + defaultEnableSorting={defaultEnableSorting} onChange={(updatedColumn: ColumnSettings) => handleColumnChange(i, updatedColumn)} onDelete={() => handleColumnDelete(i)} onCollapse={(collapsed) => handleColumnCollapseExpand(i, collapsed)} diff --git a/table/src/components/TableColumnsEditor.tsx b/table/src/components/TableColumnsEditor.tsx index e32b703e7..bb055fd97 100644 --- a/table/src/components/TableColumnsEditor.tsx +++ b/table/src/components/TableColumnsEditor.tsx @@ -23,5 +23,11 @@ export function TableColumnsEditor({ onChange, value }: TableColumnsEditorProps) onChange({ ...value, columnSettings: columns }); } - return ; + return ( + + ); } diff --git a/table/src/components/TablePanel.test.tsx b/table/src/components/TablePanel.test.tsx index 654cff1da..6eec6c143 100644 --- a/table/src/components/TablePanel.test.tsx +++ b/table/src/components/TablePanel.test.tsx @@ -134,6 +134,27 @@ describe('TablePanel', () => { TEST_TIMEOUT ); + it( + 'should enable sorting on all columns when enableSorting is set in general settings', + async () => { + renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE, { + enableSorting: true, + // column settings without an explicit enableSorting inherit the general default + columnSettings: [ + { name: 'value', header: 'Value' }, + { name: 'env', enableSorting: false }, + ], + }); + + const valueHeaderCell = await screen.findByRole('columnheader', { name: /Value/i }); + expect(await within(valueHeaderCell).findByTestId('ArrowDownwardIcon')).toBeInTheDocument(); + + const envHeaderCell = await screen.findByRole('columnheader', { name: 'env' }); + expect(within(envHeaderCell).queryByTestId('ArrowDownwardIcon')).not.toBeInTheDocument(); + }, + TEST_TIMEOUT + ); + it('should apply transforms', async () => { renderPanel(MOCK_TIME_SERIES_DATA_SINGLEVALUE, { transforms: [ diff --git a/table/src/components/TablePanel.tsx b/table/src/components/TablePanel.tsx index 75106f32f..6f5a7d8e5 100644 --- a/table/src/components/TablePanel.tsx +++ b/table/src/components/TablePanel.tsx @@ -343,7 +343,8 @@ function generateColumnConfig( columnSettings: ColumnSettings[], allVariables: VariableStateMap, gaugeRangeByColumn: Record, - globalCellSettings: CellSettings[] = [] + globalCellSettings: CellSettings[] = [], + defaultEnableSorting = false ): TableColumnConfig | undefined { for (const column of columnSettings) { if (column.name === name) { @@ -360,7 +361,7 @@ function generateColumnConfig( accessorKey: name, header: header ?? name, headerDescription, - enableSorting, + enableSorting: enableSorting ?? defaultEnableSorting, width, align, dataLink: modifiedDataLink, @@ -372,6 +373,7 @@ function generateColumnConfig( return { accessorKey: name, header: name, + enableSorting: defaultEnableSorting, }; } @@ -544,7 +546,8 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps spec.columnSettings ?? [], allVariables, gaugeRangeByColumn, - spec.cellSettings ?? [] + spec.cellSettings ?? [], + spec.enableSorting ?? false ); if (columnConfig !== undefined) { columns.push(columnConfig); @@ -561,7 +564,8 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps spec.columnSettings ?? [], allVariables, gaugeRangeByColumn, - spec.cellSettings ?? [] + spec.cellSettings ?? [], + spec.enableSorting ?? false ); if (columnConfig !== undefined) { columns.push(columnConfig); @@ -571,7 +575,15 @@ export function TablePanel({ contentDimensions, spec, queryResults }: TableProps } return columns; - }, [keys, spec.columnSettings, spec.defaultColumnHidden, allVariables, gaugeRangeByColumn, spec.cellSettings]); + }, [ + keys, + spec.columnSettings, + spec.defaultColumnHidden, + spec.enableSorting, + allVariables, + gaugeRangeByColumn, + spec.cellSettings, + ]); // Filtering state — declared before cellConfigs so filteredData is available for cell config evaluation const [columnFilters, setColumnFilters] = useState([]); diff --git a/table/src/components/TableSettingsEditor.tsx b/table/src/components/TableSettingsEditor.tsx index 083cf05ee..4d2b8658d 100644 --- a/table/src/components/TableSettingsEditor.tsx +++ b/table/src/components/TableSettingsEditor.tsx @@ -93,6 +93,14 @@ export function TableSettingsEditor({ onChange, value }: TableSettingsEditorProp onChange({ ...value, enableFiltering: checked }); } + function handleEnableSortingChange(_event: ChangeEvent, checked: boolean): void { + onChange({ + ...value, + enableSorting: checked, + columnSettings: value.columnSettings?.map(({ enableSorting: _ignored, ...column }) => column), + }); + } + return ( @@ -110,6 +118,10 @@ export function TableSettingsEditor({ onChange, value }: TableSettingsEditorProp label="Enable Column Filtering" control={} /> + } + />