Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discover] [Unified Data Table] Add document comparison mode #166577

Merged
merged 73 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ee654f1
Add initial implementation of document comparison
davismcphee Sep 16, 2023
55bc9a4
Fixing some bugs and display issues
davismcphee Sep 16, 2023
c4a5e7a
Add support for pinning and removing docs
davismcphee Sep 16, 2023
6378993
Add advanced diffing
davismcphee Sep 16, 2023
9ae16ec
Add support for selecting diff modes
davismcphee Sep 16, 2023
3108dd2
Add support for character and word diffs
davismcphee Sep 16, 2023
2f58b14
Improve line diff indicators
davismcphee Sep 16, 2023
91cb6c7
Use local storage for diff options
davismcphee Sep 16, 2023
f591305
Fine tuning diff display
davismcphee Sep 16, 2023
649269e
Separate CompareDocuments into a separate component
davismcphee Sep 16, 2023
e1cf7eb
Cleanup
davismcphee Sep 16, 2023
05f319b
More cleanup
davismcphee Sep 16, 2023
dd49b22
Add i18n strings
davismcphee Sep 16, 2023
76214ee
Create useComparisonColumns
davismcphee Sep 16, 2023
339df3c
Create useComparisonFields hook
davismcphee Sep 16, 2023
82c517e
Add ComparisonControls component
davismcphee Sep 16, 2023
c846b66
Clean up ComparisonControls
davismcphee Sep 16, 2023
c36af69
Create useComparisonCellValue hook
davismcphee Sep 17, 2023
a951d10
Clean up useComparisonCellValue
davismcphee Sep 17, 2023
02105d3
Clean up imports
davismcphee Sep 17, 2023
bf77631
Add column auto sizing
davismcphee Sep 17, 2023
0ca65d8
Remove row hover
davismcphee Sep 17, 2023
8f901f5
Hide data table footer when in comaprison mode
davismcphee Sep 18, 2023
09e1acc
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Sep 18, 2023
1178039
Cleanup after rebase
davismcphee Nov 27, 2023
f0dc9fc
Add white-space pre-wrap to comparison cells
davismcphee Nov 27, 2023
bdd73d5
Fix issues from merge
davismcphee Mar 20, 2024
c9f6362
Update doc comparison toolbar styles
davismcphee Mar 21, 2024
a3ca2be
Add pin icon and improve grid styles
davismcphee Mar 21, 2024
885324e
Add support for renderCustomComparisonToolbar
davismcphee Mar 21, 2024
cd2b34b
Increase margin between compare doc count display, and add row hover …
davismcphee Mar 22, 2024
08b9291
Improve typings
davismcphee Mar 22, 2024
e9bc6c5
Add ability to reorder comparison docs
davismcphee Mar 22, 2024
90b7449
Allow only showing field rows where values have differences
davismcphee Mar 23, 2024
fa9055b
Move comparison styles to dedicatd hook to improve performance
davismcphee Mar 23, 2024
1de4bc5
Use 'records' for comparison title in ES|QL mode
davismcphee Mar 23, 2024
5a36bff
Hide empty fields when showing full field list
davismcphee Mar 23, 2024
1178a62
Update copy to use 'records' in ES|QL mode
davismcphee Mar 23, 2024
5d18870
Improve ES|QL column header display, and update 'records' to 'results'
davismcphee Mar 23, 2024
4f27c08
Always show comparison settings
davismcphee Mar 25, 2024
7bb90d7
Limit comparison mode to 100 fields for performance
davismcphee Mar 26, 2024
94ed425
Selectively enable comparison mode in Unified Data Table
davismcphee Mar 26, 2024
1dfa779
Fix broken test types
davismcphee Mar 26, 2024
cb51f7e
Add initial Jest tests
davismcphee Mar 27, 2024
364958d
Remove 'Show diff' button and replace it with a diff mode 'None' option
davismcphee Mar 27, 2024
82847d1
Adding more Jest tests
davismcphee Mar 27, 2024
284a2ab
Fix issue where wrong doc IDs were shown in column headers
davismcphee Mar 28, 2024
ebcdde5
Refactor use_comparison_cell_value to improve performance, show '-' f…
davismcphee Mar 29, 2024
a58f4b7
Add diff memoization and additional tests
davismcphee Mar 29, 2024
582b229
Add remaining Jest tests
davismcphee Mar 30, 2024
7797d3c
Add comparison functional tests
davismcphee Mar 31, 2024
faa6dc4
Add ES|QL comparison functional tests
davismcphee Mar 31, 2024
228db60
Fix snapshot
davismcphee Mar 31, 2024
9695f72
Update functional test causing failures
davismcphee Mar 31, 2024
8a46871
One more functional test fix
davismcphee Mar 31, 2024
1a16b74
Fixing functional test
davismcphee Apr 1, 2024
9d8d3b2
Switch 'row' to 'result' in Unified Doc Viewer
davismcphee Apr 1, 2024
8d9c3d2
Merge branch 'main' into discover-doc-compare
davismcphee Apr 3, 2024
31df11b
Apply PR feedback
davismcphee Apr 4, 2024
b2a6bd5
Move render_custom_toolbar to Unified Data Table
davismcphee Apr 4, 2024
06d53cc
Merge branch 'main' into discover-doc-compare
davismcphee Apr 5, 2024
7cc4023
Move compare button to toolbar
davismcphee Apr 6, 2024
ca6755c
Add show diff switch and update comparison settings menu design
davismcphee Apr 6, 2024
9e63710
Update comparison tooltips and column headers
davismcphee Apr 6, 2024
243a8ca
Update i18n values
davismcphee Apr 6, 2024
bc24f98
Update functional test table headers
davismcphee Apr 7, 2024
740856b
Merge branch 'main' into discover-doc-compare
davismcphee Apr 7, 2024
76cb00f
Merge branch 'main' into discover-doc-compare
davismcphee Apr 8, 2024
f8e9a71
Remove 'Document' prefix from column headers
davismcphee Apr 8, 2024
da20ce9
Remove custom badge styles
davismcphee Apr 8, 2024
4050791
Disable diff mode buttons and show diff decorations switch when show …
davismcphee Apr 9, 2024
8c5bcaa
Fix issue where docs are removed from comparison when the main table …
davismcphee Apr 9, 2024
5e021ba
Merge branch 'main' into discover-doc-compare
davismcphee Apr 10, 2024
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
37 changes: 37 additions & 0 deletions packages/kbn-discover-utils/src/__mocks__/es_hits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
* Side Public License, v 1.
*/

