From cbeb7004dc6554880c7b97d5393bcf581d68851b Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 25 Apr 2023 13:53:47 +0100 Subject: [PATCH 01/20] replace dashboard listing --- .../dashboards/pages/landing_page/index.tsx | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index d6d9be365fc30..3bb6d09d869b8 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -7,7 +7,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } from '@elastic/eui'; import React from 'react'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; -import { LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; +import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { DashboardsTable } from '../../../common/components/dashboards/dashboards_table'; @@ -21,6 +21,7 @@ import { LinkButton } from '../../../common/components/links/helpers'; import * as i18n from './translations'; import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../common/lib/telemetry'; import { DASHBOARDS_PAGE_TITLE } from '../translations'; +import { useGetSecuritySolutionUrl } from '../../../common/components/link_to'; const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }) => { const { isLoading, url } = useCreateSecurityDashboardLink(); @@ -57,7 +58,13 @@ export const DashboardsLandingPage = () => { const dashboardLinks = useAppRootNavLink(SecurityPageName.dashboards)?.links ?? []; const { show: canReadDashboard, createNew: canCreateDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); - + const { navigateTo } = useNavigateTo(); + const getSecuritySolutionUrl = useGetSecuritySolutionUrl(); + const getHref = (id: string) => + `${getSecuritySolutionUrl({ + deepLinkId: SecurityPageName.dashboards, + path: id, + })}`; return (
@@ -77,7 +84,20 @@ export const DashboardsLandingPage = () => { - + { + if (dashboardId) { + track(METRIC_TYPE.CLICK, TELEMETRY_EVENT.DASHBOARD); + navigateTo({ url: getHref(dashboardId) }); + } + return alert( + `Here's where I would redirect you to ${dashboardId ?? 'a new Dashboard'}` + ); + }} + getDashboardUrl={(id, timeRestore) => { + return getHref(id); + }} + /> )} From ff32ec56c1db0515d58bb2b86f0bd4da792eb583 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 25 Apr 2023 14:12:15 +0000 Subject: [PATCH 02/20] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../public/dashboards/pages/landing_page/index.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 3bb6d09d869b8..7282bb992b6ec 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -10,7 +10,6 @@ import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; -import { DashboardsTable } from '../../../common/components/dashboards/dashboards_table'; import { LandingImageCards } from '../../../landing_pages/components/landing_links_images'; import { SecurityPageName } from '../../../../common/constants'; import { useCapabilities, useNavigateTo } from '../../../common/lib/kibana'; From 576ec4a6fe21ca8ffef2551137f5c77ce601dc88 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 25 Apr 2023 15:30:48 +0100 Subject: [PATCH 03/20] styling --- .../dashboards/pages/landing_page/index.tsx | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 3bb6d09d869b8..0b3b1cec74e3b 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -56,7 +56,7 @@ const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard export const DashboardsLandingPage = () => { const dashboardLinks = useAppRootNavLink(SecurityPageName.dashboards)?.links ?? []; - const { show: canReadDashboard, createNew: canCreateDashboard } = + const { show: canReadDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); const { navigateTo } = useNavigateTo(); const getSecuritySolutionUrl = useGetSecuritySolutionUrl(); @@ -65,39 +65,31 @@ export const DashboardsLandingPage = () => { deepLinkId: SecurityPageName.dashboards, path: id, })}`; - return ( - -
- - - -

{i18n.DASHBOARDS_PAGE_SECTION_DEFAULT}

-
- - - + useCreateSecurityDashboardLink(); + return ( + {canReadDashboard && ( <> - -

{i18n.DASHBOARDS_PAGE_SECTION_CUSTOM}

-
- - { if (dashboardId) { track(METRIC_TYPE.CLICK, TELEMETRY_EVENT.DASHBOARD); navigateTo({ url: getHref(dashboardId) }); } - return alert( - `Here's where I would redirect you to ${dashboardId ?? 'a new Dashboard'}` - ); }} getDashboardUrl={(id, timeRestore) => { return getHref(id); }} - /> + > + + + +

{i18n.DASHBOARDS_PAGE_SECTION_CUSTOM}

