Skip to content

Commit

Permalink
[Secuity Solution][DQD] add list view and latest flyout (Phase 1) (#1…
Browse files Browse the repository at this point in the history
…88468)

addresses #185881

## Data Quality Dashboard UI Overhaul (Phase 1)

This PR introduces UI changes to the Data Quality Dashboard, focusing on
improving user experience and improving on existing data quality check
process.

## Notable changes:

- Update List View UI
- Move in-row check expansion into a Flyout
- Remove summary tab from index check 
- Update index check UI
- Add table action to trigger manual individual index check without
opening index check properties
- Add in-flyout button to trigger manual individual check
- Add additional index stats panel within flyout

## Notable technical changes:

- remove prop drilling of new and existing omnipresent props by unifying
them in a series of context providers at the top of DQ dashboard
- introduce TestDataQualityProviders separate from external
TestProviders (renamed to TestExternalProviders) in tests. Change
affected tests.
- introduce `useIndicesCheck` hook to unify logic of index checking to
be able to call index checking from anywhere within DQD code without
relying on flaky and complicated useEffect driven logic of state
updates.
- introduce `useIsMounted`, hook to resolve issues with react state
update leaks
- introduce throttled `useCurrentWindowWidth` to handle custom index
flyout sizing for different screens.
- cleanup and refactor whatever is directly related or affected by
aforementioned code/test changes (including traces of removal of summary
tab)
- add extensive behavioral unit tests

# UI Changes (Before / After)

## List View Layout made more spacious

