-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Security Solution][DQD] Add historical results tour guide (#196127)
addresses #195971 This PR adds missing new historical results feature tour guide. ## Tour guide features: - ability to maintain visual presence while collapsing accordions in list-view - move from list-view to flyout view and back - seamlessly integrates with existing opening flyout and history tab functionality ## PR decisions with explanation: - data-tour-element has been introduced on select elements (like first actions of each first row) to avoid polluting every single element with data-test-subj. This way it's imho specific and semantically more clear what the elements are for. - early on I tried to control the anchoring with refs but some eui elements don't allow passing refs like EuiTab, so instead a more simpler and straightforward approach with dom selectors has been chosen - localStorage key name has been picked in accordance with other instances of usage `securitySolution.dataQualityDashboard.historicalResultsTour.v8.16.isActive` the name includes the full domain + the version when it's introduced. And since this tour step is a single step there is no need to stringify an object with `isTourActive` in and it's much simpler to just bake the activity state into the name and make the value just a boolean. ## UI Demo ### Anchor reposition demo (listview + flyout) https://github.com/user-attachments/assets/0f961c51-0e36-48ca-aab4-bef3b0d1269e ### List view tour guide try it + reload demo https://github.com/user-attachments/assets/ca1f5fda-ee02-4a48-827c-91df757a8ddf ### FlyOut Try It + reload demo https://github.com/user-attachments/assets/d0801ac3-1ed1-4e64-9d6b-3140b8402bdf ### Manual history tab selection path + reload demo https://github.com/user-attachments/assets/34dbb447-2fd6-4dc0-a4f5-682c9c65cc8b ### Manual open history view path + reload demo https://github.com/user-attachments/assets/945dd042-fc12-476e-8d23-f48c9ded9f65 ### Dismiss list view tour guide + reload demo https://github.com/user-attachments/assets/d20d1416-827f-46f2-9161-a3c0a8cbd932 ### Dismiss FlyOut tour guide + reload demo https://github.com/user-attachments/assets/8f085f59-20a9-49f0-b5b3-959c4719f5cb ### Serverless empty pattern handling + reposition demo https://github.com/user-attachments/assets/4af5939e-663c-4439-a3fc-deff2d4de7e4
- Loading branch information
Showing
16 changed files
with
1,304 additions
and
26 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
...ality_dashboard/impl/data_quality_panel/data_quality_details/indices_details/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
export const HISTORICAL_RESULTS_TOUR_IS_DISMISSED_STORAGE_KEY = | ||
'securitySolution.dataQualityDashboard.historicalResultsTour.v8.16.isDismissed'; |
28 changes: 28 additions & 0 deletions
28
...ata_quality_details/indices_details/hooks/use_is_historical_results_tour_active/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* 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 { useCallback } from 'react'; | ||
import useLocalStorage from 'react-use/lib/useLocalStorage'; | ||
|
||
import { HISTORICAL_RESULTS_TOUR_IS_DISMISSED_STORAGE_KEY } from '../../constants'; | ||
|
||
export const useIsHistoricalResultsTourActive = () => { | ||
const [isTourDismissed, setIsTourDismissed] = useLocalStorage<boolean>( | ||
HISTORICAL_RESULTS_TOUR_IS_DISMISSED_STORAGE_KEY, | ||
false | ||
); | ||
|
||
const isTourActive = !isTourDismissed; | ||
const setIsTourActive = useCallback( | ||
(active: boolean) => { | ||
setIsTourDismissed(!active); | ||
}, | ||
[setIsTourDismissed] | ||
); | ||
|
||
return [isTourActive, setIsTourActive] as const; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
...panel/data_quality_details/indices_details/pattern/historical_results_tour/index.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* 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 React from 'react'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import userEvent from '@testing-library/user-event'; | ||
|
||
import { HISTORICAL_RESULTS_TOUR_SELECTOR_KEY } from '../constants'; | ||
import { HistoricalResultsTour } from '.'; | ||
import { INTRODUCING_DATA_QUALITY_HISTORY, VIEW_PAST_RESULTS } from './translations'; | ||
|
||
const anchorSelectorValue = 'test-anchor'; | ||
|
||
describe('HistoricalResultsTour', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
describe('given no anchor element', () => { | ||
it('does not render the tour step', () => { | ||
render( | ||
<HistoricalResultsTour | ||
anchorSelectorValue={anchorSelectorValue} | ||
onTryIt={jest.fn()} | ||
isOpen={true} | ||
onDismissTour={jest.fn()} | ||
/> | ||
); | ||
|
||
expect(screen.queryByText(INTRODUCING_DATA_QUALITY_HISTORY)).not.toBeInTheDocument(); | ||
}); | ||
}); | ||
|
||
describe('given an anchor element', () => { | ||
beforeEach(() => { | ||
// eslint-disable-next-line no-unsanitized/property | ||
document.body.innerHTML = `<div ${HISTORICAL_RESULTS_TOUR_SELECTOR_KEY}="${anchorSelectorValue}"></div>`; | ||
}); | ||
|
||
describe('when isOpen is true', () => { | ||
const onTryIt = jest.fn(); | ||
const onDismissTour = jest.fn(); | ||
beforeEach(() => { | ||
render( | ||
<HistoricalResultsTour | ||
anchorSelectorValue={anchorSelectorValue} | ||
onTryIt={onTryIt} | ||
isOpen={true} | ||
onDismissTour={onDismissTour} | ||
/> | ||
); | ||
}); | ||
it('renders the tour step', async () => { | ||
expect( | ||
await screen.findByRole('dialog', { name: INTRODUCING_DATA_QUALITY_HISTORY }) | ||
).toBeInTheDocument(); | ||
expect(screen.getByText(INTRODUCING_DATA_QUALITY_HISTORY)).toBeInTheDocument(); | ||
expect(screen.getByText(VIEW_PAST_RESULTS)).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /Close/i })).toBeInTheDocument(); | ||
expect(screen.getByRole('button', { name: /Try It/i })).toBeInTheDocument(); | ||
|
||
const historicalResultsTour = screen.getByTestId('historicalResultsTour'); | ||
expect(historicalResultsTour.querySelector('[data-tour-element]')).toHaveAttribute( | ||
'data-tour-element', | ||
anchorSelectorValue | ||
); | ||
}); | ||
|
||
describe('when the close button is clicked', () => { | ||
it('calls dismissTour', async () => { | ||
await userEvent.click(await screen.findByRole('button', { name: /Close/i })); | ||
expect(onDismissTour).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
|
||
describe('when the try it button is clicked', () => { | ||
it('calls onTryIt', async () => { | ||
await userEvent.click(await screen.findByRole('button', { name: /Try It/i })); | ||
expect(onTryIt).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('when isOpen is false', () => { | ||
it('does not render the tour step', async () => { | ||
render( | ||
<HistoricalResultsTour | ||
anchorSelectorValue={anchorSelectorValue} | ||
onTryIt={jest.fn()} | ||
isOpen={false} | ||
onDismissTour={jest.fn()} | ||
/> | ||
); | ||
|
||
await waitFor(() => | ||
expect(screen.queryByText(INTRODUCING_DATA_QUALITY_HISTORY)).not.toBeInTheDocument() | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.