Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import React, { memo, useMemo, useCallback, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';

import type { DataTableRecord } from '@kbn/discover-utils/types';
import type { UnifiedDataTableProps } from '@kbn/unified-data-table';
import type {
UnifiedDataTableProps,
UnifiedDataTableSettingsColumn,
} from '@kbn/unified-data-table';
import { UnifiedDataTable, DataLoadingState } from '@kbn/unified-data-table';
import type { DataView } from '@kbn/data-views-plugin/public';
import type {
Expand Down Expand Up @@ -147,9 +150,24 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(

const showTimeCol = useMemo(() => !!dataView && !!dataView.timeFieldName, [dataView]);

const { rowHeight, sampleSize, excludedRowRendererIds } = useSelector((state: State) =>
selectTimelineById(state, timelineId)
);
const {
rowHeight,
sampleSize,
excludedRowRendererIds,
columns: timelineColumns,
} = useSelector((state: State) => selectTimelineById(state, timelineId));

const settings: UnifiedDataTableProps['settings'] = useMemo(() => {
const _columns: Record<string, UnifiedDataTableSettingsColumn> = {};
timelineColumns.forEach((timelineColumn) => {
_columns[timelineColumn.id] = {
width: timelineColumn.initialWidth ?? undefined,
};
});
return {
columns: _columns,
};
}, [timelineColumns]);

const { tableRows, tableStylesOverride } = useMemo(
() => transformTimelineItemToUnifiedRows({ events, dataView }),
Expand Down Expand Up @@ -192,27 +210,19 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
[tableRows, handleOnEventDetailPanelOpened, closeFlyout]
);

const onColumnResize = useCallback(
({ columnId, width }: { columnId: string; width?: number }) => {
dispatch(
timelineActions.updateColumnWidth({
columnId,
id: timelineId,
width, // initialWidth?
})
);
},
[dispatch, timelineId]
);

const onResizeDataGrid = useCallback<NonNullable<UnifiedDataTableProps['onResize']>>(
(colSettings) => {
onColumnResize({
columnId: colSettings.columnId,
...(colSettings.width ? { width: Math.round(colSettings.width) } : {}),
});
if (colSettings.width) {
dispatch(
timelineActions.updateColumnWidth({
columnId: colSettings.columnId,
id: timelineId,
width: Math.round(colSettings.width),
})
);
}
},
[onColumnResize]
[dispatch, timelineId]
);

const onChangeItemsPerPage = useCallback<
Expand Down Expand Up @@ -425,6 +435,7 @@ export const TimelineDataTableComponent: React.FC<DataTableProps> = memo(
externalControlColumns={leadingControlColumns}
onUpdatePageIndex={onUpdatePageIndex}
getRowIndicator={getTimelineRowTypeIndicator}
settings={settings}
/>
</StyledTimelineUnifiedDataTable>
</StatefulEventContext.Provider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,6 @@ export const updateItemsPerPageOptions = actionCreator<{
itemsPerPageOptions: number[];
}>('UPDATE_ITEMS_PER_PAGE_OPTIONS');

export const applyDeltaToColumnWidth = actionCreator<{
id: string;
columnId: string;
delta: number;
}>('APPLY_DELTA_TO_COLUMN_WIDTH');

export const clearEventsLoading = actionCreator<{
id: string;
}>('CLEAR_TGRID_EVENTS_LOADING');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,12 @@ import {
defaultUdtHeaders,
defaultColumnHeaderType,
} from '../components/timeline/body/column_headers/default_headers';
import {
DEFAULT_COLUMN_MIN_WIDTH,
RESIZED_COLUMN_MIN_WITH,
} from '../components/timeline/body/constants';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../components/timeline/body/constants';
import { defaultHeaders } from '../../common/mock';
import {
addNewTimeline,
addTimelineProviders,
addTimelineToStore,
applyDeltaToTimelineColumnWidth,
removeTimelineColumn,
removeTimelineProvider,
updateTimelineColumns,
Expand All @@ -45,14 +41,18 @@ import {
updateTimelineShowTimeline,
updateTimelineSort,
updateTimelineTitleAndDescription,
upsertTimelineColumn,
updateTimelineGraphEventId,
updateTimelineColumnWidth,
upsertTimelineColumn,
} from './helpers';
import type { TimelineModel } from './model';
import { timelineDefaults } from './defaults';
import type { TimelineById } from './types';
import { Direction } from '../../../common/search_strategy';
import {
type LocalStorageColumnSettings,
setStoredTimelineColumnsConfig,
} from './middlewares/timeline_localstorage';

jest.mock('../../common/utils/normalize_time_range');
jest.mock('../../common/utils/default_date_settings', () => {
Expand Down Expand Up @@ -157,6 +157,10 @@ const columnsMock: ColumnHeaderOptions[] = [
];

describe('Timeline', () => {
beforeEach(() => {
setStoredTimelineColumnsConfig(undefined);
});

describe('#add saved object Timeline to store ', () => {
test('should return a timelineModel with default value and not just a timelineResult ', () => {
const update = addTimelineToStore({
Expand All @@ -175,6 +179,47 @@ describe('Timeline', () => {
});
});

test('should apply the locally stored column config', () => {
const initialWidth = 123456789;
const storedConfig: LocalStorageColumnSettings = {
'@timestamp': {
id: '@timestamp',
initialWidth,
},
};
setStoredTimelineColumnsConfig(storedConfig);
const update = addTimelineToStore({
id: 'foo',
timeline: {
...basicTimeline,
columns: [{ id: '@timestamp', columnHeaderType: 'not-filtered' }],
},
timelineById: timelineByIdMock,
});

expect(update.foo.columns.find((col) => col.id === '@timestamp')).toEqual(
expect.objectContaining({
initialWidth,
})
);
});

test('should not apply changes to the columns when no previous config is stored in localStorage', () => {
const update = addTimelineToStore({
id: 'foo',
timeline: {
...basicTimeline,
columns: [{ id: '@timestamp', columnHeaderType: 'not-filtered' }],
},
timelineById: timelineByIdMock,
});

expect(update.foo.columns.find((col) => col.id === '@timestamp')).toEqual({
id: '@timestamp',
columnHeaderType: 'not-filtered',
});
});

test('should override timerange if adding an immutable template', () => {
const update = addTimelineToStore({
id: 'foo',
Expand Down Expand Up @@ -458,6 +503,49 @@ describe('Timeline', () => {

expect(update.foo.columns).toEqual(expectedColumns);
});

test('should apply the locally stored column config to new columns', () => {
const initialWidth = 123456789;
const storedConfig: LocalStorageColumnSettings = {
'event.action': {
id: 'event.action',
initialWidth,
},
};
setStoredTimelineColumnsConfig(storedConfig);
const expectedColumns = [{ ...columnToAdd, initialWidth }];
const update = upsertTimelineColumn({
column: columnToAdd,
id: 'foo',
index: 0,
timelineById,
});

expect(update.foo.columns).toEqual(expectedColumns);
});

test('should apply the locally stored column config to existing columns', () => {
const initialWidth = 123456789;
const storedConfig: LocalStorageColumnSettings = {
'@timestamp': {
id: '@timestamp',
initialWidth,
},
};
setStoredTimelineColumnsConfig(storedConfig);
const update = upsertTimelineColumn({
column: columns[0],
id: 'foo',
index: 0,
timelineById: mockWithExistingColumns,
});

expect(update.foo.columns.find((col) => col.id === '@timestamp')).toEqual(
expect.objectContaining({
initialWidth,
})
);
});
});

describe('#addTimelineProvider', () => {
Expand Down Expand Up @@ -599,87 +687,6 @@ describe('Timeline', () => {
});
});

describe('#applyDeltaToColumnWidth', () => {
let mockWithExistingColumns: TimelineById;
beforeEach(() => {
mockWithExistingColumns = {
...timelineByIdMock,
foo: {
...timelineByIdMock.foo,
columns: columnsMock,
},
};
});
test('should return a new reference and not the same reference', () => {
const delta = 50;
const update = applyDeltaToTimelineColumnWidth({
id: 'foo',
columnId: columnsMock[0].id,
delta,
timelineById: mockWithExistingColumns,
});

expect(update).not.toBe(timelineByIdMock);
});

test('should update initialWidth with the specified delta when the delta is positive', () => {
const aDateColumn = columnsMock[0];
const delta = 50;
const expectedToHaveNewWidth = {
...aDateColumn,
initialWidth: Number(aDateColumn.initialWidth) + 50,
};
const expectedColumns = [expectedToHaveNewWidth, columnsMock[1], columnsMock[2]];

const update = applyDeltaToTimelineColumnWidth({
id: 'foo',
columnId: aDateColumn.id,
delta,
timelineById: mockWithExistingColumns,
});

expect(update.foo.columns).toEqual(expectedColumns);
});

test('should update initialWidth with the specified delta when the delta is negative, and the resulting width is greater than the min column width', () => {
const aDateColumn = columnsMock[0];
const delta = 50 * -1; // the result will still be above the min column size
const expectedToHaveNewWidth = {
...aDateColumn,
initialWidth: Number(aDateColumn.initialWidth) - 50,
};
const expectedColumns = [expectedToHaveNewWidth, columnsMock[1], columnsMock[2]];

const update = applyDeltaToTimelineColumnWidth({
id: 'foo',
columnId: aDateColumn.id,
delta,
timelineById: mockWithExistingColumns,
});

expect(update.foo.columns).toEqual(expectedColumns);
});

test('should set initialWidth to `RESIZED_COLUMN_MIN_WITH` when the requested delta results in a column that is too small ', () => {
const aDateColumn = columnsMock[0];
const delta = (Number(aDateColumn.initialWidth) - 5) * -1; // the requested delta would result in a width of just 5 pixels, which is too small
const expectedToHaveNewWidth = {
...aDateColumn,
initialWidth: RESIZED_COLUMN_MIN_WITH, // we expect the minimum
};
const expectedColumns = [expectedToHaveNewWidth, columnsMock[1], columnsMock[2]];

const update = applyDeltaToTimelineColumnWidth({
id: 'foo',
columnId: aDateColumn.id,
delta,
timelineById: mockWithExistingColumns,
});

expect(update.foo.columns).toEqual(expectedColumns);
});
});

describe('#addAndProviderToTimelineProvider', () => {
test('should add a new and provider to an existing timeline provider', () => {
const providerToAdd: DataProvider[] = [
Expand Down Expand Up @@ -861,6 +868,28 @@ describe('Timeline', () => {
});
expect(update.foo.columns).toEqual([...columnsMock]);
});

test('should apply the locally stored column config', () => {
const initialWidth = 123456789;
const storedConfig: LocalStorageColumnSettings = {
'@timestamp': {
id: '@timestamp',
initialWidth,
},
};
setStoredTimelineColumnsConfig(storedConfig);
const update = updateTimelineColumns({
id: 'foo',
columns: columnsMock,
timelineById: timelineByIdMock,
});

expect(update.foo.columns.find((col) => col.id === '@timestamp')).toEqual(
expect.objectContaining({
initialWidth,
})
);
});
});

describe('#updateTimelineTitleAndDescription', () => {
Expand Down
Loading