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

[SecuritySolution] Replace dashboard listing with Kibana dashboard listing #155716

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cbeb700
replace dashboard listing
angorayc Apr 25, 2023
ff32ec5
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Apr 25, 2023
576ec4a
styling
angorayc Apr 25, 2023
29f2a65
Merge branch 'dashboard-listing' of github.com:angorayc/kibana into d…
angorayc Apr 25, 2023
2fbf9b8
add fixed tag
angorayc May 16, 2023
7f33c9b
Merge remote-tracking branch 'upstream/main' into dashboard-listing
angorayc May 16, 2023
6fa35ce
disable actions
angorayc May 16, 2023
6b1d0b8
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine May 16, 2023
6a5a791
types
angorayc May 16, 2023
a9a63a5
Merge branch 'dashboard-listing' of github.com:angorayc/kibana into d…
angorayc May 16, 2023
6f04f3b
rm default value
angorayc May 16, 2023
0f04412
rm default value
angorayc May 16, 2023
205a5dc
Merge branch 'main' of github.com:elastic/kibana into dashboard-listing
angorayc May 17, 2023
498ae01
update tags selection behaviour
angorayc May 17, 2023
f92bca0
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine May 17, 2023
c961de4
Merge branch 'main' of github.com:elastic/kibana into dashboard-listing
angorayc May 17, 2023
55df917
Merge branch 'dashboard-listing' of github.com:angorayc/kibana into d…
angorayc May 17, 2023
fe9d202
unit tests
angorayc May 18, 2023
b743ea0
unit tests
angorayc May 18, 2023
eb0b111
fix selection
angorayc May 18, 2023
47ca980
fix types
angorayc May 18, 2023
770cfee
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine May 18, 2023
b797fc0
fix types
angorayc May 18, 2023
23728b5
Merge branch 'dashboard-listing' of github.com:angorayc/kibana into d…
angorayc May 18, 2023
de5584b
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine May 18, 2023
3bbac62
fix types
angorayc May 19, 2023
9581c92
Merge branch 'dashboard-listing' of github.com:angorayc/kibana into d…
angorayc May 19, 2023
3b84127
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine May 19, 2023
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
50 changes: 29 additions & 21 deletions packages/content-management/table_list/src/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import type {
Props as TableListViewProps,
UserContentCommonSchema,
} from '../table_list_view';
import type { TableItemsRowActions } from '../types';
import type { TableItemsRowActions, Tag } from '../types';
import { TableSortSelect } from './table_sort_select';
import { TagFilterPanel } from './tag_filter_panel';
import { useTagFilterPanel } from './use_tag_filter_panel';
Expand All @@ -46,43 +46,47 @@ type TagManagementProps = Pick<
>;

interface Props<T extends UserContentCommonSchema> extends State<T>, TagManagementProps {
clearTagSelection: () => void;
deleteItems: TableListViewProps<T>['deleteItems'];
dispatch: Dispatch<Action<T>>;
entityName: string;
entityNamePlural: string;
isFetchingItems: boolean;
tableCaption: string;
tableColumns: Array<EuiBasicTableColumn<T>>;
fixedTag?: string;
hasUpdatedAtMetadata: boolean;
deleteItems: TableListViewProps<T>['deleteItems'];
tableItemsRowActions: TableItemsRowActions;
isFetchingItems: boolean;
onSortChange: (column: SortColumnField, direction: Direction) => void;
onTableChange: (criteria: CriteriaWithPagination<T>) => void;
onTableSearchChange: (arg: { query: Query | null; queryText: string }) => void;
clearTagSelection: () => void;
tableCaption: string;
tableColumns: Array<EuiBasicTableColumn<T>>;
tableItemsRowActions: TableItemsRowActions;
fixedTagReferences?: Tag[] | null;
}