+
+ + +
)} From 2fbf9b8b9230a5ed84c8af082ebeca4c0c2ad5ab Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 16 May 2023 15:38:36 +0100 Subject: [PATCH 04/20] add fixed tag --- .../table_list/src/components/table.tsx | 10 ++- .../src/components/use_tag_filter_panel.tsx | 18 +++-- .../table_list/src/table_list_view.tsx | 67 ++++++++++++++----- .../dashboard_listing/dashboard_listing.tsx | 13 ++++ .../dashboards/pages/landing_page/index.tsx | 17 ++++- 5 files changed, 100 insertions(+), 25 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 3214e7bf00a72..35015efc3985b 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -83,6 +83,7 @@ export function Table({ addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, clearTagSelection, + fixedTag = 'Security Solution', }: Props) { const { getTagList } = useServices(); @@ -147,7 +148,14 @@ export function Table({ totalActiveFilters, } = useTagFilterPanel({ query: searchQuery.query, - getTagList, + getTagList: useCallback(() => { + let tags = getTagList(); + if (fixedTag) { + tags = tags.filter((tag) => tag.name === fixedTag) ?? []; + } + return tags; + }, [fixedTag, getTagList]), + fixedTag: 'Security Solution', tagsToTableItemMap, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index ca7aab6f8bb08..5998c67743e02 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -34,6 +34,7 @@ export interface Params { query: Query | null; tagsToTableItemMap: { [tagId: string]: string[] }; getTagList: () => Tag[]; + fixedTag?: string; addOrRemoveIncludeTagFilter: (tag: Tag) => void; addOrRemoveExcludeTagFilter: (tag: Tag) => void; } @@ -42,6 +43,7 @@ export const useTagFilterPanel = ({ query, tagsToTableItemMap, getTagList, + fixedTag = 'Security Solution', addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, }: Params) => { @@ -52,7 +54,9 @@ export const useTagFilterPanel = ({ // "isInUse" state which disable the transition. const [isInUse, setIsInUse] = useState(false); const [options, setOptions] = useState([]); - const [tagSelection, setTagSelection] = useState({}); + const [tagSelection, setTagSelection] = useState( + fixedTag ? { [fixedTag]: 'include' } : {} + ); const totalActiveFilters = Object.keys(tagSelection).length; const onSelectChange = useCallback( @@ -68,6 +72,9 @@ export const useTagFilterPanel = ({ const onOptionClick = useCallback( (tag: Tag) => (e: MouseEvent) => { + if (fixedTag) { + return; + } const withModifierKey = (isMac && e.metaKey) || (!isMac && e.ctrlKey); if (withModifierKey) { @@ -76,7 +83,7 @@ export const useTagFilterPanel = ({ addOrRemoveIncludeTagFilter(tag); } }, - [addOrRemoveIncludeTagFilter, addOrRemoveExcludeTagFilter] + [fixedTag, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter] ); const updateTagList = useCallback(() => { @@ -118,7 +125,7 @@ export const useTagFilterPanel = ({ }); setOptions(tagsToSelectOptions); - }, [getTagList, tagsToTableItemMap, tagSelection, onOptionClick]); + }, [getTagList, fixedTag, tagSelection, onOptionClick, tagsToTableItemMap]); const onFilterButtonClick = useCallback(() => { setIsPopoverOpen((prev) => !prev); @@ -129,6 +136,9 @@ export const useTagFilterPanel = ({ }, []); useEffect(() => { + if (fixedTag) { + return; + } /** * Data flow for tag filter panel state: * When we click (or Ctrl + click) on a tag in the filter panel: @@ -160,7 +170,7 @@ export const useTagFilterPanel = ({ setTagSelection(updatedTagSelection); } - }, [query]); + }, [fixedTag, query]); useEffect(() => { if (isPopoverOpen) { diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 2191a3c9b7eee..26a45d03a08e7 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -20,6 +20,7 @@ import { CriteriaWithPagination, Query, Ast, + EuiPaddingSize, } from '@elastic/eui'; import { keyBy, uniq, get } from 'lodash'; import { i18n } from '@kbn/i18n'; @@ -103,7 +104,11 @@ export interface Props { @@ -260,6 +265,11 @@ function TableListViewComp({ titleColumnName, additionalRightSideActions = [], withoutPageTemplateWrapper, + withPageTemplateHeader = true, + restrictPageSectionWidth = true, + pageSectionPadding = 'm', + fixedTag = 'Security Solution', + tagReferences, }: Props) { if (!getDetailViewLink && !onClickTitle) { throw new Error( @@ -292,6 +302,14 @@ function TableListViewComp({ getTagList, } = useServices(); + const getTagListing = useCallback(() => { + let tags = getTagList(); + if (fixedTag) { + tags = tags.filter((tag) => tag.name === fixedTag) ?? []; + } + return tags; + }, [fixedTag, getTagList]); + const openContentEditor = useOpenContentEditor(); const isInRouterContext = useInRouterContext(); @@ -374,9 +392,16 @@ function TableListViewComp({ referencesToExclude, } = searchQueryParser ? await searchQueryParser(searchQuery.text) - : { searchQuery: searchQuery.text, references: undefined, referencesToExclude: undefined }; - - const response = await findItems(searchQueryParsed, { references, referencesToExclude }); + : { + searchQuery: searchQuery.text, + references: tagReferences ?? undefined, + referencesToExclude: undefined, + }; + + const response = await findItems(searchQueryParsed, { + references: tagReferences ?? references, + referencesToExclude, + }); if (!isMounted.current) { return; @@ -396,7 +421,7 @@ function TableListViewComp({ data: err, }); } - }, [searchQueryParser, findItems, searchQuery.text]); + }, [searchQueryParser, searchQuery.text, tagReferences, findItems]); const updateQuery = useCallback( (query: Query) => { @@ -427,7 +452,9 @@ function TableListViewComp({ const inspectItem = useCallback( (item: T) => { const tags = getTagIdsFromReferences(item.references).map((_id) => { - return item.references.find(({ id: refId }) => refId === _id) as SavedObjectsReference; + return item.references.find(({ id: refId }) => { + return refId === _id; + }) as SavedObjectsReference; }); const close = openContentEditor({ @@ -804,7 +831,7 @@ function TableListViewComp({ termMatch = searchTerm; if (references?.length || referencesToExclude?.length) { - const allTags = getTagList(); + const allTags = getTagListing(); if (references?.length) { references.forEach(({ id: refId }) => { @@ -860,7 +887,7 @@ function TableListViewComp({ updateQueryFromURL(urlState.s); updateSortFromURL(urlState.sort); - }, [urlState, searchQueryParser, getTagList, urlStateEnabled]); + }, [urlState, searchQueryParser, getTagListing, urlStateEnabled]); useEffect(() => { isMounted.current = true; @@ -911,16 +938,22 @@ function TableListViewComp({ return ( - {tableListTitle}} - description={tableListDescription} - rightSideItems={[ - renderCreateButton() ?? , - ...additionalRightSideActions?.slice(0, 2), - ]} - data-test-subj="top-nav" - /> - + {withPageTemplateHeader && ( + {tableListTitle}} + description={tableListDescription} + rightSideItems={[ + renderCreateButton() ?? , + ...additionalRightSideActions?.slice(0, 2), + ]} + data-test-subj="top-nav" + /> + )} + {/* Any children passed to the component */} {children} diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 8572356687fd6..02d34b1cd9dc8 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -20,6 +20,7 @@ import { reportPerformanceMetricEvent } from '@kbn/ebt-tools'; import { toMountPoint, useExecutionContext } from '@kbn/kibana-react-plugin/public'; import type { SavedObjectsFindOptionsReference, SimpleSavedObject } from '@kbn/core/public'; +import { EuiPaddingSize } from '@elastic/eui'; import { SAVED_OBJECT_DELETE_TIME, SAVED_OBJECT_LOADED_TIME, @@ -75,6 +76,10 @@ export type DashboardListingProps = PropsWithChildren<{ useSessionStorageIntegration?: boolean; goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string; + withPageTemplateHeader?: boolean; + restrictPageSectionWidth?: boolean; + pageSectionPadding?: EuiPaddingSize; + tagReferences?: SavedObjectsFindOptionsReference[] | undefined; }>; export const DashboardListing = ({ @@ -83,6 +88,10 @@ export const DashboardListing = ({ goToDashboard, getDashboardUrl, useSessionStorageIntegration, + withPageTemplateHeader = true, + restrictPageSectionWidth = true, + pageSectionPadding = 'm', + tagReferences, }: DashboardListingProps) => { const { application, @@ -242,7 +251,11 @@ export const DashboardListing = ({ listingLimit={listingLimit} emptyPrompt={emptyPrompt} findItems={fetchItems} + tagReferences={tagReferences} id="dashboard" + withPageTemplateHeader={withPageTemplateHeader} + restrictPageSectionWidth={restrictPageSectionWidth} + pageSectionPadding={pageSectionPadding} > <> {children} diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 1e172b23d2e75..50e9d8328956e 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -55,7 +55,7 @@ const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard export const DashboardsLandingPage = () => { const dashboardLinks = useAppRootNavLink(SecurityPageName.dashboards)?.links ?? []; - const { show: canReadDashboard } = + const { show: canReadDashboard, createNew: canCreateDashboard } = useCapabilities(LEGACY_DASHBOARD_APP_ID); const { navigateTo } = useNavigateTo(); const getSecuritySolutionUrl = useGetSecuritySolutionUrl(); @@ -68,6 +68,16 @@ export const DashboardsLandingPage = () => { return ( +
+ + + +

{i18n.DASHBOARDS_PAGE_SECTION_DEFAULT}

+
+ + + + {canReadDashboard && ( <> { getDashboardUrl={(id, timeRestore) => { return getHref(id); }} + withPageTemplateHeader={false} + restrictPageSectionWidth={false} + pageSectionPadding="none" > - -

{i18n.DASHBOARDS_PAGE_SECTION_CUSTOM}

From 6fa35cea744c3439ca25a8f4c392eb56dc915a05 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 16 May 2023 16:41:36 +0100 Subject: [PATCH 05/20] disable actions --- .../table_list/src/components/table.tsx | 3 + .../src/components/tag_filter_panel.tsx | 118 ++++++++++-------- .../src/components/use_tag_filter_panel.tsx | 2 +- .../table_list/src/table_list_view.tsx | 3 +- .../dashboard_listing/dashboard_listing.tsx | 3 + .../dashboards/pages/landing_page/index.tsx | 9 +- 6 files changed, 80 insertions(+), 58 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 35015efc3985b..45391ce8f55ba 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -180,6 +180,7 @@ export function Table({ return { type: 'custom_component', component: () => { + const disableActions = fixedTag != null; return ( ({ onFilterButtonClick={onFilterButtonClick} onSelectChange={onSelectChange} clearTagSelection={clearTagSelection} + disableActions={disableActions} /> ); }, @@ -203,6 +205,7 @@ export function Table({ onFilterButtonClick, onSelectChange, clearTagSelection, + fixedTag, ]); const searchFilters = useMemo(() => { diff --git a/packages/content-management/table_list/src/components/tag_filter_panel.tsx b/packages/content-management/table_list/src/components/tag_filter_panel.tsx index 03439f9dec161..80f946c35051d 100644 --- a/packages/content-management/table_list/src/components/tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/tag_filter_panel.tsx @@ -52,6 +52,7 @@ interface Props { totalActiveFilters: number; onFilterButtonClick: () => void; onSelectChange: (updatedOptions: TagOptionItem[]) => void; + disableActions?: boolean; } export const TagFilterPanel: FC = ({ @@ -63,6 +64,7 @@ export const TagFilterPanel: FC = ({ onSelectChange, closePopover, clearTagSelection, + disableActions, }) => { const { euiTheme } = useEuiTheme(); const { navigateToUrl, currentAppId$, getTagManagementUrl } = useServices(); @@ -122,18 +124,24 @@ export const TagFilterPanel: FC = ({ Tags - - {totalActiveFilters > 0 && ( - - {i18n.translate( - 'contentManagement.tableList.tagFilterPanel.clearSelectionButtonLabelLabel', - { - defaultMessage: 'Clear selection', - } - )} - - )} - + {!disableActions && ( + + {totalActiveFilters > 0 && ( + + {i18n.translate( + 'contentManagement.tableList.tagFilterPanel.clearSelectionButtonLabelLabel', + { + defaultMessage: 'Clear selection', + } + )} + + )} + + )} @@ -143,7 +151,7 @@ export const TagFilterPanel: FC = ({ renderOption={(option) => option.view} emptyMessage="There aren't any tags" noMatchesMessage="No tag matches the search" - onChange={onSelectChange} + onChange={!disableActions ? onSelectChange : null} data-test-subj="tagSelectableList" {...searchProps} > @@ -156,49 +164,51 @@ export const TagFilterPanel: FC = ({ ); }} - - - - - - {i18n.translate( - 'contentManagement.tableList.tagFilterPanel.modifierKeyHelpText', - { - defaultMessage: '{modifierKeyPrefix} + click exclude', - values: { - modifierKeyPrefix, - }, - } - )} - - - + {!disableActions && ( + + + + + + {i18n.translate( + 'contentManagement.tableList.tagFilterPanel.modifierKeyHelpText', + { + defaultMessage: '{modifierKeyPrefix} + click exclude', + values: { + modifierKeyPrefix, + }, + } + )} + + + - - Save - + + Save + - - - - {i18n.translate( - 'contentManagement.tableList.tagFilterPanel.manageAllTagsLinkLabel', - { - defaultMessage: 'Manage tags', - } - )} - - - - - + + + + {i18n.translate( + 'contentManagement.tableList.tagFilterPanel.manageAllTagsLinkLabel', + { + defaultMessage: 'Manage tags', + } + )} + + + + + + )} ); diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index 5998c67743e02..abbfe6e5920f2 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -125,7 +125,7 @@ export const useTagFilterPanel = ({ }); setOptions(tagsToSelectOptions); - }, [getTagList, fixedTag, tagSelection, onOptionClick, tagsToTableItemMap]); + }, [getTagList, tagSelection, onOptionClick, tagsToTableItemMap]); const onFilterButtonClick = useCallback(() => { setIsPopoverOpen((prev) => !prev); diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 26a45d03a08e7..9ce086af2c3b9 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -109,6 +109,7 @@ export interface Props { @@ -268,7 +269,7 @@ function TableListViewComp({ withPageTemplateHeader = true, restrictPageSectionWidth = true, pageSectionPadding = 'm', - fixedTag = 'Security Solution', + fixedTag, tagReferences, }: Props) { if (!getDetailViewLink && !onClickTitle) { diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 02d34b1cd9dc8..0eb60ea3ee68c 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -80,6 +80,7 @@ export type DashboardListingProps = PropsWithChildren<{ restrictPageSectionWidth?: boolean; pageSectionPadding?: EuiPaddingSize; tagReferences?: SavedObjectsFindOptionsReference[] | undefined; + fixedTag?: string }>; export const DashboardListing = ({ @@ -92,6 +93,7 @@ export const DashboardListing = ({ restrictPageSectionWidth = true, pageSectionPadding = 'm', tagReferences, + fixedTag }: DashboardListingProps) => { const { application, @@ -256,6 +258,7 @@ export const DashboardListing = ({ withPageTemplateHeader={withPageTemplateHeader} restrictPageSectionWidth={restrictPageSectionWidth} pageSectionPadding={pageSectionPadding} + fixedTag={fixedTag} > <> {children} diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 1f2e0e9c0e799..ab402c19daefa 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -11,7 +11,7 @@ import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-p import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { LandingImageCards } from '../../../landing_pages/components/landing_links_images'; -import { SecurityPageName } from '../../../../common/constants'; +import { SecurityPageName, SECURITY_TAG_NAME } from '../../../../common/constants'; import { useCapabilities, useNavigateTo } from '../../../common/lib/kibana'; import { useRootNavLink } from '../../../common/links/nav_links'; import { Title } from '../../../common/components/header_page/title'; @@ -21,6 +21,7 @@ import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../common/lib/telemet import { DASHBOARDS_PAGE_TITLE } from '../translations'; import { useGetSecuritySolutionUrl } from '../../../common/components/link_to'; import { useCreateSecurityDashboardLink } from '../../hooks/use_create_security_dashboard_link'; +import { useSecurityTags } from '../../context/dashboard_context'; const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }) => { const { isLoading, url } = useCreateSecurityDashboardLink(); @@ -64,7 +65,9 @@ export const DashboardsLandingPage = () => { deepLinkId: SecurityPageName.dashboards, path: id, })}`; - useCreateSecurityDashboardLink(); + + const securityTags = useSecurityTags(); + const tagReferences = securityTags?.map((tag) => ({ id: tag.id, type: 'tag' })); return ( @@ -93,6 +96,8 @@ export const DashboardsLandingPage = () => { withPageTemplateHeader={false} restrictPageSectionWidth={false} pageSectionPadding="none" + tagReferences={tagReferences} + fixedTag={SECURITY_TAG_NAME} >

{i18n.DASHBOARDS_PAGE_SECTION_CUSTOM}

From 6b1d0b89f9fc2202a6dd17e06c439a884ab98dfb Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 16 May 2023 15:47:41 +0000 Subject: [PATCH 06/20] [CI] Auto-commit changed files from 'node scripts/precommit_hook.js --ref HEAD~1..HEAD --fix' --- .../dashboard/public/dashboard_listing/dashboard_listing.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 0eb60ea3ee68c..0fd47874c4c38 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -80,7 +80,7 @@ export type DashboardListingProps = PropsWithChildren<{ restrictPageSectionWidth?: boolean; pageSectionPadding?: EuiPaddingSize; tagReferences?: SavedObjectsFindOptionsReference[] | undefined; - fixedTag?: string + fixedTag?: string; }>; export const DashboardListing = ({ @@ -93,7 +93,7 @@ export const DashboardListing = ({ restrictPageSectionWidth = true, pageSectionPadding = 'm', tagReferences, - fixedTag + fixedTag, }: DashboardListingProps) => { const { application, From 6a5a791f5a71a4a398f3934bcab06e2a03903008 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 16 May 2023 16:55:54 +0100 Subject: [PATCH 07/20] types --- .../table_list/src/components/table.tsx | 58 ++++++++++--------- .../src/components/tag_filter_panel.tsx | 23 ++++---- .../dashboard_listing/dashboard_listing.tsx | 24 ++++---- 3 files changed, 54 insertions(+), 51 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 45391ce8f55ba..b7043a91c537b 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -46,46 +46,54 @@ type TagManagementProps = Pick< >; interface Props extends State, TagManagementProps { + clearTagSelection: () => void; + deleteItems: TableListViewProps['deleteItems']; dispatch: Dispatch>; entityName: string; entityNamePlural: string; - isFetchingItems: boolean; - tableCaption: string; - tableColumns: Array>; + fixedTag?: string; hasUpdatedAtMetadata: boolean; - deleteItems: TableListViewProps['deleteItems']; - tableItemsRowActions: TableItemsRowActions; + isFetchingItems: boolean; onSortChange: (column: SortColumnField, direction: Direction) => void; onTableChange: (criteria: CriteriaWithPagination) => void; onTableSearchChange: (arg: { query: Query | null; queryText: string }) => void; - clearTagSelection: () => void; + tableCaption: string; + tableColumns: Array>; + tableItemsRowActions: TableItemsRowActions; } export function Table({ + addOrRemoveExcludeTagFilter, + addOrRemoveIncludeTagFilter, + clearTagSelection, + deleteItems, dispatch, - items, + entityName, + entityNamePlural, + fixedTag = 'Security Solution', + 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, - fixedTag = 'Security Solution', }: Props) { const { getTagList } = useServices(); + const getTagListing = useCallback(() => { + let tags = getTagList(); + if (fixedTag) { + tags = tags.filter((tag) => tag.name === fixedTag) ?? []; + } + return tags; + }, [fixedTag, getTagList]); const renderToolsLeft = useCallback(() => { if (!deleteItems || selectedIds.length === 0) { @@ -148,13 +156,7 @@ export function Table({ totalActiveFilters, } = useTagFilterPanel({ query: searchQuery.query, - getTagList: useCallback(() => { - let tags = getTagList(); - if (fixedTag) { - tags = tags.filter((tag) => tag.name === fixedTag) ?? []; - } - return tags; - }, [fixedTag, getTagList]), + getTagList: getTagListing, fixedTag: 'Security Solution', tagsToTableItemMap, addOrRemoveExcludeTagFilter, diff --git a/packages/content-management/table_list/src/components/tag_filter_panel.tsx b/packages/content-management/table_list/src/components/tag_filter_panel.tsx index 80f946c35051d..02d65ccb2a2bf 100644 --- a/packages/content-management/table_list/src/components/tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/tag_filter_panel.tsx @@ -29,6 +29,7 @@ import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; +import { noop } from 'lodash'; import { useServices } from '../services'; import type { TagOptionItem } from './use_tag_filter_panel'; @@ -46,25 +47,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; - disableActions?: boolean; + options: TagOptionItem[]; + totalActiveFilters: number; } export const TagFilterPanel: FC = ({ - isPopoverOpen, + clearTagSelection, + closePopover, + disableActions, isInUse, - options, - totalActiveFilters, + isPopoverOpen, onFilterButtonClick, onSelectChange, - closePopover, - clearTagSelection, - disableActions, + options, + totalActiveFilters, }) => { const { euiTheme } = useEuiTheme(); const { navigateToUrl, currentAppId$, getTagManagementUrl } = useServices(); @@ -151,7 +152,7 @@ export const TagFilterPanel: FC = ({ renderOption={(option) => option.view} emptyMessage="There aren't any tags" noMatchesMessage="No tag matches the search" - onChange={!disableActions ? onSelectChange : null} + onChange={!disableActions ? onSelectChange : noop} data-test-subj="tagSelectableList" {...searchProps} > diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 0eb60ea3ee68c..0500ffb890f86 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -72,28 +72,28 @@ const toTableListViewSavedObject = ( }; export type DashboardListingProps = PropsWithChildren<{ - initialFilter?: string; - useSessionStorageIntegration?: boolean; - goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; + fixedTag?: string; getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string; - withPageTemplateHeader?: boolean; - restrictPageSectionWidth?: boolean; + goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; + initialFilter?: string; pageSectionPadding?: EuiPaddingSize; + restrictPageSectionWidth?: boolean; tagReferences?: SavedObjectsFindOptionsReference[] | undefined; - fixedTag?: string + useSessionStorageIntegration?: boolean; + withPageTemplateHeader?: boolean; }>; export const DashboardListing = ({ children, - initialFilter, - goToDashboard, + fixedTag, getDashboardUrl, - useSessionStorageIntegration, - withPageTemplateHeader = true, - restrictPageSectionWidth = true, + goToDashboard, + initialFilter, pageSectionPadding = 'm', + restrictPageSectionWidth = true, tagReferences, - fixedTag + useSessionStorageIntegration, + withPageTemplateHeader = true, }: DashboardListingProps) => { const { application, From 6f04f3b3f3ad54a79248ad9d2c264a2d3ef78b08 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 16 May 2023 16:59:29 +0100 Subject: [PATCH 08/20] rm default value --- packages/content-management/table_list/src/components/table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index b7043a91c537b..bae13727201c0 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -70,7 +70,7 @@ export function Table({ dispatch, entityName, entityNamePlural, - fixedTag = 'Security Solution', + fixedTag, hasUpdatedAtMetadata, isFetchingItems, items, From 0f04412548743926cb6fdd2e9b32733d42ab9752 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Tue, 16 May 2023 17:03:30 +0100 Subject: [PATCH 09/20] rm default value --- .../table_list/src/components/table.tsx | 2 +- .../src/components/use_tag_filter_panel.tsx | 2 +- .../table_list/src/table_list_view.tsx | 71 ++++++++++--------- 3 files changed, 38 insertions(+), 37 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index bae13727201c0..5f716f71d0306 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -157,7 +157,7 @@ export function Table({ } = useTagFilterPanel({ query: searchQuery.query, getTagList: getTagListing, - fixedTag: 'Security Solution', + fixedTag, tagsToTableItemMap, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index abbfe6e5920f2..d8301254a23d8 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -43,7 +43,7 @@ export const useTagFilterPanel = ({ query, tagsToTableItemMap, getTagList, - fixedTag = 'Security Solution', + fixedTag, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, }: Params) => { diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 9ce086af2c3b9..260e510de991b 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -242,35 +242,35 @@ const tableColumnMetadata = { } as const; function TableListViewComp({ - tableListTitle, - tableListDescription, + additionalRightSideActions = [], + children, + contentEditor = { enabled: false }, + createItem, + customTableColumn, + deleteItems, + editItem, + emptyPrompt, entityName, entityNamePlural, - initialFilter: initialQuery, + findItems, + fixedTag, + getDetailViewLink, headingId, + id: listingId = 'userContent', + initialFilter: initialQuery, initialPageSize, listingLimit, - urlStateEnabled = true, - customTableColumn, - emptyPrompt, - rowItemActions, - findItems, - createItem, - editItem, - deleteItems, - getDetailViewLink, onClickTitle, - id: listingId = 'userContent', - contentEditor = { enabled: false }, - children, + pageSectionPadding = 'm', + restrictPageSectionWidth = true, + rowItemActions, + tableListDescription, + tableListTitle, + tagReferences, titleColumnName, - additionalRightSideActions = [], + urlStateEnabled = true, withoutPageTemplateWrapper, withPageTemplateHeader = true, - restrictPageSectionWidth = true, - pageSectionPadding = 'm', - fixedTag, - tagReferences, }: Props) { if (!getDetailViewLink && !onClickTitle) { throw new Error( @@ -975,27 +975,28 @@ function TableListViewComp({ {/* Table of items */}
+ addOrRemoveExcludeTagFilter={addOrRemoveExcludeTagFilter} + addOrRemoveIncludeTagFilter={addOrRemoveIncludeTagFilter} + clearTagSelection={clearTagSelection} + deleteItems={deleteItems} dispatch={dispatch} - items={items} - isFetchingItems={isFetchingItems} - searchQuery={searchQuery} - tableColumns={tableColumns} + entityName={entityName} + entityNamePlural={entityNamePlural} + fixedTag={fixedTag} hasUpdatedAtMetadata={hasUpdatedAtMetadata} - tableSort={tableSort} + isFetchingItems={isFetchingItems} + items={items} + onSortChange={onSortChange} + onTableChange={onTableChange} + onTableSearchChange={onTableSearchChange} pagination={pagination} + searchQuery={searchQuery} selectedIds={selectedIds} - entityName={entityName} - entityNamePlural={entityNamePlural} - tagsToTableItemMap={tagsToTableItemMap} - deleteItems={deleteItems} tableCaption={tableListTitle} + tableColumns={tableColumns} tableItemsRowActions={tableItemsRowActions} - onTableChange={onTableChange} - onTableSearchChange={onTableSearchChange} - onSortChange={onSortChange} - addOrRemoveIncludeTagFilter={addOrRemoveIncludeTagFilter} - addOrRemoveExcludeTagFilter={addOrRemoveExcludeTagFilter} - clearTagSelection={clearTagSelection} + tableSort={tableSort} + tagsToTableItemMap={tagsToTableItemMap} /> {/* Delete modal */} From 498ae01bb84030cd32039eb127676b23aaec3b9e Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Wed, 17 May 2023 18:17:35 +0100 Subject: [PATCH 10/20] update tags selection behaviour --- .../table_list/src/components/table.tsx | 19 ++-- .../src/components/tag_filter_panel.tsx | 88 +++++++++---------- .../src/components/use_tag_filter_panel.tsx | 23 +++-- .../table_list/src/table_list_view.tsx | 49 ++++++----- .../table_list/src/use_tags.ts | 24 +++-- .../dashboard_listing/dashboard_listing.tsx | 16 ++-- .../dashboard_listing_empty_prompt.tsx | 23 +++-- .../public/common/containers/tags/api.ts | 6 +- .../containers/use_fetch_security_tags.ts | 4 +- .../dashboards/pages/landing_page/index.tsx | 52 ++++++----- 10 files changed, 171 insertions(+), 133 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 5f716f71d0306..e3d4d071716c7 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -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'; @@ -60,6 +60,7 @@ interface Props extends State, TagManageme tableCaption: string; tableColumns: Array>; tableItemsRowActions: TableItemsRowActions; + fixedTagReferences?: Tag[] | null; } export function Table({ @@ -85,15 +86,9 @@ export function Table({ tableItemsRowActions, tableSort, tagsToTableItemMap, + fixedTagReferences, }: Props) { const { getTagList } = useServices(); - const getTagListing = useCallback(() => { - let tags = getTagList(); - if (fixedTag) { - tags = tags.filter((tag) => tag.name === fixedTag) ?? []; - } - return tags; - }, [fixedTag, getTagList]); const renderToolsLeft = useCallback(() => { if (!deleteItems || selectedIds.length === 0) { @@ -156,11 +151,11 @@ export function Table({ totalActiveFilters, } = useTagFilterPanel({ query: searchQuery.query, - getTagList: getTagListing, - fixedTag, + getTagList, tagsToTableItemMap, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, + fixedTagReferences, }); const tableSortSelectFilter = useMemo(() => { @@ -182,7 +177,7 @@ export function Table({ return { type: 'custom_component', component: () => { - const disableActions = fixedTag != null; + const disableActions = fixedTagReferences != null; return ( ({ }, }; }, [ + fixedTagReferences, isPopoverOpen, isInUse, closePopover, @@ -207,7 +203,6 @@ export function Table({ onFilterButtonClick, onSelectChange, clearTagSelection, - fixedTag, ]); const searchFilters = useMemo(() => { diff --git a/packages/content-management/table_list/src/components/tag_filter_panel.tsx b/packages/content-management/table_list/src/components/tag_filter_panel.tsx index 02d65ccb2a2bf..8fec81aa61658 100644 --- a/packages/content-management/table_list/src/components/tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/tag_filter_panel.tsx @@ -29,7 +29,6 @@ import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; -import { noop } from 'lodash'; import { useServices } from '../services'; import type { TagOptionItem } from './use_tag_filter_panel'; @@ -152,7 +151,7 @@ export const TagFilterPanel: FC = ({ renderOption={(option) => option.view} emptyMessage="There aren't any tags" noMatchesMessage="No tag matches the search" - onChange={!disableActions ? onSelectChange : noop} + onChange={onSelectChange} data-test-subj="tagSelectableList" {...searchProps} > @@ -165,51 +164,50 @@ export const TagFilterPanel: FC = ({ ); }} - {!disableActions && ( - - - - - - {i18n.translate( - 'contentManagement.tableList.tagFilterPanel.modifierKeyHelpText', - { - defaultMessage: '{modifierKeyPrefix} + click exclude', - values: { - modifierKeyPrefix, - }, - } - )} - - - - - Save - + + + + + + {i18n.translate( + 'contentManagement.tableList.tagFilterPanel.modifierKeyHelpText', + { + defaultMessage: '{modifierKeyPrefix} + click exclude', + values: { + modifierKeyPrefix, + }, + } + )} + + + - - - - {i18n.translate( - 'contentManagement.tableList.tagFilterPanel.manageAllTagsLinkLabel', - { - defaultMessage: 'Manage tags', - } - )} - - - - - - )} + + Save + + + + + + {i18n.translate( + 'contentManagement.tableList.tagFilterPanel.manageAllTagsLinkLabel', + { + defaultMessage: 'Manage tags', + } + )} + + + + + ); diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index d8301254a23d8..2e37705a6a115 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -34,18 +34,18 @@ export interface Params { query: Query | null; tagsToTableItemMap: { [tagId: string]: string[] }; getTagList: () => Tag[]; - fixedTag?: string; addOrRemoveIncludeTagFilter: (tag: Tag) => void; addOrRemoveExcludeTagFilter: (tag: Tag) => void; + fixedTagReferences?: Tag[] | null; } export const useTagFilterPanel = ({ query, tagsToTableItemMap, getTagList, - fixedTag, 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. @@ -54,25 +54,27 @@ export const useTagFilterPanel = ({ // "isInUse" state which disable the transition. const [isInUse, setIsInUse] = useState(false); const [options, setOptions] = useState([]); - const [tagSelection, setTagSelection] = useState( - fixedTag ? { [fixedTag]: 'include' } : {} - ); + const [tagSelection, setTagSelection] = useState({}); const totalActiveFilters = Object.keys(tagSelection).length; const onSelectChange = useCallback( (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 (fixedTag) { + if (fixedTagReferences?.find((ref) => ref.name === tag.name)) { return; } const withModifierKey = (isMac && e.metaKey) || (!isMac && e.ctrlKey); @@ -83,7 +85,7 @@ export const useTagFilterPanel = ({ addOrRemoveIncludeTagFilter(tag); } }, - [fixedTag, addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter] + [addOrRemoveExcludeTagFilter, addOrRemoveIncludeTagFilter, fixedTagReferences] ); const updateTagList = useCallback(() => { @@ -136,9 +138,6 @@ export const useTagFilterPanel = ({ }, []); useEffect(() => { - if (fixedTag) { - return; - } /** * Data flow for tag filter panel state: * When we click (or Ctrl + click) on a tag in the filter panel: @@ -170,7 +169,7 @@ export const useTagFilterPanel = ({ setTagSelection(updatedTagSelection); } - }, [fixedTag, query]); + }, [query]); useEffect(() => { if (isPopoverOpen) { diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 260e510de991b..3dbee5a7a4bfc 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -43,7 +43,7 @@ import { getReducer } from './reducer'; import type { SortColumnField } from './components'; import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; -import { RowActions, TableItemsRowActions } from './types'; +import { RowActions, TableItemsRowActions, Tag } from './types'; interface ContentEditorConfig extends Pick { @@ -108,8 +108,7 @@ export interface Props { @@ -183,7 +182,7 @@ const urlStateDeserializer = (params: URLQueryParams): URLState => { } }); - // For backward compability with the Dashboard app we will support both "s" and "title" passed + // For backward compatibility with the Dashboard app we will support both "s" and "title" passed // in the query params. We might want to stop supporting both in a future release (v9.0?) stateFromURL.s = sanitizedParams.s ?? sanitizedParams.title; @@ -241,6 +240,20 @@ const tableColumnMetadata = { }, } as const; +const appendQuery = (q: Query, tagName: string) => { + return q.addOrFieldValue('tag', tagName, true, 'eq'); +}; + +const getDefaultQuery = (initialQuery: string, fixedTagReferences: Tag[] | null | undefined) => { + const query = new Query(Ast.create([]), undefined, initialQuery); + + return ( + fixedTagReferences?.reduce((q, ref) => { + return appendQuery(q, ref.name); + }, query) ?? query + ); +}; + function TableListViewComp({ additionalRightSideActions = [], children, @@ -253,7 +266,6 @@ function TableListViewComp({ entityName, entityNamePlural, findItems, - fixedTag, getDetailViewLink, headingId, id: listingId = 'userContent', @@ -266,7 +278,7 @@ function TableListViewComp({ rowItemActions, tableListDescription, tableListTitle, - tagReferences, + fixedTagReferences, titleColumnName, urlStateEnabled = true, withoutPageTemplateWrapper, @@ -303,14 +315,6 @@ function TableListViewComp({ getTagList, } = useServices(); - const getTagListing = useCallback(() => { - let tags = getTagList(); - if (fixedTag) { - tags = tags.filter((tag) => tag.name === fixedTag) ?? []; - } - return tags; - }, [fixedTag, getTagList]); - const openContentEditor = useOpenContentEditor(); const isInRouterContext = useInRouterContext(); @@ -342,7 +346,7 @@ function TableListViewComp({ selectedIds: [], searchQuery: initialQuery !== undefined - ? { text: initialQuery, query: new Query(Ast.create([]), undefined, initialQuery) } + ? { text: initialQuery, query: getDefaultQuery(initialQuery, fixedTagReferences) } : { text: '', query: new Query(Ast.create([]), undefined, '') }, pagination: { pageIndex: 0, @@ -355,7 +359,7 @@ function TableListViewComp({ direction: 'asc', }, }), - [initialPageSize, initialQuery] + [fixedTagReferences, initialPageSize, initialQuery] ); const [state, dispatch] = useReducer(reducer, initialState); @@ -395,12 +399,12 @@ function TableListViewComp({ ? await searchQueryParser(searchQuery.text) : { searchQuery: searchQuery.text, - references: tagReferences ?? undefined, + references: undefined, referencesToExclude: undefined, }; const response = await findItems(searchQueryParsed, { - references: tagReferences ?? references, + references, referencesToExclude, }); @@ -422,7 +426,7 @@ function TableListViewComp({ data: err, }); } - }, [searchQueryParser, searchQuery.text, tagReferences, findItems]); + }, [searchQueryParser, searchQuery.text, findItems]); const updateQuery = useCallback( (query: Query) => { @@ -448,6 +452,7 @@ function TableListViewComp({ query: searchQuery.query, updateQuery, items, + fixedTagReferences, }); const inspectItem = useCallback( @@ -832,7 +837,7 @@ function TableListViewComp({ termMatch = searchTerm; if (references?.length || referencesToExclude?.length) { - const allTags = getTagListing(); + const allTags = getTagList(); if (references?.length) { references.forEach(({ id: refId }) => { @@ -888,7 +893,7 @@ function TableListViewComp({ updateQueryFromURL(urlState.s); updateSortFromURL(urlState.sort); - }, [urlState, searchQueryParser, getTagListing, urlStateEnabled]); + }, [urlState, searchQueryParser, urlStateEnabled, getTagList]); useEffect(() => { isMounted.current = true; @@ -982,7 +987,6 @@ function TableListViewComp({ dispatch={dispatch} entityName={entityName} entityNamePlural={entityNamePlural} - fixedTag={fixedTag} hasUpdatedAtMetadata={hasUpdatedAtMetadata} isFetchingItems={isFetchingItems} items={items} @@ -997,6 +1001,7 @@ function TableListViewComp({ tableItemsRowActions={tableItemsRowActions} tableSort={tableSort} tagsToTableItemMap={tagsToTableItemMap} + fixedTagReferences={fixedTagReferences} /> {/* Delete modal */} diff --git a/packages/content-management/table_list/src/use_tags.ts b/packages/content-management/table_list/src/use_tags.ts index 345a3484306ff..3f9112a5b41cb 100644 --- a/packages/content-management/table_list/src/use_tags.ts +++ b/packages/content-management/table_list/src/use_tags.ts @@ -17,10 +17,12 @@ export function useTags({ query, updateQuery, items, + fixedTagReferences, }: { query: Query; updateQuery: (query: Query) => void; items: UserContentCommonSchema[]; + fixedTagReferences?: Tag[] | null; }) { // Return a map of tag.id to an array of saved object ids having that tag // { 'abc-123': ['saved_object_id_1', 'saved_object_id_2', ...] } @@ -78,13 +80,23 @@ export function useTags({ ); const removeTagFromIncludeClause = useMemo( - () => updateTagClauseGetter((q, tag) => q.removeOrFieldValue('tag', tag.name)), - [updateTagClauseGetter] + () => + updateTagClauseGetter((q, tag) => + fixedTagReferences?.find((ref) => ref.name === tag.name) + ? q + : q.removeOrFieldValue('tag', tag.name) + ), + [fixedTagReferences, updateTagClauseGetter] ); const addTagToExcludeClause = useMemo( - () => updateTagClauseGetter((q, tag) => q.addOrFieldValue('tag', tag.name, false, 'eq')), - [updateTagClauseGetter] + () => + updateTagClauseGetter((q, tag) => + fixedTagReferences?.find((ref) => ref.name === tag.name) + ? q + : q.addOrFieldValue('tag', tag.name, false, 'eq') + ), + [fixedTagReferences, updateTagClauseGetter] ); const removeTagFromExcludeClause = useMemo( @@ -112,8 +124,8 @@ export function useTags({ [ hasTagInExclude, hasTagInInclude, - removeTagFromExcludeClause, addTagToIncludeClause, + removeTagFromExcludeClause, removeTagFromIncludeClause, ] ); @@ -138,8 +150,8 @@ export function useTags({ [ hasTagInInclude, hasTagInExclude, - removeTagFromIncludeClause, addTagToExcludeClause, + removeTagFromIncludeClause, removeTagFromExcludeClause, ] ); diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 0500ffb890f86..1e91d10b0847c 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -8,6 +8,7 @@ import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import React, { PropsWithChildren, useCallback, useState } from 'react'; +import type { Tag } from '@kbn/saved-objects-tagging-plugin/common'; import { TableListView, @@ -72,28 +73,30 @@ const toTableListViewSavedObject = ( }; export type DashboardListingProps = PropsWithChildren<{ - fixedTag?: string; + disableCreateDashboardButton?: boolean; getDashboardUrl: (dashboardId: string, usesTimeRestore: boolean) => string; goToDashboard: (dashboardId?: string, viewMode?: ViewMode) => void; initialFilter?: string; pageSectionPadding?: EuiPaddingSize; restrictPageSectionWidth?: boolean; - tagReferences?: SavedObjectsFindOptionsReference[] | undefined; + fixedTagReferences?: Tag[] | null; useSessionStorageIntegration?: boolean; withPageTemplateHeader?: boolean; + urlStateEnabled?: boolean; }>; export const DashboardListing = ({ children, - fixedTag, + disableCreateDashboardButton, getDashboardUrl, goToDashboard, initialFilter, pageSectionPadding = 'm', restrictPageSectionWidth = true, - tagReferences, + fixedTagReferences, useSessionStorageIntegration, withPageTemplateHeader = true, + urlStateEnabled, }: DashboardListingProps) => { const { application, @@ -209,6 +212,7 @@ export const DashboardListing = ({ const emptyPrompt = ( <> {children} diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx index a518c520bcbd8..374b7593ee4ee 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.tsx @@ -29,18 +29,20 @@ import { DASHBOARD_PANELS_UNSAVED_ID } from '../services/dashboard_session_stora export interface DashboardListingEmptyPromptProps { createItem: () => void; - unsavedDashboardIds: string[]; + disableCreateDashboardButton?: boolean; goToDashboard: DashboardListingProps['goToDashboard']; setUnsavedDashboardIds: React.Dispatch>; + unsavedDashboardIds: string[]; useSessionStorageIntegration: DashboardListingProps['useSessionStorageIntegration']; } export const DashboardListingEmptyPrompt = ({ - useSessionStorageIntegration, + createItem, + disableCreateDashboardButton, + goToDashboard, setUnsavedDashboardIds, unsavedDashboardIds, - goToDashboard, - createItem, + useSessionStorageIntegration, }: DashboardListingEmptyPromptProps) => { const { application, @@ -56,7 +58,13 @@ export const DashboardListingEmptyPrompt = ({ const getEmptyAction = useCallback(() => { if (!isEditingFirstDashboard) { return ( - + {noItemsStrings.getCreateNewDashboardText()} ); @@ -94,11 +102,12 @@ export const DashboardListingEmptyPrompt = ({ ); }, [ + createItem, dashboardSessionStorage, + disableCreateDashboardButton, + goToDashboard, isEditingFirstDashboard, setUnsavedDashboardIds, - goToDashboard, - createItem, ]); if (!showWriteControls) { diff --git a/x-pack/plugins/security_solution/public/common/containers/tags/api.ts b/x-pack/plugins/security_solution/public/common/containers/tags/api.ts index 57b27318103fc..4a9dd805e3384 100644 --- a/x-pack/plugins/security_solution/public/common/containers/tags/api.ts +++ b/x-pack/plugins/security_solution/public/common/containers/tags/api.ts @@ -6,10 +6,14 @@ */ import type { HttpSetup } from '@kbn/core/public'; -import type { Tag } from '@kbn/saved-objects-tagging-plugin/public'; import type { TagAttributes } from '@kbn/saved-objects-tagging-plugin/common'; import { INTERNAL_TAGS_URL } from '../../../../common/constants'; +export interface Tag { + id: string; + attributes: TagAttributes; +} + export const getTagsByName = ( { http, tagName }: { http: HttpSetup; tagName: string }, abortSignal?: AbortSignal diff --git a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts index a91daf7be16eb..601f1abf8060c 100644 --- a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts +++ b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts @@ -42,9 +42,9 @@ export const useFetchSecurityTags = () => { const tagsResult = useMemo(() => { if (tags?.length) { - return tags; + return tags.map((t) => ({ id: t.id, ...t.attributes })); } - return tag ? [tag] : undefined; + return tag ? [{ id: tag.id, ...tag.attributes }] : undefined; }, [tags, tag]); return { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index ab402c19daefa..ed7b3e66503b3 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } from '@elastic/eui'; -import React from 'react'; +import React, { useCallback } from 'react'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; @@ -60,14 +60,31 @@ export const DashboardsLandingPage = () => { useCapabilities(LEGACY_DASHBOARD_APP_ID); const { navigateTo } = useNavigateTo(); const getSecuritySolutionUrl = useGetSecuritySolutionUrl(); - const getHref = (id: string) => - `${getSecuritySolutionUrl({ - deepLinkId: SecurityPageName.dashboards, - path: id, - })}`; + const getSecuritySolutionDashboardUrl = useCallback( + (id: string) => + `${getSecuritySolutionUrl({ + deepLinkId: SecurityPageName.dashboards, + path: id, + })}`, + [getSecuritySolutionUrl] + ); + const { isLoading: loadingCreateDashboardUrl, url: createDashboardUrl } = + useCreateSecurityDashboardLink(); + + const getHref = useCallback( + (id: string | undefined) => (id ? getSecuritySolutionDashboardUrl(id) : createDashboardUrl), + [createDashboardUrl, getSecuritySolutionDashboardUrl] + ); + + const goToDashboard = useCallback( + (dashboardId: string | undefined) => { + track(METRIC_TYPE.CLICK, TELEMETRY_EVENT.DASHBOARD); + navigateTo({ url: getHref(dashboardId) }); + }, + [getHref, navigateTo] + ); const securityTags = useSecurityTags(); - const tagReferences = securityTags?.map((tag) => ({ id: tag.id, type: 'tag' })); return ( @@ -84,20 +101,15 @@ export const DashboardsLandingPage = () => { {canReadDashboard && ( <> { - if (dashboardId) { - track(METRIC_TYPE.CLICK, TELEMETRY_EVENT.DASHBOARD); - navigateTo({ url: getHref(dashboardId) }); - } - }} - getDashboardUrl={(id, timeRestore) => { - return getHref(id); - }} - withPageTemplateHeader={false} - restrictPageSectionWidth={false} + disableCreateDashboardButton={loadingCreateDashboardUrl} + getDashboardUrl={getSecuritySolutionDashboardUrl} + goToDashboard={goToDashboard} pageSectionPadding="none" - tagReferences={tagReferences} - fixedTag={SECURITY_TAG_NAME} + restrictPageSectionWidth={false} + fixedTagReferences={securityTags} + withPageTemplateHeader={false} + initialFilter={`tag:("${SECURITY_TAG_NAME}")`} + urlStateEnabled={false} >

{i18n.DASHBOARDS_PAGE_SECTION_CUSTOM}

From f92bca066ec03de9811be59ffe9b0d1cd3889583 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 17 May 2023 17:23:08 +0000 Subject: [PATCH 11/20] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/dashboard/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index ba94e006057ec..bbb4688d508c9 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -57,6 +57,7 @@ "@kbn/saved-objects-management-plugin", "@kbn/shared-ux-button-toolbar", "@kbn/core-saved-objects-server", + "@kbn/saved-objects-tagging-plugin", ], "exclude": [ "target/**/*", From fe9d2027cce61de2158e72a2baa090931dfc14ba Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 May 2023 11:47:47 +0100 Subject: [PATCH 12/20] unit tests --- .../common/containers/tags/__mocks__/api.ts | 9 +- .../pages/landing_page/index.test.tsx | 138 ++++++++++++------ .../dashboards/pages/landing_page/index.tsx | 38 ++++- 3 files changed, 130 insertions(+), 55 deletions(-) diff --git a/x-pack/plugins/security_solution/public/common/containers/tags/__mocks__/api.ts b/x-pack/plugins/security_solution/public/common/containers/tags/__mocks__/api.ts index 6f87ede894dd1..fa08f28035b56 100644 --- a/x-pack/plugins/security_solution/public/common/containers/tags/__mocks__/api.ts +++ b/x-pack/plugins/security_solution/public/common/containers/tags/__mocks__/api.ts @@ -6,13 +6,16 @@ */ export const MOCK_TAG_ID = 'securityTagId'; +export const MOCK_TAG_NAME = 'test tag'; export const DEFAULT_TAGS_RESPONSE = [ { id: MOCK_TAG_ID, - name: 'test tag', - description: 'test tag description', - color: '#2c7b82', + attributes: { + name: MOCK_TAG_NAME, + description: 'test tag description', + color: '#2c7b82', + }, }, ]; diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx index fa12b78af2991..a929c258e03db 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { render } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import React from 'react'; import { SecurityPageName } from '../../../app/types'; import { TestProviders } from '../../../common/mock'; @@ -13,12 +13,24 @@ import { DashboardsLandingPage } from '.'; import type { NavLinkItem } from '../../../common/components/navigation/types'; import { useCapabilities } from '../../../common/lib/kibana'; import * as telemetry from '../../../common/lib/telemetry'; - +import { DashboardListingTable } from '@kbn/dashboard-plugin/public'; +import { + DEFAULT_TAGS_RESPONSE, + MOCK_TAG_NAME, +} from '../../../common/containers/tags/__mocks__/api'; +import { DashboardContextProvider } from '../../context/dashboard_context'; +import { act } from 'react-dom/test-utils'; + +jest.mock('../../../common/containers/tags/api'); jest.mock('../../../common/lib/kibana'); jest.mock('../../../common/utils/route/spy_routes', () => ({ SpyRoute: () => null })); -jest.mock('../../components/dashboards_table', () => ({ - DashboardsTable: () => , -})); +jest.mock('@kbn/dashboard-plugin/public', () => { + const actual = jest.requireActual('@kbn/dashboard-plugin/public'); + return { + ...actual, + DashboardListingTable: jest.fn().mockReturnValue(), + }; +}); const DEFAULT_DASHBOARD_CAPABILITIES = { show: true, createNew: true }; const mockUseCapabilities = useCapabilities as jest.Mock; @@ -63,97 +75,133 @@ jest.mock('../../hooks/use_create_security_dashboard_link', () => { }; }); -const renderDashboardLanding = () => render(, { wrapper: TestProviders }); +const TestComponent = () => ( + + + + + +); + +const renderDashboardLanding = async () => { + await act(async () => { + render(); + }); +}; describe('Dashboards landing', () => { + beforeEach(() => { + mockUseCapabilities.mockReturnValue(DEFAULT_DASHBOARD_CAPABILITIES); + mockUseCreateSecurityDashboard.mockReturnValue(CREATE_DASHBOARD_LINK); + }); + describe('Dashboards default links', () => { - it('should render items', () => { - const { queryByText } = renderDashboardLanding(); + it('should render items', async () => { + await renderDashboardLanding(); - expect(queryByText(OVERVIEW_ITEM_LABEL)).toBeInTheDocument(); - expect(queryByText(DETECTION_RESPONSE_ITEM_LABEL)).toBeInTheDocument(); + expect(screen.queryByText(OVERVIEW_ITEM_LABEL)).toBeInTheDocument(); + expect(screen.queryByText(DETECTION_RESPONSE_ITEM_LABEL)).toBeInTheDocument(); }); - it('should render items in the same order as defined', () => { + it('should render items in the same order as defined', async () => { mockAppManageLink.mockReturnValueOnce({ ...APP_DASHBOARD_LINKS, }); - const { queryAllByTestId } = renderDashboardLanding(); + await renderDashboardLanding(); - const renderedItems = queryAllByTestId('LandingImageCard-item'); + const renderedItems = screen.queryAllByTestId('LandingImageCard-item'); expect(renderedItems[0]).toHaveTextContent(OVERVIEW_ITEM_LABEL); expect(renderedItems[1]).toHaveTextContent(DETECTION_RESPONSE_ITEM_LABEL); }); - it('should not render items if all items filtered', () => { - mockAppManageLink.mockReturnValueOnce({ + it('should not render items if all items filtered', async () => { + mockAppManageLink.mockReturnValue({ ...APP_DASHBOARD_LINKS, links: [], }); - const { queryByText } = renderDashboardLanding(); + await renderDashboardLanding(); - expect(queryByText(OVERVIEW_ITEM_LABEL)).not.toBeInTheDocument(); - expect(queryByText(DETECTION_RESPONSE_ITEM_LABEL)).not.toBeInTheDocument(); + expect(screen.queryByText(OVERVIEW_ITEM_LABEL)).not.toBeInTheDocument(); + expect(screen.queryByText(DETECTION_RESPONSE_ITEM_LABEL)).not.toBeInTheDocument(); }); }); describe('Security Dashboards', () => { - it('should render dashboards table', () => { - const result = renderDashboardLanding(); + it('should render dashboards table', async () => { + await renderDashboardLanding(); + + expect(screen.getByTestId('dashboardsTable')).toBeInTheDocument(); + }); + + it('should call DashboardListingTable with correct fixedTagReferences', async () => { + await renderDashboardLanding(); + + expect((DashboardListingTable as jest.Mock).mock.calls[0][0].fixedTagReferences).toEqual( + DEFAULT_TAGS_RESPONSE.map((res) => ({ + id: res.id, + ...res.attributes, + })) + ); + }); + + it('should call DashboardListingTable with correct initialFilter', async () => { + await renderDashboardLanding(); - expect(result.getByTestId('dashboardsTable')).toBeInTheDocument(); + expect((DashboardListingTable as jest.Mock).mock.calls[0][0].initialFilter).toEqual( + `tag:("${MOCK_TAG_NAME}")` + ); }); - it('should not render dashboards table if no read capability', () => { - mockUseCapabilities.mockReturnValueOnce({ + it('should not render dashboards table if no read capability', async () => { + mockUseCapabilities.mockReturnValue({ ...DEFAULT_DASHBOARD_CAPABILITIES, show: false, }); - const result = renderDashboardLanding(); + await renderDashboardLanding(); - expect(result.queryByTestId('dashboardsTable')).not.toBeInTheDocument(); + expect(screen.queryByTestId('dashboardsTable')).not.toBeInTheDocument(); }); describe('Create Security Dashboard button', () => { - it('should render', () => { - const result = renderDashboardLanding(); + it('should render', async () => { + await renderDashboardLanding(); - expect(result.getByTestId('createDashboardButton')).toBeInTheDocument(); + expect(screen.getByTestId('createDashboardButton')).toBeInTheDocument(); }); - it('should not render if no write capability', () => { - mockUseCapabilities.mockReturnValueOnce({ + it('should not render if no write capability', async () => { + mockUseCapabilities.mockReturnValue({ ...DEFAULT_DASHBOARD_CAPABILITIES, createNew: false, }); - const result = renderDashboardLanding(); + await renderDashboardLanding(); - expect(result.queryByTestId('createDashboardButton')).not.toBeInTheDocument(); + expect(screen.queryByTestId('createDashboardButton')).not.toBeInTheDocument(); }); - it('should be enabled when link loaded', () => { - const result = renderDashboardLanding(); + it('should be enabled when link loaded', async () => { + await renderDashboardLanding(); - expect(result.getByTestId('createDashboardButton')).not.toHaveAttribute('disabled'); + expect(screen.getByTestId('createDashboardButton')).not.toHaveAttribute('disabled'); }); - it('should be disabled when link is not loaded', () => { - mockUseCreateSecurityDashboard.mockReturnValueOnce({ isLoading: true, url: '' }); - const result = renderDashboardLanding(); + it('should be disabled when link is not loaded', async () => { + mockUseCreateSecurityDashboard.mockReturnValue({ isLoading: true, url: '' }); + await renderDashboardLanding(); - expect(result.getByTestId('createDashboardButton')).toHaveAttribute('disabled'); + expect(screen.getByTestId('createDashboardButton')).toHaveAttribute('disabled'); }); - it('should link to correct href', () => { - const result = renderDashboardLanding(); + it('should link to correct href', async () => { + await renderDashboardLanding(); - expect(result.getByTestId('createDashboardButton')).toHaveAttribute('href', URL); + expect(screen.getByTestId('createDashboardButton')).toHaveAttribute('href', URL); }); - it('should send telemetry', () => { - const result = renderDashboardLanding(); - result.getByTestId('createDashboardButton').click(); + it('should send telemetry', async () => { + await renderDashboardLanding(); + screen.getByTestId('createDashboardButton').click(); expect(spyTrack).toHaveBeenCalledWith( telemetry.METRIC_TYPE.CLICK, telemetry.TELEMETRY_EVENT.CREATE_DASHBOARD diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 6682077a1de1c..8a8d6724bba28 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -5,13 +5,14 @@ * 2.0. */ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } from '@elastic/eui'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; +import type { Tag } from '@kbn/saved-objects-tagging-oss-plugin/common/types'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { LandingImageCards } from '../../../common/components/landing_links/landing_links_images'; -import { SecurityPageName, SECURITY_TAG_NAME } from '../../../../common/constants'; +import { SecurityPageName } from '../../../../common/constants'; import { useCapabilities, useNavigateTo } from '../../../common/lib/kibana'; import { useRootNavLink } from '../../../common/links/nav_links'; import { Title } from '../../../common/components/header_page/title'; @@ -23,8 +24,26 @@ import { useGetSecuritySolutionUrl } from '../../../common/components/link_to'; import { useCreateSecurityDashboardLink } from '../../hooks/use_create_security_dashboard_link'; import { useSecurityTags } from '../../context/dashboard_context'; -const Header: React.FC<{ canCreateDashboard: boolean }> = ({ canCreateDashboard }) => { - const { isLoading, url } = useCreateSecurityDashboardLink(); +const getInitialFilterString = (securityTags: Tag[] | null | undefined) => { + if (!securityTags) { + return; + } + const queryArray = securityTags?.reduce((acc, { name }) => { + if (name) { + acc.push(`"${name}"`); + } + return acc; + }, []); + + const query = [...new Set(queryArray)].join(' or'); + return `tag:(${query})`; +}; + +const Header: React.FC<{ canCreateDashboard: boolean; isLoading: boolean; url: string }> = ({ + canCreateDashboard, + isLoading, + url, +}) => { const { navigateTo } = useNavigateTo(); return ( @@ -85,10 +104,15 @@ export const DashboardsLandingPage = () => { ); const securityTags = useSecurityTags(); + const initialFilter = useMemo(() => getInitialFilterString(securityTags), [securityTags]); return ( -
+
@@ -98,7 +122,7 @@ export const DashboardsLandingPage = () => { - {canReadDashboard && ( + {canReadDashboard && securityTags && ( <> { restrictPageSectionWidth={false} fixedTagReferences={securityTags} withPageTemplateHeader={false} - initialFilter={`tag:("${SECURITY_TAG_NAME}")`} + initialFilter={initialFilter} urlStateEnabled={false} > From b743ea05d204bfc5f0093baa18ae5221fc8f5848 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 May 2023 14:24:08 +0100 Subject: [PATCH 13/20] unit tests --- .../table_list/src/table_list_view.test.tsx | 108 +++++++++++++++++- .../table_list/src/table_list_view.tsx | 4 +- .../src/testbed/types.ts | 2 +- .../dashboard_listing.test.tsx | 80 +++++++++++++ .../dashboard_listing_empty_prompt.test.tsx | 16 +++ .../use_fetch_security_tags.test.ts | 8 +- 6 files changed, 210 insertions(+), 8 deletions(-) diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index 0245af450fb8a..bfae40f22e38f 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -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 () => { @@ -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( WithServices(TableListView, { @@ -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; diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 3dbee5a7a4bfc..2cc69690979fe 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -458,9 +458,7 @@ function TableListViewComp({ const inspectItem = useCallback( (item: T) => { const tags = getTagIdsFromReferences(item.references).map((_id) => { - return item.references.find(({ id: refId }) => { - return refId === _id; - }) as SavedObjectsReference; + return item.references.find(({ id: refId }) => refId === _id) as SavedObjectsReference; }); const close = openContentEditor({ diff --git a/packages/kbn-test-jest-helpers/src/testbed/types.ts b/packages/kbn-test-jest-helpers/src/testbed/types.ts index c5d591b10c4b9..7e722223f4dc1 100644 --- a/packages/kbn-test-jest-helpers/src/testbed/types.ts +++ b/packages/kbn-test-jest-helpers/src/testbed/types.ts @@ -100,7 +100,7 @@ export interface TestBed { * Select or unselect a form checkbox. * * @param dataTestSubject The test subject of the checkbox (can be a nested path. e.g. "myForm.mySelect"). - * @param isChecked Defines if the checkobx is active or not + * @param isChecked Defines if the checkbox is active or not */ selectCheckBox: (checkboxTestSubject: T, isChecked?: boolean) => void; /** diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx index 383b51fbd65fa..539bba8470157 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -66,6 +66,86 @@ test('initial filter is passed through', async () => { ); }); +test('fixedTagReferences is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + const fixedTagReferences = [{ id: 'mockTagId', name: 'mockTagName', description: '', color: '' }]; + + await act(async () => { + ({ component } = mountWith({ props: { fixedTagReferences } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ fixedTagReferences }), + expect.any(Object) // react context + ); +}); + +test('withPageTemplateHeader is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + const withPageTemplateHeader = false; + + await act(async () => { + ({ component } = mountWith({ props: { withPageTemplateHeader } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ withPageTemplateHeader }), + expect.any(Object) // react context + ); +}); + +test('restrictPageSectionWidth is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + const restrictPageSectionWidth = false; + + await act(async () => { + ({ component } = mountWith({ props: { restrictPageSectionWidth } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ restrictPageSectionWidth }), + expect.any(Object) // react context + ); +}); + +test('pageSectionPadding is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + const pageSectionPadding = 'none'; + + await act(async () => { + ({ component } = mountWith({ props: { pageSectionPadding } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ pageSectionPadding }), + expect.any(Object) // react context + ); +}); + +test('urlStateEnabled is passed through', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = false; + + let component: ReactWrapper; + const urlStateEnabled = false; + + await act(async () => { + ({ component } = mountWith({ props: { urlStateEnabled } })); + }); + component!.update(); + expect(TableListView).toHaveBeenCalledWith( + expect.objectContaining({ urlStateEnabled }), + expect.any(Object) // react context + ); +}); + test('when showWriteControls is true, table list view is passed editing functions', async () => { pluginServices.getServices().dashboardCapabilities.showWriteControls = true; diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx index 886d43a1db6d9..2551779752274 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing_empty_prompt.test.tsx @@ -34,6 +34,7 @@ const makeDefaultProps = (): DashboardListingEmptyPromptProps => ({ goToDashboard: jest.fn(), setUnsavedDashboardIds: jest.fn(), useSessionStorageIntegration: true, + disableCreateDashboardButton: false, }); function mountWith({ @@ -75,6 +76,21 @@ test('renders empty prompt with link when showWriteControls is on', async () => expect(component!.find('EuiLink').length).toBe(1); }); +test('renders disabled action button when disableCreateDashboardButton is true', async () => { + pluginServices.getServices().dashboardCapabilities.showWriteControls = true; + + let component: ReactWrapper; + await act(async () => { + ({ component } = mountWith({ props: { disableCreateDashboardButton: true } })); + }); + + component!.update(); + + expect(component!.find(`[data-test-subj="newItemButton"]`).first().prop('disabled')).toEqual( + true + ); +}); + test('renders continue button when no dashboards exist but one is in progress', async () => { pluginServices.getServices().dashboardCapabilities.showWriteControls = true; let component: ReactWrapper; diff --git a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts index 6041df888dfcb..77e7e2800fc20 100644 --- a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts +++ b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts @@ -12,6 +12,7 @@ import { SECURITY_TAG_DESCRIPTION, SECURITY_TAG_NAME, } from '../../../common/constants'; +import { DEFAULT_TAGS_RESPONSE } from '../../common/containers/tags/__mocks__/api'; import { useKibana } from '../../common/lib/kibana'; import { useFetchSecurityTags } from './use_fetch_security_tags'; @@ -66,11 +67,12 @@ describe('useFetchSecurityTags', () => { }); test('should return Security Solution tags', async () => { - const mockFoundTags = [{ id: 'tagId', name: 'Security Solution', description: '', color: '' }]; - mockGet.mockResolvedValue(mockFoundTags); + mockGet.mockResolvedValue(DEFAULT_TAGS_RESPONSE); + + const expected = DEFAULT_TAGS_RESPONSE.map((tag) => ({ id: tag.id, ...tag.attributes })); const { result } = await asyncRenderUseCreateSecurityDashboardLink(); expect(mockPut).not.toHaveBeenCalled(); - expect(result.current.tags).toEqual(expect.objectContaining(mockFoundTags)); + expect(result.current.tags).toEqual(expect.objectContaining(expected)); }); }); From eb0b11168fe12627b86773264bcaf2f027f8918a Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 May 2023 15:56:46 +0100 Subject: [PATCH 14/20] fix selection --- .../src/components/use_tag_filter_panel.tsx | 13 ++++++-- .../table_list/src/table_list_view.test.tsx | 8 +++-- .../table_list/src/table_list_view.tsx | 32 ++++++++++++++----- .../table_list/src/types.ts | 9 ++++++ .../dashboard_listing.test.tsx | 4 ++- .../saved_objects_tagging_oss/common/types.ts | 5 +-- .../containers/use_fetch_security_tags.ts | 4 +-- .../dashboards/pages/landing_page/index.tsx | 9 +++--- 8 files changed, 63 insertions(+), 21 deletions(-) diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index 2e37705a6a115..b0ab95e675377 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -5,7 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useEffect, useState, useCallback, useMemo } from 'react'; import type { MouseEvent } from 'react'; import { Query, EuiFlexGroup, EuiFlexItem, EuiText, EuiHealth, EuiBadge } from '@elastic/eui'; import type { FieldValueOptionType } from '@elastic/eui'; @@ -39,6 +39,12 @@ export interface Params { fixedTagReferences?: Tag[] | null; } +const getTotalActiveFilters = (options: TagOptionItem[], fixedTagReferences?: Tag[] | null) => + Math.max( + fixedTagReferences?.length ?? 0, + options.filter((option) => option.checked === 'on').length ?? 0 + ); + export const useTagFilterPanel = ({ query, tagsToTableItemMap, @@ -55,7 +61,10 @@ export const useTagFilterPanel = ({ const [isInUse, setIsInUse] = useState(false); const [options, setOptions] = useState([]); const [tagSelection, setTagSelection] = useState({}); - const totalActiveFilters = Object.keys(tagSelection).length; + const totalActiveFilters = useMemo( + () => getTotalActiveFilters(options, fixedTagReferences), + [fixedTagReferences, options] + ); const onSelectChange = useCallback( (updatedOptions: TagOptionItem[]) => { diff --git a/packages/content-management/table_list/src/table_list_view.test.tsx b/packages/content-management/table_list/src/table_list_view.test.tsx index bfae40f22e38f..a84b19d815ad0 100644 --- a/packages/content-management/table_list/src/table_list_view.test.tsx +++ b/packages/content-management/table_list/src/table_list_view.test.tsx @@ -736,7 +736,9 @@ describe('TableListView', () => { await act(async () => { testBed = await setupTagFiltering({ findItems, - fixedTagReferences: [{ id: 'id-tag-1', name: 'tag-1', description: '', color: '' }], + fixedTagReferences: [ + { id: 'id-tag-1', name: 'tag-1', description: '', color: '', type: 'tag' }, + ], }); }); @@ -758,7 +760,9 @@ describe('TableListView', () => { await act(async () => { testBed = await setupTagFiltering({ findItems, - fixedTagReferences: [{ id: 'id-tag-1', name: 'tag-1', description: '', color: '' }], + fixedTagReferences: [ + { id: 'id-tag-1', name: 'tag-1', description: '', color: '', type: 'tag' }, + ], }); }); diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 2cc69690979fe..3bd7f2547569d 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -43,7 +43,7 @@ import { getReducer } from './reducer'; import type { SortColumnField } from './components'; import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; -import { RowActions, TableItemsRowActions, Tag } from './types'; +import { RowActions, TableItemsRowActions, TagReference } from './types'; interface ContentEditorConfig extends Pick { @@ -108,7 +108,7 @@ export interface Props { @@ -244,16 +244,32 @@ const appendQuery = (q: Query, tagName: string) => { return q.addOrFieldValue('tag', tagName, true, 'eq'); }; -const getDefaultQuery = (initialQuery: string, fixedTagReferences: Tag[] | null | undefined) => { +const getDefaultQuery = ( + initialQuery: string, + fixedTagReferences: TagReference[] | null | undefined +) => { const query = new Query(Ast.create([]), undefined, initialQuery); - + const uniqueQueryArray = fixedTagReferences?.reduce((acc, { name }) => { + if (name && acc.indexOf(name) === -1) { + acc.push(name); + } + return acc; + }, []); return ( - fixedTagReferences?.reduce((q, ref) => { - return appendQuery(q, ref.name); + uniqueQueryArray?.reduce((q, ref) => { + return appendQuery(q, ref); }, query) ?? query ); }; +const getFindItemReference = ( + references: SavedObjectsFindOptionsReference[] | undefined, + fixedTagReferences?: TagReference[] | null | undefined +): SavedObjectsFindOptionsReference[] | undefined => { + const fixedTagFindReferences = fixedTagReferences?.map(({ id, type }) => ({ id, type })) ?? []; + return [...(references ?? []), ...fixedTagFindReferences]; +}; + function TableListViewComp({ additionalRightSideActions = [], children, @@ -404,7 +420,7 @@ function TableListViewComp({ }; const response = await findItems(searchQueryParsed, { - references, + references: getFindItemReference(references, fixedTagReferences), referencesToExclude, }); @@ -426,7 +442,7 @@ function TableListViewComp({ data: err, }); } - }, [searchQueryParser, searchQuery.text, findItems]); + }, [searchQueryParser, searchQuery.text, findItems, fixedTagReferences]); const updateQuery = useCallback( (query: Query) => { diff --git a/packages/content-management/table_list/src/types.ts b/packages/content-management/table_list/src/types.ts index c8e734a289451..2ba812c966b4c 100644 --- a/packages/content-management/table_list/src/types.ts +++ b/packages/content-management/table_list/src/types.ts @@ -11,6 +11,15 @@ export interface Tag { name: string; description: string; color: string; + type: string; +} + +export interface TagReference { + id: string; + name: string; + description: string; + color: string; + type: string; } export type TableRowAction = 'delete'; diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx index 539bba8470157..419453d73ae7d 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.test.tsx @@ -70,7 +70,9 @@ test('fixedTagReferences is passed through', async () => { pluginServices.getServices().dashboardCapabilities.showWriteControls = false; let component: ReactWrapper; - const fixedTagReferences = [{ id: 'mockTagId', name: 'mockTagName', description: '', color: '' }]; + const fixedTagReferences = [ + { id: 'mockTagId', name: 'mockTagName', description: '', color: '', type: 'tag' }, + ]; await act(async () => { ({ component } = mountWith({ props: { fixedTagReferences } })); diff --git a/src/plugins/saved_objects_tagging_oss/common/types.ts b/src/plugins/saved_objects_tagging_oss/common/types.ts index e62639659b5f4..06e2cc2b8cc6b 100644 --- a/src/plugins/saved_objects_tagging_oss/common/types.ts +++ b/src/plugins/saved_objects_tagging_oss/common/types.ts @@ -7,10 +7,11 @@ */ export interface Tag { + color: string; + description: string; id: string; name: string; - description: string; - color: string; + type: string; } export interface TagAttributes { diff --git a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts index 601f1abf8060c..6e8cb3eef6a6b 100644 --- a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts +++ b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.ts @@ -42,9 +42,9 @@ export const useFetchSecurityTags = () => { const tagsResult = useMemo(() => { if (tags?.length) { - return tags.map((t) => ({ id: t.id, ...t.attributes })); + return tags.map((t) => ({ id: t.id, type: 'tag', ...t.attributes })); } - return tag ? [{ id: tag.id, ...tag.attributes }] : undefined; + return tag ? [{ id: tag.id, type: 'tag', ...tag.attributes }] : undefined; }, [tags, tag]); return { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 8a8d6724bba28..8bfa8af6c005f 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -28,14 +28,15 @@ const getInitialFilterString = (securityTags: Tag[] | null | undefined) => { if (!securityTags) { return; } - const queryArray = securityTags?.reduce((acc, { name }) => { - if (name) { - acc.push(`"${name}"`); + const uniqueQueryArray = securityTags?.reduce((acc, { name }) => { + const nameString = `"${name}"`; + if (name && acc.indexOf(nameString) === -1) { + acc.push(nameString); } return acc; }, []); - const query = [...new Set(queryArray)].join(' or'); + const query = [uniqueQueryArray].join(' or'); return `tag:(${query})`; }; From 47ca980d9e8d806ae4f1d941adc7e6e513328c68 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 May 2023 16:11:53 +0100 Subject: [PATCH 15/20] fix types --- .../table_list/src/components/table.tsx | 6 ++++-- .../table_list/src/components/use_tag_filter_panel.tsx | 3 ++- .../content-management/table_list/src/table_list_view.tsx | 3 ++- packages/content-management/table_list/src/types.ts | 8 -------- packages/content-management/table_list/src/use_tags.ts | 4 +++- .../dashboards/containers/use_fetch_security_tags.test.ts | 6 +++++- .../public/dashboards/pages/landing_page/index.test.tsx | 1 + 7 files changed, 17 insertions(+), 14 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index e3d4d071716c7..0c01bbdf8f16a 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import { useServices } from '../services'; import type { Action } from '../actions'; import type { @@ -28,7 +29,8 @@ import type { Props as TableListViewProps, UserContentCommonSchema, } from '../table_list_view'; -import type { TableItemsRowActions, Tag } from '../types'; +import type { TableItemsRowActions } from '../types'; + import { TableSortSelect } from './table_sort_select'; import { TagFilterPanel } from './tag_filter_panel'; import { useTagFilterPanel } from './use_tag_filter_panel'; @@ -60,7 +62,7 @@ interface Props extends State, TagManageme tableCaption: string; tableColumns: Array>; tableItemsRowActions: TableItemsRowActions; - fixedTagReferences?: Tag[] | null; + fixedTagReferences?: TagReference[] | null; } export function Table({ diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index b0ab95e675377..3af24e819187a 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -10,6 +10,7 @@ import type { MouseEvent } from 'react'; import { Query, EuiFlexGroup, EuiFlexItem, EuiText, EuiHealth, EuiBadge } from '@elastic/eui'; import type { FieldValueOptionType } from '@elastic/eui'; +import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import type { Tag } from '../types'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; @@ -36,7 +37,7 @@ export interface Params { getTagList: () => Tag[]; addOrRemoveIncludeTagFilter: (tag: Tag) => void; addOrRemoveExcludeTagFilter: (tag: Tag) => void; - fixedTagReferences?: Tag[] | null; + fixedTagReferences?: TagReference[] | null; } const getTotalActiveFilters = (options: TagOptionItem[], fixedTagReferences?: Tag[] | null) => diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 3bd7f2547569d..54205163cb95d 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -30,6 +30,7 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { useOpenContentEditor } from '@kbn/content-management-content-editor'; import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; +import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import { Table, ConfirmDeleteModal, @@ -43,7 +44,7 @@ import { getReducer } from './reducer'; import type { SortColumnField } from './components'; import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; -import { RowActions, TableItemsRowActions, TagReference } from './types'; +import { RowActions, TableItemsRowActions } from './types'; interface ContentEditorConfig extends Pick { diff --git a/packages/content-management/table_list/src/types.ts b/packages/content-management/table_list/src/types.ts index 2ba812c966b4c..67cd0cda08ad7 100644 --- a/packages/content-management/table_list/src/types.ts +++ b/packages/content-management/table_list/src/types.ts @@ -14,14 +14,6 @@ export interface Tag { type: string; } -export interface TagReference { - id: string; - name: string; - description: string; - color: string; - type: string; -} - export type TableRowAction = 'delete'; export type RowActions = { diff --git a/packages/content-management/table_list/src/use_tags.ts b/packages/content-management/table_list/src/use_tags.ts index 3f9112a5b41cb..185c20e7c1446 100644 --- a/packages/content-management/table_list/src/use_tags.ts +++ b/packages/content-management/table_list/src/use_tags.ts @@ -8,7 +8,9 @@ import { useCallback, useMemo } from 'react'; import { Query } from '@elastic/eui'; +import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import type { Tag } from './types'; + import type { UserContentCommonSchema } from './table_list_view'; type QueryUpdater = (query: Query, tag: Tag) => Query; @@ -22,7 +24,7 @@ export function useTags({ query: Query; updateQuery: (query: Query) => void; items: UserContentCommonSchema[]; - fixedTagReferences?: Tag[] | null; + fixedTagReferences?: TagReference[] | null; }) { // Return a map of tag.id to an array of saved object ids having that tag // { 'abc-123': ['saved_object_id_1', 'saved_object_id_2', ...] } diff --git a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts index 77e7e2800fc20..bfd4f8fbc9b85 100644 --- a/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts +++ b/x-pack/plugins/security_solution/public/dashboards/containers/use_fetch_security_tags.test.ts @@ -69,7 +69,11 @@ describe('useFetchSecurityTags', () => { test('should return Security Solution tags', async () => { mockGet.mockResolvedValue(DEFAULT_TAGS_RESPONSE); - const expected = DEFAULT_TAGS_RESPONSE.map((tag) => ({ id: tag.id, ...tag.attributes })); + const expected = DEFAULT_TAGS_RESPONSE.map((tag) => ({ + id: tag.id, + type: 'tag', + ...tag.attributes, + })); const { result } = await asyncRenderUseCreateSecurityDashboardLink(); expect(mockPut).not.toHaveBeenCalled(); diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx index a929c258e03db..f710fd3ff2bd0 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.test.tsx @@ -140,6 +140,7 @@ describe('Dashboards landing', () => { expect((DashboardListingTable as jest.Mock).mock.calls[0][0].fixedTagReferences).toEqual( DEFAULT_TAGS_RESPONSE.map((res) => ({ id: res.id, + type: 'tag', ...res.attributes, })) ); From 770cfee6f8e7ece8d1f8ff04157747121ac83848 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 18 May 2023 15:18:14 +0000 Subject: [PATCH 16/20] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/content-management/table_list/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/content-management/table_list/tsconfig.json b/packages/content-management/table_list/tsconfig.json index 16a8a6b1a6de1..aedbd0dce309c 100644 --- a/packages/content-management/table_list/tsconfig.json +++ b/packages/content-management/table_list/tsconfig.json @@ -25,6 +25,7 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-link-redirect-app", "@kbn/test-jest-helpers", + "@kbn/saved-objects-tagging-plugin", ], "exclude": [ "target/**/*", From b797fc0771576a2d6c0f3cbe7350fdcb0ed0572b Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Thu, 18 May 2023 17:18:26 +0100 Subject: [PATCH 17/20] fix types --- .../src/components/use_tag_filter_panel.tsx | 5 ++++- .../table_list/src/table_list_view.tsx | 6 ++---- packages/content-management/table_list/src/types.ts | 7 +++++++ .../public/dashboard_listing/dashboard_listing.tsx | 11 +++++++++-- src/plugins/saved_objects_tagging_oss/common/types.ts | 3 --- .../public/dashboards/context/dashboard_context.tsx | 8 +++++++- 6 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index 3af24e819187a..f51fc0d19084f 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -40,7 +40,10 @@ export interface Params { fixedTagReferences?: TagReference[] | null; } -const getTotalActiveFilters = (options: TagOptionItem[], fixedTagReferences?: Tag[] | null) => +const getTotalActiveFilters = ( + options: TagOptionItem[], + fixedTagReferences?: TagReference[] | null +) => Math.max( fixedTagReferences?.length ?? 0, options.filter((option) => option.checked === 'on').length ?? 0 diff --git a/packages/content-management/table_list/src/table_list_view.tsx b/packages/content-management/table_list/src/table_list_view.tsx index 54205163cb95d..72017c33be9f3 100644 --- a/packages/content-management/table_list/src/table_list_view.tsx +++ b/packages/content-management/table_list/src/table_list_view.tsx @@ -29,8 +29,6 @@ import type { IHttpFetchError } from '@kbn/core-http-browser'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { useOpenContentEditor } from '@kbn/content-management-content-editor'; import type { OpenContentEditorParams } from '@kbn/content-management-content-editor'; - -import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import { Table, ConfirmDeleteModal, @@ -44,7 +42,7 @@ import { getReducer } from './reducer'; import type { SortColumnField } from './components'; import { useTags } from './use_tags'; import { useInRouterContext, useUrlState } from './use_url_state'; -import { RowActions, TableItemsRowActions } from './types'; +import { RowActions, TableItemsRowActions, TagReference } from './types'; interface ContentEditorConfig extends Pick { @@ -1002,6 +1000,7 @@ function TableListViewComp({ dispatch={dispatch} entityName={entityName} entityNamePlural={entityNamePlural} + fixedTagReferences={fixedTagReferences} hasUpdatedAtMetadata={hasUpdatedAtMetadata} isFetchingItems={isFetchingItems} items={items} @@ -1016,7 +1015,6 @@ function TableListViewComp({ tableItemsRowActions={tableItemsRowActions} tableSort={tableSort} tagsToTableItemMap={tagsToTableItemMap} - fixedTagReferences={fixedTagReferences} /> {/* Delete modal */} diff --git a/packages/content-management/table_list/src/types.ts b/packages/content-management/table_list/src/types.ts index 67cd0cda08ad7..baad92ff009fb 100644 --- a/packages/content-management/table_list/src/types.ts +++ b/packages/content-management/table_list/src/types.ts @@ -11,6 +11,13 @@ export interface Tag { name: string; description: string; color: string; +} + +export interface TagReference { + id: string; + name: string; + description: string; + color: string; type: string; } diff --git a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx index 1e91d10b0847c..c545ee159043b 100644 --- a/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx +++ b/src/plugins/dashboard/public/dashboard_listing/dashboard_listing.tsx @@ -8,7 +8,6 @@ import { FormattedRelative, I18nProvider } from '@kbn/i18n-react'; import React, { PropsWithChildren, useCallback, useState } from 'react'; -import type { Tag } from '@kbn/saved-objects-tagging-plugin/common'; import { TableListView, @@ -44,6 +43,14 @@ type TableListViewApplicationService = DashboardApplicationService & { capabilities: { advancedSettings: { save: boolean } }; }; +interface TagReference { + id: string; + type: string; + name: string; + color: string; + description: string; +} + const SAVED_OBJECTS_LIMIT_SETTING = 'savedObjects:listingLimit'; const SAVED_OBJECTS_PER_PAGE_SETTING = 'savedObjects:perPage'; @@ -79,7 +86,7 @@ export type DashboardListingProps = PropsWithChildren<{ initialFilter?: string; pageSectionPadding?: EuiPaddingSize; restrictPageSectionWidth?: boolean; - fixedTagReferences?: Tag[] | null; + fixedTagReferences?: TagReference[] | null; useSessionStorageIntegration?: boolean; withPageTemplateHeader?: boolean; urlStateEnabled?: boolean; diff --git a/src/plugins/saved_objects_tagging_oss/common/types.ts b/src/plugins/saved_objects_tagging_oss/common/types.ts index 06e2cc2b8cc6b..7ba311b499d17 100644 --- a/src/plugins/saved_objects_tagging_oss/common/types.ts +++ b/src/plugins/saved_objects_tagging_oss/common/types.ts @@ -7,11 +7,8 @@ */ export interface Tag { - color: string; - description: string; id: string; name: string; - type: string; } export interface TagAttributes { diff --git a/x-pack/plugins/security_solution/public/dashboards/context/dashboard_context.tsx b/x-pack/plugins/security_solution/public/dashboards/context/dashboard_context.tsx index 32aea030e0632..e00b0aee57072 100644 --- a/x-pack/plugins/security_solution/public/dashboards/context/dashboard_context.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/context/dashboard_context.tsx @@ -9,8 +9,14 @@ import React from 'react'; import type { Tag } from '@kbn/saved-objects-tagging-plugin/common'; import { useFetchSecurityTags } from '../containers/use_fetch_security_tags'; +export interface TagReference extends Tag { + type: string; + color: string; + description: string; +} + export interface DashboardContextType { - securityTags: Tag[] | null; + securityTags: TagReference[] | null; } const DashboardContext = React.createContext({ securityTags: null }); From de5584b7837e65119f525c595856b9b0e5f56689 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 18 May 2023 16:26:38 +0000 Subject: [PATCH 18/20] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- src/plugins/dashboard/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/dashboard/tsconfig.json b/src/plugins/dashboard/tsconfig.json index bbb4688d508c9..ba94e006057ec 100644 --- a/src/plugins/dashboard/tsconfig.json +++ b/src/plugins/dashboard/tsconfig.json @@ -57,7 +57,6 @@ "@kbn/saved-objects-management-plugin", "@kbn/shared-ux-button-toolbar", "@kbn/core-saved-objects-server", - "@kbn/saved-objects-tagging-plugin", ], "exclude": [ "target/**/*", From 3bbac62048492bafc39911a971d0b64399667691 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Fri, 19 May 2023 11:20:39 +0100 Subject: [PATCH 19/20] fix types --- .../content-management/table_list/src/components/table.tsx | 5 +---- .../table_list/src/components/use_tag_filter_panel.tsx | 3 +-- packages/content-management/table_list/src/use_tags.ts | 3 +-- src/plugins/saved_objects_tagging_oss/common/types.ts | 2 ++ .../public/dashboards/pages/landing_page/index.tsx | 4 ++-- 5 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/content-management/table_list/src/components/table.tsx b/packages/content-management/table_list/src/components/table.tsx index 0c01bbdf8f16a..9104327c7f46a 100644 --- a/packages/content-management/table_list/src/components/table.tsx +++ b/packages/content-management/table_list/src/components/table.tsx @@ -21,7 +21,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; import { useServices } from '../services'; import type { Action } from '../actions'; import type { @@ -29,7 +28,7 @@ import type { Props as TableListViewProps, UserContentCommonSchema, } from '../table_list_view'; -import type { TableItemsRowActions } from '../types'; +import type { TableItemsRowActions, TagReference } from '../types'; import { TableSortSelect } from './table_sort_select'; import { TagFilterPanel } from './tag_filter_panel'; @@ -53,7 +52,6 @@ interface Props extends State, TagManageme dispatch: Dispatch>; entityName: string; entityNamePlural: string; - fixedTag?: string; hasUpdatedAtMetadata: boolean; isFetchingItems: boolean; onSortChange: (column: SortColumnField, direction: Direction) => void; @@ -73,7 +71,6 @@ export function Table({ dispatch, entityName, entityNamePlural, - fixedTag, hasUpdatedAtMetadata, isFetchingItems, items, diff --git a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx index f51fc0d19084f..fc4d6373e6033 100644 --- a/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx +++ b/packages/content-management/table_list/src/components/use_tag_filter_panel.tsx @@ -10,8 +10,7 @@ import type { MouseEvent } from 'react'; import { Query, EuiFlexGroup, EuiFlexItem, EuiText, EuiHealth, EuiBadge } from '@elastic/eui'; import type { FieldValueOptionType } from '@elastic/eui'; -import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; -import type { Tag } from '../types'; +import type { Tag, TagReference } from '../types'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; diff --git a/packages/content-management/table_list/src/use_tags.ts b/packages/content-management/table_list/src/use_tags.ts index 185c20e7c1446..913d49f71d863 100644 --- a/packages/content-management/table_list/src/use_tags.ts +++ b/packages/content-management/table_list/src/use_tags.ts @@ -8,8 +8,7 @@ import { useCallback, useMemo } from 'react'; import { Query } from '@elastic/eui'; -import type { Tag as TagReference } from '@kbn/saved-objects-tagging-plugin/common'; -import type { Tag } from './types'; +import type { Tag, TagReference } from './types'; import type { UserContentCommonSchema } from './table_list_view'; diff --git a/src/plugins/saved_objects_tagging_oss/common/types.ts b/src/plugins/saved_objects_tagging_oss/common/types.ts index 7ba311b499d17..e62639659b5f4 100644 --- a/src/plugins/saved_objects_tagging_oss/common/types.ts +++ b/src/plugins/saved_objects_tagging_oss/common/types.ts @@ -9,6 +9,8 @@ export interface Tag { id: string; name: string; + description: string; + color: string; } export interface TagAttributes { diff --git a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx index 8bfa8af6c005f..62efd71a196bf 100644 --- a/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/pages/landing_page/index.tsx @@ -8,7 +8,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, EuiSpacer, EuiTitle } fro import React, { useCallback, useMemo } from 'react'; import type { DashboardCapabilities } from '@kbn/dashboard-plugin/common/types'; import { DashboardListingTable, LEGACY_DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; -import type { Tag } from '@kbn/saved-objects-tagging-oss-plugin/common/types'; import { SecuritySolutionPageWrapper } from '../../../common/components/page_wrapper'; import { SpyRoute } from '../../../common/utils/route/spy_routes'; import { LandingImageCards } from '../../../common/components/landing_links/landing_links_images'; @@ -22,9 +21,10 @@ import { METRIC_TYPE, TELEMETRY_EVENT, track } from '../../../common/lib/telemet import { DASHBOARDS_PAGE_TITLE } from '../translations'; import { useGetSecuritySolutionUrl } from '../../../common/components/link_to'; import { useCreateSecurityDashboardLink } from '../../hooks/use_create_security_dashboard_link'; +import type { TagReference } from '../../context/dashboard_context'; import { useSecurityTags } from '../../context/dashboard_context'; -const getInitialFilterString = (securityTags: Tag[] | null | undefined) => { +const getInitialFilterString = (securityTags: TagReference[] | null | undefined) => { if (!securityTags) { return; } From 3b84127e3a8dd190afacf0478903900051597d07 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Fri, 19 May 2023 10:26:55 +0000 Subject: [PATCH 20/20] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- packages/content-management/table_list/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/content-management/table_list/tsconfig.json b/packages/content-management/table_list/tsconfig.json index aedbd0dce309c..16a8a6b1a6de1 100644 --- a/packages/content-management/table_list/tsconfig.json +++ b/packages/content-management/table_list/tsconfig.json @@ -25,7 +25,6 @@ "@kbn/shared-ux-page-kibana-template", "@kbn/shared-ux-link-redirect-app", "@kbn/test-jest-helpers", - "@kbn/saved-objects-tagging-plugin", ], "exclude": [ "target/**/*",