import type { DataView, DataViewField } from '@kbn/data-views-plugin/common';
import { KBN_FIELD_TYPES } from '@kbn/field-types';
import type { EsHitRecord } from '../types';

export const esHitsMock = [
{
_index: 'i',
Expand Down Expand Up @@ -54,3 +58,36 @@ export const esHitsMockWithSort = esHitsMock.map((hit) => ({
...hit,
sort: [hit._source.date], // some `sort` param should be specified for "fetch more" to work
}));

const baseDate = new Date('2024-01-1').getTime();
const dateInc = 100_000_000;

const generateFieldValue = (field: DataViewField, index: number) => {
switch (field.type) {
case KBN_FIELD_TYPES.BOOLEAN:
return index % 2 === 0;
case KBN_FIELD_TYPES.DATE:
return new Date(baseDate + index * dateInc).toISOString();
case KBN_FIELD_TYPES.NUMBER:
return Array.from(field.name).reduce((sum, char) => sum + char.charCodeAt(0) + index, 0);
case KBN_FIELD_TYPES.STRING:
return `${field.name}_${index}`;
default:
throw new Error(`Unsupported type ${field.type}`);
}
};

export const generateEsHits = (dataView: DataView, count: number): EsHitRecord[] => {
return Array.from({ length: count }, (_, i) => ({
_index: 'i',
_id: i.toString(),
_score: 1,
fields: dataView.fields.reduce<Record<string, any>>(
(source, field) => ({
...source,
[field.name]: [generateFieldValue(field, i)],
}),
{}
),
}));
};
15 changes: 10 additions & 5 deletions packages/kbn-unified-data-table/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@
*/

export { UnifiedDataTable, DataLoadingState } from './src/components/data_table';
export type {
UnifiedDataTableProps,
UnifiedDataTableRenderCustomToolbar,
UnifiedDataTableRenderCustomToolbarProps,
} from './src/components/data_table';
export type { UnifiedDataTableProps } from './src/components/data_table';
export {
RowHeightSettings,
type RowHeightSettingsProps,
Expand All @@ -31,3 +27,12 @@ export { popularizeField } from './src/utils/popularize_field';
export { useColumns } from './src/hooks/use_data_grid_columns';
export { OPEN_DETAILS, SELECT_ROW } from './src/components/data_table_columns';
export { DataTableRowControl } from './src/components/data_table_row_control';

export type {
UnifiedDataTableRenderCustomToolbar,
UnifiedDataTableRenderCustomToolbarProps,
} from './src/components/custom_toolbar/render_custom_toolbar';
export {
getRenderCustomToolbarWithElements,
renderCustomToolbar,
} from './src/components/custom_toolbar/render_custom_toolbar';
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { EuiDataGridProps } from '@elastic/eui';
import { buildDataTableRecord } from '@kbn/discover-utils';
import { generateEsHits } from '@kbn/discover-utils/src/__mocks__';
import { render } from '@testing-library/react';
import { omit } from 'lodash';
import React from 'react';
import { dataViewWithTimefieldMock } from '../../../__mocks__/data_view_with_timefield';
import CompareDocuments, { CompareDocumentsProps } from './compare_documents';
import { useComparisonFields } from './hooks/use_comparison_fields';

let mockLocalStorage: Record<string, string> = {};

jest.mock('react-use/lib/useLocalStorage', () =>
jest.fn((key: string, value: unknown) => {
mockLocalStorage[key] = JSON.stringify(value);
return [value, jest.fn()];
})
);

let mockDataGridProps: EuiDataGridProps | undefined;

jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
EuiDataGrid: jest.fn((props) => {
mockDataGridProps = props;
return <></>;
}),
}));