export function Table<T extends UserContentCommonSchema>({
addOrRemoveExcludeTagFilter,
addOrRemoveIncludeTagFilter,
clearTagSelection,
deleteItems,
dispatch,
items,
entityName,
entityNamePlural,
fixedTag,
hasUpdatedAtMetadata,
isFetchingItems,
items,
onSortChange,
onTableChange,
onTableSearchChange,
pagination,
searchQuery,
selectedIds,
pagination,
tableCaption,
tableColumns,
tableItemsRowActions,
tableSort,
hasUpdatedAtMetadata,
entityName,
entityNamePlural,
tagsToTableItemMap,
tableItemsRowActions,
deleteItems,
tableCaption,
onTableChange,
onTableSearchChange,
onSortChange,
addOrRemoveExcludeTagFilter,
addOrRemoveIncludeTagFilter,
clearTagSelection,
fixedTagReferences,
}: Props<T>) {
const { getTagList } = useServices();

Expand Down Expand Up @@ -151,6 +155,7 @@ export function Table<T extends UserContentCommonSchema>({
tagsToTableItemMap,
addOrRemoveExcludeTagFilter,
addOrRemoveIncludeTagFilter,
fixedTagReferences,
});

const tableSortSelectFilter = useMemo<SearchFilterConfig>(() => {
Expand All @@ -172,6 +177,7 @@ export function Table<T extends UserContentCommonSchema>({
return {
type: 'custom_component',
component: () => {
const disableActions = fixedTagReferences != null;
return (
<TagFilterPanel
isPopoverOpen={isPopoverOpen}
Expand All @@ -182,11 +188,13 @@ export function Table<T extends UserContentCommonSchema>({
onFilterButtonClick={onFilterButtonClick}
onSelectChange={onSelectChange}
clearTagSelection={clearTagSelection}
disableActions={disableActions}
/>
);
},
};
}, [
fixedTagReferences,
isPopoverOpen,
isInUse,
closePopover,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,23 +46,25 @@ const saveBtnWrapperCSS = css`
interface Props {
clearTagSelection: () => void;
closePopover: () => void;
isPopoverOpen: boolean;
disableActions?: boolean;
isInUse: boolean;
options: TagOptionItem[];
totalActiveFilters: number;
isPopoverOpen: boolean;
onFilterButtonClick: () => void;
onSelectChange: (updatedOptions: TagOptionItem[]) => void;
options: TagOptionItem[];
totalActiveFilters: number;
}

export const TagFilterPanel: FC<Props> = ({
isPopoverOpen,
clearTagSelection,
closePopover,
disableActions,
isInUse,
options,
totalActiveFilters,
isPopoverOpen,
onFilterButtonClick,
onSelectChange,
closePopover,
clearTagSelection,
options,
totalActiveFilters,
}) => {
const { euiTheme } = useEuiTheme();
const { navigateToUrl, currentAppId$, getTagManagementUrl } = useServices();
Expand Down Expand Up @@ -122,18 +124,24 @@ export const TagFilterPanel: FC<Props> = ({
<EuiPopoverTitle paddingSize="m" css={popoverTitleCSS}>
<EuiFlexGroup>
<EuiFlexItem>Tags</EuiFlexItem>
<EuiFlexItem grow={false}>
{totalActiveFilters > 0 && (
<EuiButtonEmpty flush="both" onClick={clearTagSelection} css={clearSelectionBtnCSS}>
{i18n.translate(
'contentManagement.tableList.tagFilterPanel.clearSelectionButtonLabelLabel',
{
defaultMessage: 'Clear selection',
}
)}
</EuiButtonEmpty>
)}
</EuiFlexItem>
{!disableActions && (
<EuiFlexItem grow={false}>
{totalActiveFilters > 0 && (
<EuiButtonEmpty
flush="both"
onClick={clearTagSelection}
css={clearSelectionBtnCSS}
>
{i18n.translate(
'contentManagement.tableList.tagFilterPanel.clearSelectionButtonLabelLabel',
{
defaultMessage: 'Clear selection',
}
)}
</EuiButtonEmpty>
)}
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiPopoverTitle>
<EuiSelectable<any>
Expand All @@ -156,6 +164,7 @@ export const TagFilterPanel: FC<Props> = ({
);
}}
</EuiSelectable>

<EuiPopoverFooter paddingSize="m">
<EuiFlexGroup direction="column" alignItems="center" gutterSize="s">
<EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface Params {
getTagList: () => Tag[];
addOrRemoveIncludeTagFilter: (tag: Tag) => void;
addOrRemoveExcludeTagFilter: (tag: Tag) => void;
fixedTagReferences?: Tag[] | null;
}

export const useTagFilterPanel = ({
Expand All @@ -44,6 +45,7 @@ export const useTagFilterPanel = ({
getTagList,
addOrRemoveExcludeTagFilter,
addOrRemoveIncludeTagFilter,
fixedTagReferences,
}: Params) => {
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
// When the panel is "in use" it means that it is opened and the user is interacting with it.
Expand All @@ -59,15 +61,22 @@ export const useTagFilterPanel = ({
(updatedOptions: TagOptionItem[]) => {
// Note: see data flow comment in useEffect() below
const diff = updatedOptions.find((item, index) => item.checked !== options[index].checked);

if (fixedTagReferences?.find((ref) => ref.name === diff?.tag.name)) {
return;
}
if (diff) {
addOrRemoveIncludeTagFilter(diff.tag);
}
},
[options, addOrRemoveIncludeTagFilter]
[fixedTagReferences, options, addOrRemoveIncludeTagFilter]
);

const onOptionClick = useCallback(
(tag: Tag) => (e: MouseEvent) => {
if (fixedTagReferences?.find((ref) => ref.name === tag.name)) {
return;
}
const withModifierKey = (isMac && e.metaKey) || (!isMac && e.ctrlKey);

if (withModifierKey) {
Expand All @@ -76,7 +85,7 @@ export const useTagFilterPanel = ({
addOrRemoveIncludeTagFilter(tag);
}
},
[addOrRemoveIncludeTagFilter, addOrRemoveExcludeTagFilter]
[addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, fixedTagReferences]
);

const updateTagList = useCallback(() => {
Expand Down Expand Up @@ -118,7 +127,7 @@ export const useTagFilterPanel = ({
});

setOptions(tagsToSelectOptions);
}, [getTagList, tagsToTableItemMap, tagSelection, onOptionClick]);
}, [getTagList, tagSelection, onOptionClick, tagsToTableItemMap]);

const onFilterButtonClick = useCallback(() => {
setIsPopoverOpen((prev) => !prev);
Expand Down
108 changes: 107 additions & 1 deletion packages/content-management/table_list/src/table_list_view.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ describe('TableListView', () => {
},
];

test('should have an "inpect" button if the content editor is enabled', async () => {
test('should have an "inspect" button if the content editor is enabled', async () => {
let testBed: TestBed;

await act(async () => {
Expand All @@ -629,6 +629,63 @@ describe('TableListView', () => {
});
});

describe('render PageTemplateHeader', () => {
const hits: UserContentCommonSchema[] = [
{
id: '123',
updatedAt: twoDaysAgo.toISOString(),
type: 'dashboard',
attributes: {
title: 'Item 1',
description: 'Item 1 description',
},
references: [],
},
{
id: '456',
// This is the latest updated and should come first in the table
updatedAt: yesterday.toISOString(),
type: 'dashboard',
attributes: {
title: 'Item 2',
description: 'Item 2 description',
},
references: [],
},
];

test('should render PageTemplateHeader', async () => {
let testBed: TestBed;

await act(async () => {
testBed = await setup({
findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }),
});
});

const { exists, component } = testBed!;
component.update();

expect(exists('top-nav')).toBeTruthy();
});

test('should not render PageTemplateHeader if withPageTemplateHeader is false', async () => {
let testBed: TestBed;

await act(async () => {
testBed = await setup({
findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }),
withPageTemplateHeader: false,
});
});

const { exists, component } = testBed!;
component.update();

expect(exists('top-nav')).toBeFalsy();
});
});

describe('tag filtering', () => {
const setupTagFiltering = registerTestBed<string, TableListViewProps>(
WithServices<TableListViewProps>(TableListView, {
Expand Down Expand Up @@ -671,6 +728,55 @@ describe('TableListView', () => {
},
];

test('should render default query if fixedTagReferences is provided', async () => {
let testBed: TestBed;

const findItems = jest.fn().mockResolvedValue({ total: hits.length, hits });

await act(async () => {
testBed = await setupTagFiltering({
findItems,
fixedTagReferences: [{ id: 'id-tag-1', name: 'tag-1', description: '', color: '' }],
});
});

const { component, find } = testBed!;
component.update();

const getSearchBoxValue = () => find('tableListSearchBox').props().defaultValue;

const expected = 'tag:(tag-1)';

expect(getSearchBoxValue()).toBe(expected);
});

test('should not able to remove the default selected tag if fixedTagReferences is provided', async () => {
let testBed: TestBed;

const findItems = jest.fn().mockResolvedValue({ total: hits.length, hits });

await act(async () => {
testBed = await setupTagFiltering({
findItems,
fixedTagReferences: [{ id: 'id-tag-1', name: 'tag-1', description: '', color: '' }],
});
});

const { component, find } = testBed!;
component.update();

await act(async () => {
find('tag-id-tag-1').simulate('click');
});
component.update();

const getSearchBoxValue = () => find('tableListSearchBox').props().defaultValue;

const expected = 'tag:(tag-1)';

expect(getSearchBoxValue()).toBe(expected);
});

test('should filter by tag from the table', async () => {
let testBed: TestBed;

Expand Down
Loading