![image](https://github.com/user-attachments/assets/8cc25285-9a7b-45d9-82f2-55bb7ad4680b)

## Check All Layout made more spacious

![image](https://github.com/user-attachments/assets/c254cfa9-92cf-4971-9737-9c85e253696e)

## Total Stats Rollup converted to badges

![image](https://github.com/user-attachments/assets/68831c84-274d-40d8-b9fa-813e169e8f1e)

## ILM Phase Filter repositioned

![image](https://github.com/user-attachments/assets/85227737-1217-48b9-82b9-dc2d6a24e58c)

## Pattern Title Section

- Rearranged into accordion trigger button. Initially open by default.
- IlmPhase badges and Pattern title are now  stacked horizontally.
- Index check result emoji converted to 'Pass' | 'Fail' badge
- Pattern stats text rearranged as badged text. Incompatible fields show
as red badges (when present), the rest - hollow.


![image](https://github.com/user-attachments/assets/ca3c6e3e-a7ed-4a49-a7a6-9ea4842ac2de)

## Latest Pattern Indices Check Table

- Added a new actions column with 2 actions (from left to right): 
- View details (replaces row expander functionality (and icon) and
instead opens the index check results in a flyout)
- Check now **(NEW)** (adds ability to inline check the index without
opening it.)
- Index check result emoji turned to 'Pass' | 'Fail' badge
- `IlmPhase`, `Size`, `Last Check` columns width is shrunk to give more
space for index name


![image](https://github.com/user-attachments/assets/d9c83d9b-de3a-4153-9ed7-a9823e62f67d)

## Flyout Header

- Added index name with result check badge as title
- Added last check time as subtitle
- Added Tabs section for Latest Check and History **(REMOVED in latest
revision)**


![image](https://github.com/user-attachments/assets/b66617de-2b59-44d5-908b-3fabb3f7087e)

## Flyout Stats Panel

- Added new index stats panel


![image](https://github.com/user-attachments/assets/f21974f5-72b5-4a38-90de-f2ff5b9d1fd0)

## Index Check Fields Tab

- Tabs converted to a button group
- Summary Tab is **REMOVED**
- All field count badges have hollow color, except for red color for
`incompatible fields` tab (when count > 0) and `ecs compliant fields`
tab (when `@timestamp` is missing)


![image](https://github.com/user-attachments/assets/e78218d4-237f-4ad1-95f3-0eb4d57356fa)

## Index Check Fields Callouts

- Callout header is removed (to avoid duplication with active tab name)
- Actions are converted into sticky footer (shows when scrolled sticky
to bottom, otherwise renders after the table)
- Same for every index check fields tab


![image](https://github.com/user-attachments/assets/d8a192e8-cdfd-4691-b862-91abdedcb9b8)

## Compare Table List Values

- List values in compare tables are now horizontally stacked instead of
vertical to save space (applies to all compare tables in each index
check fields tab)


![image](https://github.com/user-attachments/assets/7ef3341d-1be9-41e7-a789-15262fac6de6)

## Compare Table Columns

- `ECS description` field width increased at the expense of `field`
field, to make room for more readable description (applies respectively
to all compare tables within index check fields tabs)


![image](https://github.com/user-attachments/assets/b45235a9-cf3d-4b6d-b65a-eaadc577cf2f)

## Flyout footer

- Add `Check now` button, that checks currently open index again and
updates the results in place.


![image](https://github.com/user-attachments/assets/02b9a63d-6c0c-4eff-aeb6-452ae78f28ea)
  • Loading branch information
kapral18 authored Aug 21, 2024
1 parent 8d4704f commit 51764fa
Show file tree
Hide file tree
Showing 172 changed files with 6,045 additions and 4,975 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ import { render, screen } from '@testing-library/react';
import React from 'react';

import { mockAllowedValues } from '../../mock/allowed_values/mock_allowed_values';
import { TestProviders } from '../../mock/test_providers/test_providers';
import { TestExternalProviders } from '../../mock/test_providers/test_providers';
import { EcsAllowedValues } from '.';

describe('EcsAllowedValues', () => {
describe('when `allowedValues` exists', () => {
beforeEach(() => {
render(
<TestProviders>
<TestExternalProviders>
<EcsAllowedValues allowedValues={mockAllowedValues} />
</TestProviders>
</TestExternalProviders>
);
});

Expand All @@ -36,9 +36,9 @@ describe('EcsAllowedValues', () => {
describe('when `allowedValues` is undefined', () => {
beforeEach(() => {
render(
<TestProviders>
<TestExternalProviders>
<EcsAllowedValues allowedValues={undefined} />
</TestProviders>
</TestExternalProviders>
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,11 @@

import { EuiCode, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';

import { EMPTY_PLACEHOLDER } from '../helpers';
import { CodeSuccess } from '../../styles';
import type { AllowedValue } from '../../types';

const EcsAllowedValueFlexItem = styled(EuiFlexItem)`
margin-bottom: ${({ theme }) => theme.eui.euiSizeXS};
`;

interface Props {
allowedValues: AllowedValue[] | undefined;
}
Expand All @@ -25,11 +20,11 @@ const EcsAllowedValuesComponent: React.FC<Props> = ({ allowedValues }) =>
allowedValues == null ? (
<EuiCode data-test-subj="ecsAllowedValuesEmpty">{EMPTY_PLACEHOLDER}</EuiCode>
) : (
<EuiFlexGroup data-test-subj="ecsAllowedValues" direction="column" gutterSize="none">
<EuiFlexGroup data-test-subj="ecsAllowedValues" direction="row" wrap={true} gutterSize="xs">
{allowedValues.map((x, i) => (
<EcsAllowedValueFlexItem grow={false} key={`${x.name}_${i}`}>
<EuiFlexItem grow={false} key={`${x.name}_${i}`}>
<CodeSuccess>{x.name}</CodeSuccess>
</EcsAllowedValueFlexItem>
</EuiFlexItem>
))}
</EuiFlexGroup>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
someField,
eventCategoryWithUnallowedValues,
} from '../../mock/enriched_field_metadata/mock_enriched_field_metadata';
import { TestProviders } from '../../mock/test_providers/test_providers';
import { TestExternalProviders } from '../../mock/test_providers/test_providers';
import {
DOCUMENT_VALUES_ACTUAL,
ECS_DESCRIPTION,
Expand All @@ -30,7 +30,7 @@ import { EMPTY_PLACEHOLDER, getCommonTableColumns } from '.';
describe('getCommonTableColumns', () => {
test('it returns the expected column configuration', () => {
expect(getCommonTableColumns().map((x) => omit('render', x))).toEqual([
{ field: 'indexFieldName', name: FIELD, sortable: true, truncateText: false, width: '20%' },
{ field: 'indexFieldName', name: FIELD, sortable: true, truncateText: false, width: '15%' },
{
field: 'type',
name: ECS_MAPPING_TYPE_EXPECTED,
Expand Down Expand Up @@ -64,7 +64,7 @@ describe('getCommonTableColumns', () => {
name: ECS_DESCRIPTION,
sortable: false,
truncateText: false,
width: '20%',
width: '25%',
},
]);
});
Expand All @@ -76,9 +76,9 @@ describe('getCommonTableColumns', () => {
const expected = 'keyword';

render(
<TestProviders>
<TestExternalProviders>
{typeColumnRender != null && typeColumnRender(eventCategory.type, eventCategory)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeSuccess')).toHaveTextContent(expected);
Expand All @@ -89,9 +89,9 @@ describe('getCommonTableColumns', () => {
const typeColumnRender = columns[1].render;

render(
<TestProviders>
<TestExternalProviders>
{typeColumnRender != null && typeColumnRender(undefined, eventCategory)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeSuccess')).toHaveTextContent(EMPTY_PLACEHOLDER);
Expand All @@ -113,13 +113,13 @@ describe('getCommonTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{indexFieldTypeColumnRender != null &&
indexFieldTypeColumnRender(
withTypeMismatchSameFamily.indexFieldType,
withTypeMismatchSameFamily
)}
</TestProviders>
</TestExternalProviders>
);
});

Expand All @@ -146,13 +146,13 @@ describe('getCommonTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{indexFieldTypeColumnRender != null &&
indexFieldTypeColumnRender(
withTypeMismatchDifferentFamily.indexFieldType,
withTypeMismatchDifferentFamily
)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeDanger')).toHaveTextContent(indexFieldType);
Expand All @@ -165,10 +165,10 @@ describe('getCommonTableColumns', () => {
const indexFieldTypeColumnRender = columns[2].render;

render(
<TestProviders>
<TestExternalProviders>
{indexFieldTypeColumnRender != null &&
indexFieldTypeColumnRender(eventCategory.indexFieldType, eventCategory)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeSuccess')).toHaveTextContent(eventCategory.indexFieldType);
Expand All @@ -187,10 +187,10 @@ describe('getCommonTableColumns', () => {
: 'unexpected';

render(
<TestProviders>
<TestExternalProviders>
{allowedValuesolumnRender != null &&
allowedValuesolumnRender(eventCategory.allowed_values, eventCategory)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('ecsAllowedValues')).toHaveTextContent(expectedAllowedValuesNames);
Expand All @@ -206,10 +206,10 @@ describe('getCommonTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{allowedValuesolumnRender != null &&
allowedValuesolumnRender(undefined, withUndefinedAllowedValues)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('ecsAllowedValuesEmpty')).toHaveTextContent(EMPTY_PLACEHOLDER);
Expand All @@ -222,13 +222,13 @@ describe('getCommonTableColumns', () => {
const indexInvalidValuesRender = columns[4].render;

render(
<TestProviders>
<TestExternalProviders>
{indexInvalidValuesRender != null &&
indexInvalidValuesRender(
eventCategoryWithUnallowedValues.indexInvalidValues,
eventCategoryWithUnallowedValues
)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('indexInvalidValues')).toHaveTextContent(
Expand All @@ -249,10 +249,10 @@ describe('getCommonTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{descriptionolumnRender != null &&
descriptionolumnRender(withDescription.description, withDescription)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('description')).toHaveTextContent(expectedDescription);
Expand All @@ -263,9 +263,9 @@ describe('getCommonTableColumns', () => {
const descriptionolumnRender = columns[5].render;

render(
<TestProviders>
<TestExternalProviders>
{descriptionolumnRender != null && descriptionolumnRender(undefined, someField)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('emptyDescription')).toHaveTextContent(EMPTY_PLACEHOLDER);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const getCommonTableColumns = (): Array<
name: i18n.FIELD,
sortable: true,
truncateText: false,
width: '20%',
width: '15%',
},
{
field: 'type',
Expand Down Expand Up @@ -98,6 +98,6 @@ export const getCommonTableColumns = (): Array<
),
sortable: false,
truncateText: false,
width: '20%',
width: '25%',
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { omit } from 'lodash/fp';
import React from 'react';

import { SAME_FAMILY } from '../../data_quality_panel/same_family/translations';
import { TestProviders } from '../../mock/test_providers/test_providers';
import { TestExternalProviders } from '../../mock/test_providers/test_providers';
import { eventCategory } from '../../mock/enriched_field_metadata/mock_enriched_field_metadata';
import { EcsBasedFieldMetadata } from '../../types';
import { getIncompatibleMappingsTableColumns } from '.';
Expand All @@ -25,7 +25,7 @@ describe('getIncompatibleMappingsTableColumns', () => {
name: 'Field',
sortable: true,
truncateText: false,
width: '25%',
width: '15%',
},
{
field: 'type',
Expand All @@ -46,7 +46,7 @@ describe('getIncompatibleMappingsTableColumns', () => {
name: 'ECS description',
sortable: false,
truncateText: false,
width: '25%',
width: '35%',
},
]);
});
Expand All @@ -58,9 +58,9 @@ describe('getIncompatibleMappingsTableColumns', () => {
const expected = 'keyword';

render(
<TestProviders>
<TestExternalProviders>
{typeColumnRender != null && typeColumnRender(eventCategory.type, eventCategory)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeSuccess')).toHaveTextContent(expected);
Expand All @@ -82,13 +82,13 @@ describe('getIncompatibleMappingsTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{indexFieldTypeColumnRender != null &&
indexFieldTypeColumnRender(
withTypeMismatchSameFamily.indexFieldType,
withTypeMismatchSameFamily
)}
</TestProviders>
</TestExternalProviders>
);
});

Expand All @@ -115,13 +115,13 @@ describe('getIncompatibleMappingsTableColumns', () => {
};

render(
<TestProviders>
<TestExternalProviders>
{indexFieldTypeColumnRender != null &&
indexFieldTypeColumnRender(
withTypeMismatchDifferentFamily.indexFieldType,
withTypeMismatchDifferentFamily
)}
</TestProviders>
</TestExternalProviders>
);

expect(screen.getByTestId('codeDanger')).toHaveTextContent(indexFieldType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const getIncompatibleMappingsTableColumns = (): Array<
name: i18n.FIELD,
sortable: true,
truncateText: false,
width: '25%',
width: '15%',
},
{
field: 'type',
Expand Down Expand Up @@ -54,6 +54,6 @@ export const getIncompatibleMappingsTableColumns = (): Array<
name: i18n.ECS_DESCRIPTION,
sortable: false,
truncateText: false,
width: '25%',
width: '35%',
},
];
Loading

0 comments on commit 51764fa

Please sign in to comment.