diff --git a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx index 8ed8eaab0d..d4c34bdf0a 100644 --- a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx +++ b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx @@ -521,7 +521,7 @@ export default function CeleryOverviewTable({ locale={{ emptyText: isLoading ? null : No data, }} - scroll={{ x: true }} + scroll={{ x: 'max-content' }} showSorterTooltip onDragColumn={handleDragColumn} onRow={(record): { onClick: () => void; className: string } => ({ diff --git a/frontend/src/components/ResizeTable/ResizableHeader.tsx b/frontend/src/components/ResizeTable/ResizableHeader.tsx index 8611a45886..cf23975222 100644 --- a/frontend/src/components/ResizeTable/ResizableHeader.tsx +++ b/frontend/src/components/ResizeTable/ResizableHeader.tsx @@ -1,3 +1,5 @@ +import './ResizeTable.styles.scss'; + import { SyntheticEvent, useMemo } from 'react'; import { Resizable, ResizeCallbackData } from 'react-resizable'; @@ -10,8 +12,8 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element { const handle = useMemo( () => ( e.stopPropagation()} + className="resize-handle" /> ), [], @@ -19,7 +21,7 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element { if (!width) { // eslint-disable-next-line react/jsx-props-no-spreading - return ; + return ; } return ( @@ -29,9 +31,10 @@ function ResizableHeader(props: ResizableHeaderProps): JSX.Element { handle={handle} onResize={onResize} draggableOpts={enableUserSelectHack} + minConstraints={[150, 0]} > {/* eslint-disable-next-line react/jsx-props-no-spreading */} - + ); } diff --git a/frontend/src/components/ResizeTable/ResizeTable.styles.scss b/frontend/src/components/ResizeTable/ResizeTable.styles.scss new file mode 100644 index 0000000000..b514d56820 --- /dev/null +++ b/frontend/src/components/ResizeTable/ResizeTable.styles.scss @@ -0,0 +1,53 @@ +.resizable-header { + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + position: relative; + + .ant-table-column-title { + white-space: normal; + overflow: hidden; + text-overflow: ellipsis; + } +} + +.resize-main-table { + .ant-table-body { + .ant-table-tbody { + .ant-table-row { + .ant-table-cell { + .ant-typography { + white-space: unset; + } + } + } + } + } +} + +.logs-table, +.traces-table { + .resize-table { + .resize-handle { + position: absolute; + top: 0; + bottom: 0; + inset-inline-end: -5px; + width: 10px; + cursor: col-resize; + + &::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 1px; + height: 1.6em; + background-color: var(--bg-slate-200); + transition: background-color 0.2s; + } + } + } +} diff --git a/frontend/src/components/ResizeTable/ResizeTable.tsx b/frontend/src/components/ResizeTable/ResizeTable.tsx index 5f8ac7a4a5..800ab10629 100644 --- a/frontend/src/components/ResizeTable/ResizeTable.tsx +++ b/frontend/src/components/ResizeTable/ResizeTable.tsx @@ -2,35 +2,63 @@ import { Table } from 'antd'; import { ColumnsType } from 'antd/lib/table'; +import cx from 'classnames'; import { dragColumnParams } from 'hooks/useDragColumns/configs'; -import { set } from 'lodash-es'; +import { RowData } from 'lib/query/createTableColumnsFromQuery'; +import { debounce, set } from 'lodash-es'; +import { useDashboard } from 'providers/Dashboard/Dashboard'; import { SyntheticEvent, useCallback, useEffect, useMemo, + useRef, useState, } from 'react'; import ReactDragListView from 'react-drag-listview'; import { ResizeCallbackData } from 'react-resizable'; +import { Widgets } from 'types/api/dashboard/getAll'; import ResizableHeader from './ResizableHeader'; import { DragSpanStyle } from './styles'; import { ResizeTableProps } from './types'; +// eslint-disable-next-line sonarjs/cognitive-complexity function ResizeTable({ columns, onDragColumn, pagination, + widgetId, + shouldPersistColumnWidths = false, ...restProps }: ResizeTableProps): JSX.Element { const [columnsData, setColumns] = useState([]); + const { setColumnWidths, selectedDashboard } = useDashboard(); + + const columnWidths = shouldPersistColumnWidths + ? (selectedDashboard?.data?.widgets?.find( + (widget) => widget.id === widgetId, + ) as Widgets)?.columnWidths + : undefined; + + const updateAllColumnWidths = useRef( + debounce((widthsConfig: Record) => { + if (!widgetId || !shouldPersistColumnWidths) return; + setColumnWidths?.((prev) => ({ + ...prev, + [widgetId]: widthsConfig, + })); + }, 1000), + ).current; const handleResize = useCallback( (index: number) => ( - _e: SyntheticEvent, + e: SyntheticEvent, { size }: ResizeCallbackData, ): void => { + e.preventDefault(); + e.stopPropagation(); + const newColumns = [...columnsData]; newColumns[index] = { ...newColumns[index], @@ -65,6 +93,7 @@ function ResizeTable({ ...restProps, components: { header: { cell: ResizableHeader } }, columns: mergedColumns, + className: cx('resize-main-table', restProps.className), }; set( @@ -78,9 +107,39 @@ function ResizeTable({ useEffect(() => { if (columns) { - setColumns(columns); + // Apply stored column widths from widget configuration + const columnsWithStoredWidths = columns.map((col) => { + const dataIndex = (col as RowData).dataIndex as string; + if (dataIndex && columnWidths && columnWidths[dataIndex]) { + return { + ...col, + width: columnWidths[dataIndex], // Apply stored width + }; + } + return col; + }); + + setColumns(columnsWithStoredWidths); } - }, [columns]); + }, [columns, columnWidths]); + + useEffect(() => { + if (!shouldPersistColumnWidths) return; + // Collect all column widths in a single object + const newColumnWidths: Record = {}; + + mergedColumns.forEach((col) => { + if (col.width && (col as RowData).dataIndex) { + const dataIndex = (col as RowData).dataIndex as string; + newColumnWidths[dataIndex] = col.width as number; + } + }); + + // Only update if there are actual widths to set + if (Object.keys(newColumnWidths).length > 0) { + updateAllColumnWidths(newColumnWidths); + } + }, [mergedColumns, updateAllColumnWidths, shouldPersistColumnWidths]); return onDragColumn ? ( diff --git a/frontend/src/components/ResizeTable/styles.ts b/frontend/src/components/ResizeTable/styles.ts index e69b208348..8aad2535b0 100644 --- a/frontend/src/components/ResizeTable/styles.ts +++ b/frontend/src/components/ResizeTable/styles.ts @@ -8,6 +8,8 @@ export const SpanStyle = styled.span` width: 0.625rem; height: 100%; cursor: col-resize; + margin-left: 4px; + margin-right: 4px; `; export const DragSpanStyle = styled.span` diff --git a/frontend/src/components/ResizeTable/types.ts b/frontend/src/components/ResizeTable/types.ts index b912ca44d9..7b794ade1f 100644 --- a/frontend/src/components/ResizeTable/types.ts +++ b/frontend/src/components/ResizeTable/types.ts @@ -9,6 +9,8 @@ import { TableDataSource } from './contants'; export interface ResizeTableProps extends TableProps { onDragColumn?: (fromIndex: number, toIndex: number) => void; + widgetId?: string; + shouldPersistColumnWidths?: boolean; } export interface DynamicColumnTableProps extends TableProps { tablesource: typeof TableDataSource[keyof typeof TableDataSource]; diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx index 14dbb70084..6b99243cf0 100644 --- a/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannels.test.tsx @@ -1,3 +1,4 @@ +import ROUTES from 'constants/routes'; import AlertChannels from 'container/AllAlertChannels'; import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; import { act, fireEvent, render, screen, waitFor } from 'tests/test-utils'; @@ -20,6 +21,13 @@ jest.mock('hooks/useNotifications', () => ({ })), })); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.ALL_CHANNELS}`, + }), +})); + describe('Alert Channels Settings List page', () => { beforeEach(() => { render(); diff --git a/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx index 3d957af104..542e70c0bc 100644 --- a/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx +++ b/frontend/src/container/AllAlertChannels/__tests__/AlertChannelsNormalUser.test.tsx @@ -1,3 +1,4 @@ +import ROUTES from 'constants/routes'; import AlertChannels from 'container/AllAlertChannels'; import { allAlertChannels } from 'mocks-server/__mockdata__/alerts'; import { fireEvent, render, screen, waitFor } from 'tests/test-utils'; @@ -25,6 +26,13 @@ jest.mock('hooks/useComponentPermission', () => ({ default: jest.fn().mockImplementation(() => [false]), })); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${process.env.FRONTEND_API_ENDPOINT}${ROUTES.ALL_CHANNELS}`, + }), +})); + describe('Alert Channels Settings List page (Normal User)', () => { beforeEach(() => { render(); diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index c910aba903..ae6d3a1543 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -44,7 +44,10 @@ import { EditMenuAction, ViewMenuAction } from './config'; import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState'; import GridCard from './GridCard'; import { Card, CardContainer, ReactGridLayout } from './styles'; -import { removeUndefinedValuesFromLayout } from './utils'; +import { + hasColumnWidthsChanged, + removeUndefinedValuesFromLayout, +} from './utils'; import { MenuItemKeys } from './WidgetHeader/contants'; import { WidgetRowHeader } from './WidgetRow'; @@ -68,6 +71,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { setDashboardQueryRangeCalled, setSelectedRowWidgetId, isDashboardFetching, + columnWidths, } = useDashboard(); const { data } = selectedDashboard || {}; const { pathname } = useLocation(); @@ -162,6 +166,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { logEventCalledRef.current = true; } }, [data]); + const onSaveHandler = (): void => { if (!selectedDashboard) return; @@ -171,6 +176,15 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { ...selectedDashboard.data, panelMap: { ...currentPanelMap }, layout: dashboardLayout.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET), + widgets: selectedDashboard?.data?.widgets?.map((widget) => { + if (columnWidths?.[widget.id]) { + return { + ...widget, + columnWidths: columnWidths[widget.id], + }; + } + return widget; + }), }, uuid: selectedDashboard.uuid, }; @@ -227,20 +241,31 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element { useEffect(() => { if ( + isDashboardLocked || + !saveLayoutPermission || + updateDashboardMutation.isLoading || + isDashboardFetching + ) { + return; + } + + const shouldSaveLayout = dashboardLayout && Array.isArray(dashboardLayout) && dashboardLayout.length > 0 && - !isEqual(layouts, dashboardLayout) && - !isDashboardLocked && - saveLayoutPermission && - !updateDashboardMutation.isLoading && - !isDashboardFetching - ) { + !isEqual(layouts, dashboardLayout); + + const shouldSaveColumnWidths = + dashboardLayout && + Array.isArray(dashboardLayout) && + dashboardLayout.length > 0 && + hasColumnWidthsChanged(columnWidths, selectedDashboard); + + if (shouldSaveLayout || shouldSaveColumnWidths) { onSaveHandler(); } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [dashboardLayout]); + }, [dashboardLayout, columnWidths]); const onSettingsModalSubmit = (): void => { const newTitle = form.getFieldValue('title'); diff --git a/frontend/src/container/GridCardLayout/utils.ts b/frontend/src/container/GridCardLayout/utils.ts index 102c54caf1..72026a3c49 100644 --- a/frontend/src/container/GridCardLayout/utils.ts +++ b/frontend/src/container/GridCardLayout/utils.ts @@ -1,5 +1,7 @@ import { FORMULA_REGEXP } from 'constants/regExp'; +import { isEmpty, isEqual } from 'lodash-es'; import { Layout } from 'react-grid-layout'; +import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; export const removeUndefinedValuesFromLayout = (layout: Layout[]): Layout[] => layout.map((obj) => @@ -25,3 +27,27 @@ export function extractQueryNamesFromExpression(expression: string): string[] { // Extract matches and deduplicate return [...new Set(expression.match(queryNameRegex) || [])]; } + +export const hasColumnWidthsChanged = ( + columnWidths: Record>, + selectedDashboard?: Dashboard, +): boolean => { + // If no column widths stored, no changes + if (isEmpty(columnWidths) || !selectedDashboard) return false; + + // Check each widget's column widths + return Object.keys(columnWidths).some((widgetId) => { + const dashboardWidget = selectedDashboard?.data?.widgets?.find( + (widget) => widget.id === widgetId, + ) as Widgets; + + const newWidths = columnWidths[widgetId]; + const existingWidths = dashboardWidget?.columnWidths; + + // If both are empty/undefined, no change + if (isEmpty(newWidths) || isEmpty(existingWidths)) return false; + + // Compare stored column widths with dashboard widget's column widths + return !isEqual(newWidths, existingWidths); + }); +}; diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx index 0bcefbb7ad..87ab191eed 100644 --- a/frontend/src/container/GridTableComponent/index.tsx +++ b/frontend/src/container/GridTableComponent/index.tsx @@ -43,6 +43,7 @@ function GridTableComponent({ sticky, openTracesButton, onOpenTraceBtnClick, + widgetId, ...props }: GridTableComponentProps): JSX.Element { const { t } = useTranslation(['valueGraph']); @@ -229,6 +230,7 @@ function GridTableComponent({ columns={openTracesButton ? columnDataWithOpenTracesButton : newColumnData} dataSource={dataSource} sticky={sticky} + widgetId={widgetId} onRow={ openTracesButton ? (record): React.HTMLAttributes => ({ diff --git a/frontend/src/container/GridTableComponent/types.ts b/frontend/src/container/GridTableComponent/types.ts index 7e930d51c3..778e439a22 100644 --- a/frontend/src/container/GridTableComponent/types.ts +++ b/frontend/src/container/GridTableComponent/types.ts @@ -17,6 +17,7 @@ export type GridTableComponentProps = { searchTerm?: string; openTracesButton?: boolean; onOpenTraceBtnClick?: (record: RowData) => void; + widgetId?: string; } & Pick & Omit, 'columns' | 'dataSource'>; diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index d8131b5403..5381b9923a 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -1,9 +1,9 @@ import './LogsPanelComponent.styles.scss'; -import { Table } from 'antd'; import LogDetail from 'components/LogDetail'; import { VIEW_TYPES } from 'components/LogDetail/constants'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; +import { ResizeTable } from 'components/ResizeTable'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { PANEL_TYPES } from 'constants/queryBuilder'; import Controls from 'container/Controls'; @@ -79,9 +79,14 @@ function LogsPanelComponent({ const { formatTimezoneAdjustedTimestamp } = useTimezone(); - const columns = getLogPanelColumnsList( - widget.selectedLogFields, - formatTimezoneAdjustedTimestamp, + const columns = useMemo( + () => + getLogPanelColumnsList( + widget.selectedLogFields, + formatTimezoneAdjustedTimestamp, + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [widget.selectedLogFields], ); const dataLength = @@ -216,16 +221,18 @@ function LogsPanelComponent({
- diff --git a/frontend/src/container/NewWidget/index.tsx b/frontend/src/container/NewWidget/index.tsx index 4c37252a54..399ab16f19 100644 --- a/frontend/src/container/NewWidget/index.tsx +++ b/frontend/src/container/NewWidget/index.tsx @@ -74,6 +74,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { setToScrollWidgetId, selectedRowWidgetId, setSelectedRowWidgetId, + columnWidths, } = useDashboard(); const { t } = useTranslation(['dashboard']); @@ -238,8 +239,10 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { selectedLogFields, selectedTracesFields, isLogScale, + columnWidths: columnWidths?.[selectedWidget?.id], }; }); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ columnUnits, currentQuery, @@ -260,6 +263,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element { combineHistogram, stackedBarChart, isLogScale, + columnWidths, ]); const closeModal = (): void => { diff --git a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx index 58ddaef5a8..88da74fc6b 100644 --- a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx +++ b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx @@ -26,6 +26,7 @@ function TablePanelWrapper({ searchTerm={searchTerm} openTracesButton={openTracesButton} onOpenTraceBtnClick={onOpenTraceBtnClick} + widgetId={widget.id} // eslint-disable-next-line react/jsx-props-no-spreading {...GRID_TABLE_CONFIG} /> diff --git a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap index 0f1a725125..0c77a340a4 100644 --- a/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap +++ b/frontend/src/container/PanelWrapper/__tests__/__snapshots__/TablePanelWrapper.test.tsx.snap @@ -9,6 +9,8 @@ exports[`Table panel wrappper tests table should render fine with the query resp width: 0.625rem; height: 100%; cursor: col-resize; + margin-left: 4px; + margin-right: 4px; } .c0 { @@ -54,7 +56,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp class="query-table" >
@@ -221,7 +223,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp style="overflow-x: auto; overflow-y: hidden;" >
@@ -143,12 +145,12 @@ exports[`Table panel wrappper tests table should render fine with the query resp @@ -209,7 +211,7 @@ exports[`Table panel wrappper tests table should render fine with the query resp
['sticky']; searchTerm?: string; + widgetId?: string; }; diff --git a/frontend/src/container/QueryTable/QueryTable.tsx b/frontend/src/container/QueryTable/QueryTable.tsx index e438070173..16376eb040 100644 --- a/frontend/src/container/QueryTable/QueryTable.tsx +++ b/frontend/src/container/QueryTable/QueryTable.tsx @@ -24,6 +24,7 @@ export function QueryTable({ dataSource, sticky, searchTerm, + widgetId, ...props }: QueryTableProps): JSX.Element { const { isDownloadEnabled = false, fileName = '' } = downloadOption || {}; @@ -95,8 +96,10 @@ export function QueryTable({ columns={tableColumns} tableLayout="fixed" dataSource={filterTable === null ? newDataSource : filterTable} - scroll={{ x: true }} + scroll={{ x: 'max-content' }} pagination={paginationConfig} + widgetId={widgetId} + shouldPersistColumnWidths sticky={sticky} // eslint-disable-next-line react/jsx-props-no-spreading {...props} diff --git a/frontend/src/container/TracesTableComponent/TracesTableComponent.tsx b/frontend/src/container/TracesTableComponent/TracesTableComponent.tsx index 182aa1d881..f71ddc0fd9 100644 --- a/frontend/src/container/TracesTableComponent/TracesTableComponent.tsx +++ b/frontend/src/container/TracesTableComponent/TracesTableComponent.tsx @@ -1,7 +1,7 @@ import './TracesTableComponent.styles.scss'; -import { Table } from 'antd'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; +import { ResizeTable } from 'components/ResizeTable'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import Controls from 'container/Controls'; import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs'; @@ -54,9 +54,14 @@ function TracesTableComponent({ const { formatTimezoneAdjustedTimestamp } = useTimezone(); - const columns = getListColumns( - widget.selectedTracesFields || [], - formatTimezoneAdjustedTimestamp, + const columns = useMemo( + () => + getListColumns( + widget.selectedTracesFields || [], + formatTimezoneAdjustedTimestamp, + ), + // eslint-disable-next-line react-hooks/exhaustive-deps + [widget.selectedTracesFields], ); const dataLength = @@ -116,16 +121,18 @@ function TracesTableComponent({
-
diff --git a/frontend/src/providers/Dashboard/Dashboard.tsx b/frontend/src/providers/Dashboard/Dashboard.tsx index f2699ad76c..4fa7c3be4b 100644 --- a/frontend/src/providers/Dashboard/Dashboard.tsx +++ b/frontend/src/providers/Dashboard/Dashboard.tsx @@ -40,7 +40,11 @@ import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll'; import { GlobalReducer } from 'types/reducer/globalTime'; import { v4 as generateUUID } from 'uuid'; -import { DashboardSortOrder, IDashboardContext } from './types'; +import { + DashboardSortOrder, + IDashboardContext, + WidgetColumnWidths, +} from './types'; import { sortLayout } from './util'; const DashboardContext = createContext({ @@ -74,6 +78,8 @@ const DashboardContext = createContext({ selectedRowWidgetId: '', setSelectedRowWidgetId: () => {}, isDashboardFetching: false, + columnWidths: {}, + setColumnWidths: () => {}, }); interface Props { @@ -408,6 +414,8 @@ export function DashboardProvider({ } }; + const [columnWidths, setColumnWidths] = useState({}); + const value: IDashboardContext = useMemo( () => ({ toScrollWidgetId, @@ -435,6 +443,8 @@ export function DashboardProvider({ selectedRowWidgetId, setSelectedRowWidgetId, isDashboardFetching, + columnWidths, + setColumnWidths, }), // eslint-disable-next-line react-hooks/exhaustive-deps [ @@ -457,6 +467,8 @@ export function DashboardProvider({ selectedRowWidgetId, setSelectedRowWidgetId, isDashboardFetching, + columnWidths, + setColumnWidths, ], ); diff --git a/frontend/src/providers/Dashboard/types.ts b/frontend/src/providers/Dashboard/types.ts index c5e5cbca14..13ab53f8e8 100644 --- a/frontend/src/providers/Dashboard/types.ts +++ b/frontend/src/providers/Dashboard/types.ts @@ -10,6 +10,10 @@ export interface DashboardSortOrder { search: string; } +export type WidgetColumnWidths = { + [widgetId: string]: Record; +}; + export interface IDashboardContext { isDashboardSliderOpen: boolean; isDashboardLocked: boolean; @@ -48,4 +52,6 @@ export interface IDashboardContext { selectedRowWidgetId: string | null; setSelectedRowWidgetId: React.Dispatch>; isDashboardFetching: boolean; + columnWidths: WidgetColumnWidths; + setColumnWidths: React.Dispatch>; } diff --git a/frontend/src/types/api/dashboard/getAll.ts b/frontend/src/types/api/dashboard/getAll.ts index 8cfd6f7f9a..d531338eae 100644 --- a/frontend/src/types/api/dashboard/getAll.ts +++ b/frontend/src/types/api/dashboard/getAll.ts @@ -109,6 +109,7 @@ export interface IBaseWidget { selectedLogFields: IField[] | null; selectedTracesFields: BaseAutocompleteData[] | null; isLogScale?: boolean; + columnWidths?: Record; } export interface Widgets extends IBaseWidget { query: Query;