jest.mock('./hooks/use_comparison_fields', () => {
const originalModule = jest.requireActual('./hooks/use_comparison_fields');
return {
...originalModule,
useComparisonFields: jest.fn(originalModule.useComparisonFields),
};
});

const docs = generateEsHits(dataViewWithTimefieldMock, 5).map((hit) =>
buildDataTableRecord(hit, dataViewWithTimefieldMock)
);

const getDocById = (id: string) => docs.find((doc) => doc.raw._id === id);

const renderCompareDocuments = ({
forceShowAllFields = false,
}: { forceShowAllFields?: boolean } = {}) => {
const setSelectedDocs = jest.fn();
const getCompareDocuments = (props?: Partial<CompareDocumentsProps>) => (
<CompareDocuments
id="test"
wrapper={document.body}
consumer="test"
ariaDescribedBy="test"
ariaLabelledBy="test"
dataView={dataViewWithTimefieldMock}
isPlainRecord={false}
selectedFieldNames={['message', 'extension', 'bytes']}
selectedDocs={['0', '1', '2']}
schemaDetectors={[]}
forceShowAllFields={forceShowAllFields}
showFullScreenButton={true}
fieldFormats={{} as any}
getDocById={getDocById}
setSelectedDocs={setSelectedDocs}
setIsCompareActive={jest.fn()}
{...props}
/>
);
const { rerender } = render(getCompareDocuments());
return {
setSelectedDocs,
rerender: (props?: Partial<CompareDocumentsProps>) => rerender(getCompareDocuments(props)),
};
};

describe('CompareDocuments', () => {
beforeEach(() => {
mockLocalStorage = {};
mockDataGridProps = undefined;
});

it('should pass expected grid props', () => {
renderCompareDocuments();
expect(mockDataGridProps).toBeDefined();
expect(mockDataGridProps?.columns).toBeDefined();
expect(mockDataGridProps?.css).toBeDefined();
expect(omit(mockDataGridProps, 'columns', 'css')).toMatchInlineSnapshot(`
Object {
"aria-describedby": "test",
"aria-labelledby": "test",
"columnVisibility": Object {
"setVisibleColumns": [Function],
"visibleColumns": Array [
"fields_generated-id",
"0",
"1",
"2",
],
},
"data-test-subj": "unifiedDataTableCompareDocuments",
"gridStyle": Object {
"border": "horizontal",
"cellPadding": "l",
"fontSize": "s",
"header": "underline",
"rowHover": "highlight",
"stripes": undefined,
},
"id": "test",
"inMemory": Object {
"level": "sorting",
},
"renderCellValue": [Function],
"renderCustomToolbar": [Function],
"rowCount": 3,
"rowHeightsOptions": Object {
"defaultHeight": "auto",
},
"schemaDetectors": Array [],
"toolbarVisibility": Object {
"showColumnSelector": false,
"showDisplaySelector": false,
"showFullScreenSelector": true,
},
}
`);
});

it('should get values from local storage', () => {
renderCompareDocuments();
expect(mockLocalStorage).toEqual({
'test:dataGridComparisonDiffMode': '"basic"',
'test:dataGridComparisonShowAllFields': 'false',
'test:dataGridComparisonShowDiff': 'true',
'test:dataGridComparisonShowDiffDecorations': 'true',
'test:dataGridComparisonShowMatchingValues': 'true',
});
});

it('should set selected docs when columns change', () => {
const { setSelectedDocs } = renderCompareDocuments();
const visibleColumns = ['fields_generated-id', '0', '1', '2'];
mockDataGridProps?.columnVisibility.setVisibleColumns(visibleColumns);
expect(setSelectedDocs).toHaveBeenCalledWith(visibleColumns.slice(1));
});

it('should force show all fields when prop is true', () => {
renderCompareDocuments();
expect(useComparisonFields).toHaveBeenLastCalledWith(
expect.objectContaining({ showAllFields: false })
);
renderCompareDocuments({ forceShowAllFields: true });
expect(useComparisonFields).toHaveBeenLastCalledWith(
expect.objectContaining({ showAllFields: true })
);
});

it('should retain comparison docs when getDocById loses access to them', () => {
const { rerender } = renderCompareDocuments();
const visibleColumns = ['fields_generated-id', '0', '1', '2'];
expect(mockDataGridProps?.columnVisibility.visibleColumns).toEqual(visibleColumns);
rerender({ getDocById: () => undefined });
expect(mockDataGridProps?.columnVisibility.visibleColumns).toEqual(visibleColumns);
});
});
Loading