From ac6f0d92f7717cc8b30bec1933578c45d178b8e8 Mon Sep 17 00:00:00 2001 From: Angela Chuang Date: Fri, 28 Apr 2023 11:28:13 +0100 Subject: [PATCH] fix edit dashboard url --- .../components/edit_dashboard_button.test.tsx | 58 ++++++-- .../components/edit_dashboard_button.tsx | 35 +++-- .../hooks/use_dashboard_app_link.test.tsx | 133 ------------------ .../hooks/use_dashboard_app_link.tsx | 71 ---------- 4 files changed, 71 insertions(+), 226 deletions(-) delete mode 100644 x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx delete mode 100644 x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx diff --git a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx index e4cd6fa206929..43afa552d50fd 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.test.tsx @@ -5,12 +5,16 @@ * 2.0. */ -import { render } from '@testing-library/react'; +import type { RenderResult } from '@testing-library/react'; +import { fireEvent, render } from '@testing-library/react'; import React from 'react'; +import type { Query } from '@kbn/es-query'; + import { useKibana } from '../../common/lib/kibana'; -import { createStartServicesMock } from '../../common/lib/kibana/kibana_react.mock'; import { TestProviders } from '../../common/mock/test_providers'; +import type { EditDashboardButtonComponentProps } from './edit_dashboard_button'; import { EditDashboardButton } from './edit_dashboard_button'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; jest.mock('../../common/lib/kibana/kibana_react', () => { return { @@ -24,18 +28,54 @@ describe('EditDashboardButton', () => { to: '2023-03-24T23:59:59.999Z', }; - beforeAll(() => { + const props = { + filters: [], + query: { query: '', language: '' } as Query, + savedObjectId: 'mockSavedObjectId', + timeRange, + }; + const servicesMock = { + dashboard: { locator: { getRedirectUrl: jest.fn() } }, + application: { + navigateToApp: jest.fn(), + navigateToUrl: jest.fn(), + }, + }; + + const renderButton = (testProps: EditDashboardButtonComponentProps) => { + return render( + + + + ); + }; + + let renderResult: RenderResult; + beforeEach(() => { (useKibana as jest.Mock).mockReturnValue({ - services: createStartServicesMock(), + services: servicesMock, }); + renderResult = renderButton(props); + }); + + beforeEach(() => { + jest.clearAllMocks(); }); it('should render', () => { - const { queryByTestId } = render( - - - + expect(renderResult.queryByTestId('dashboardEditButton')).toBeInTheDocument(); + }); + + it('should render dashboard edit url', () => { + fireEvent.click(renderResult.getByTestId('dashboardEditButton')); + expect(servicesMock.dashboard?.locator?.getRedirectUrl).toHaveBeenCalledWith( + expect.objectContaining({ + query: props.query, + filters: props.filters, + timeRange: props.timeRange, + dashboardId: props.savedObjectId, + viewMode: ViewMode.EDIT, + }) ); - expect(queryByTestId('dashboardEditButton')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx index c1089e95787e0..bd360229c7e1f 100644 --- a/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx +++ b/x-pack/plugins/security_solution/public/dashboards/components/edit_dashboard_button.tsx @@ -5,12 +5,12 @@ * 2.0. */ -import React from 'react'; +import React, { useCallback } from 'react'; import type { Query, Filter } from '@kbn/es-query'; import { EuiButton } from '@elastic/eui'; -import { useDashboardAppLink } from '../hooks/use_dashboard_app_link'; +import { ViewMode } from '@kbn/embeddable-plugin/public'; import { EDIT_DASHBOARD_BUTTON_TITLE } from '../pages/details/translations'; -import { useKibana } from '../../common/lib/kibana'; +import { useKibana, useNavigation } from '../../common/lib/kibana'; export interface EditDashboardButtonComponentProps { filters?: Filter[]; @@ -31,24 +31,33 @@ const EditDashboardButtonComponent: React.FC timeRange, }) => { const { - services: { uiSettings }, + services: { dashboard }, } = useKibana(); + const { navigateTo } = useNavigation(); - const { onClick } = useDashboardAppLink({ - query, - filters, - timeRange, - uiSettings, - savedObjectId, - }); - + const onClick = useCallback( + (e) => { + e.preventDefault(); + const url = dashboard?.locator?.getRedirectUrl({ + query, + filters, + timeRange, + dashboardId: savedObjectId, + viewMode: ViewMode.EDIT, + }); + if (url) { + navigateTo({ url }); + } + }, + [dashboard?.locator, query, filters, timeRange, savedObjectId, navigateTo] + ); return ( {EDIT_DASHBOARD_BUTTON_TITLE} diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx deleted file mode 100644 index decd108b6d6af..0000000000000 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.test.tsx +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { renderHook } from '@testing-library/react-hooks'; -import { useNavigation } from '../../common/lib/kibana'; -import { TestProviders } from '../../common/mock'; -import type { UseDashboardAppLinkProps } from './use_dashboard_app_link'; -import { useDashboardAppLink } from './use_dashboard_app_link'; - -jest.mock('../../common/lib/kibana', () => ({ - useNavigation: jest.fn(), -})); - -describe('useDashboardAppLink', () => { - const mockNavigateTo = jest.fn(); - const filters = [ - { - meta: { - index: 'security-solution-default', - type: 'phrase', - key: 'event.action', - params: { - query: 'host', - }, - disabled: false, - negate: false, - alias: null, - }, - query: { - match_phrase: { - 'event.action': 'host', - }, - }, - $state: { - store: 'appState', - }, - }, - ]; - const props = { - query: { - language: 'kuery', - query: '', - }, - filters: [], - timeRange: { - from: '2023-03-24T00:00:00.000Z', - fromStr: 'now/d', - to: '2023-03-24T23:59:59.999Z', - toStr: 'now/d', - }, - uiSettings: { - get: jest.fn(), - }, - savedObjectId: 'e2937420-c8ba-11ed-a7eb-3d08ee4d53cb', - }; - - beforeEach(() => { - jest.clearAllMocks(); - (useNavigation as jest.Mock).mockReturnValue({ - getAppUrl: jest - .fn() - .mockReturnValue('/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb'), - navigateTo: mockNavigateTo, - }); - }); - it('create links to Dashboard app - with filters', () => { - const testProps = { ...props, filters } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { - wrapper: TestProviders, - }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(('$state':(store:appState),meta:(alias:!n,disabled:!f,index:security-solution-default,key:event.action,negate:!f,params:(query:host),type:phrase),query:(match_phrase:(event.action:host)))),query:(language:kuery,query:''),time:(from:now%2Fd,to:now%2Fd))"` - ); - }); - - it('create links to Dashboard app - with query', () => { - const testProps = { - ...props, - query: { - language: 'kuery', - query: '@timestamp : *', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { wrapper: TestProviders }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:'@timestamp%20:%20*'),time:(from:now%2Fd,to:now%2Fd))"` - ); - }); - - it('create links to Dashboard app - with absolute time', () => { - const testProps = { - ...props, - timeRange: { - from: '2023-03-24T00:00:00.000Z', - to: '2023-03-24T23:59:59.999Z', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { wrapper: TestProviders }); - expect(result.current.href).toMatchInlineSnapshot( - `"/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:''),time:(from:'2023-03-24T00:00:00.000Z',to:'2023-03-24T23:59:59.999Z'))"` - ); - }); - - it('navigate to dashboard app with preserved states', () => { - const testProps = { - ...props, - timeRange: { - from: '2023-03-24T00:00:00.000Z', - to: '2023-03-24T23:59:59.999Z', - }, - } as unknown as UseDashboardAppLinkProps; - - const { result } = renderHook(() => useDashboardAppLink(testProps), { - wrapper: TestProviders, - }); - result.current.onClick({ - preventDefault: jest.fn(), - } as unknown as React.MouseEvent); - - expect(mockNavigateTo).toHaveBeenCalledWith( - expect.objectContaining({ - url: "/app/dashboards#/view/e2937420-c8ba-11ed-a7eb-3d08ee4d53cb?_g=(filters:!(),query:(language:kuery,query:''),time:(from:'2023-03-24T00:00:00.000Z',to:'2023-03-24T23:59:59.999Z'))", - }) - ); - }); -}); diff --git a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx b/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx deleted file mode 100644 index 1bf8a16f1ce19..0000000000000 --- a/x-pack/plugins/security_solution/public/dashboards/hooks/use_dashboard_app_link.tsx +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { createDashboardEditUrl, DASHBOARD_APP_ID } from '@kbn/dashboard-plugin/public'; -import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public'; -import type { IUiSettingsClient } from '@kbn/core/public'; -import { useMemo } from 'react'; -import type { Filter, Query } from '@kbn/es-query'; -import { useNavigation } from '../../common/lib/kibana'; - -const GLOBAL_STATE_STORAGE_KEY = '_g'; - -export interface UseDashboardAppLinkProps { - query?: Query; - filters?: Filter[]; - timeRange: { - from: string; - to: string; - fromStr?: string | undefined; - toStr?: string | undefined; - }; - uiSettings: IUiSettingsClient; - savedObjectId: string | undefined; -} - -export const useDashboardAppLink = ({ - query, - filters, - timeRange: { from, fromStr, to, toStr }, - uiSettings, - savedObjectId, -}: UseDashboardAppLinkProps) => { - const { navigateTo, getAppUrl } = useNavigation(); - const useHash = uiSettings.get('state:storeInSessionStorage'); - - let editDashboardUrl = useMemo( - () => - getAppUrl({ - appId: DASHBOARD_APP_ID, - path: `#${createDashboardEditUrl(savedObjectId)}`, - }), - [getAppUrl, savedObjectId] - ); - - editDashboardUrl = setStateToKbnUrl( - GLOBAL_STATE_STORAGE_KEY, - { - time: { from: fromStr ?? from, to: toStr ?? to }, - filters, - query, - }, - { useHash, storeInHashQuery: true }, - editDashboardUrl - ); - - const editDashboardLinkProps = useMemo( - () => ({ - onClick: (ev: React.MouseEvent) => { - ev.preventDefault(); - navigateTo({ url: editDashboardUrl }); - }, - href: editDashboardUrl, - }), - [editDashboardUrl, navigateTo] - ); - return editDashboardLinkProps; -};