Skip to content

Commit

Permalink
[Serverless] New UX for Global Search bar (#158978)
Browse files Browse the repository at this point in the history
**Global Search**

Project document:
https://docs.google.com/document/d/1Z-AkRoagS6JMVN5YHZpjgQr0sYGFXBTNS9SHsbLPfGQ/edit#

## Summary
Closes #159763

Currently there is a known issue with the search bar when there is not
enough screen real estate:
#154415

### Checklist

Delete any items that are not applicable to this PR.

- [x] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [x] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)

---------

Co-authored-by: Kibana Machine <[email protected]>
  • Loading branch information
tsullivan and kibanamachine authored Jun 21, 2023
1 parent aaaef2e commit ec3b60b
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 209 deletions.
342 changes: 204 additions & 138 deletions x-pack/plugins/global_search_bar/public/components/search_bar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
* 2.0.
*/

import React from 'react';
import { act, render, screen, fireEvent } from '@testing-library/react';
import { of, BehaviorSubject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import type { ChromeStyle } from '@kbn/core-chrome-browser';
import { applicationServiceMock } from '@kbn/core/public/mocks';
import { globalSearchPluginMock } from '@kbn/global-search-plugin/public/mocks';
import { GlobalSearchBatchedResults, GlobalSearchResult } from '@kbn/global-search-plugin/public';
import { SearchBar } from './search_bar';
import { globalSearchPluginMock } from '@kbn/global-search-plugin/public/mocks';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { TrackUiMetricFn } from '../types';
import { act, fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { BehaviorSubject, of } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import type { TrackUiMetricFn } from '../types';
import { SearchBar } from './search_bar';

jest.mock(
'react-virtualized-auto-sizer',
Expand Down Expand Up @@ -87,145 +88,210 @@ describe('SearchBar', () => {
expect(await screen.findAllByTestId('nav-search-option')).toHaveLength(list.length);
};

it('correctly filters and sorts results', async () => {
searchService.find
.mockReturnValueOnce(
of(
createBatch('Discover', 'Canvas'),
createBatch({ id: 'Visualize', type: 'test' }, 'Graph')
)
)
.mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })));

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

expect(searchService.find).toHaveBeenCalledTimes(0);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
expect(searchService.find).toHaveBeenCalledWith({}, {});
await assertSearchResults(['Canvas • Kibana', 'Discover • Kibana', 'Graph • Kibana']);

simulateTypeChar('d');

await assertSearchResults(['Discover • Kibana', 'My Dashboard • Test']);
expect(searchService.find).toHaveBeenCalledTimes(2);
expect(searchService.find).toHaveBeenLastCalledWith({ term: 'd' }, {});

expect(trackUiMetric).nthCalledWith(1, 'count', 'search_focus');
expect(trackUiMetric).nthCalledWith(2, 'count', 'search_request');
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});
describe('chromeStyle: classic', () => {
const chromeStyle$ = of<ChromeStyle>('classic');

it('supports keyboard shortcuts', async () => {
render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);
act(() => {
fireEvent.keyDown(window, { key: '/', ctrlKey: true, metaKey: true });
it('correctly filters and sorts results', async () => {
searchService.find
.mockReturnValueOnce(
of(
createBatch('Discover', 'Canvas'),
createBatch({ id: 'Visualize', type: 'test' }, 'Graph')
)
)
.mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })));

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

expect(searchService.find).toHaveBeenCalledTimes(0);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
expect(searchService.find).toHaveBeenCalledWith({}, {});
await assertSearchResults(['Canvas • Kibana', 'Discover • Kibana', 'Graph • Kibana']);

simulateTypeChar('d');

await assertSearchResults(['Discover • Kibana', 'My Dashboard • Test']);
expect(searchService.find).toHaveBeenCalledTimes(2);
expect(searchService.find).toHaveBeenLastCalledWith({ term: 'd' }, {});

expect(trackUiMetric).nthCalledWith(1, 'count', 'search_focus');
expect(trackUiMetric).nthCalledWith(2, 'count', 'search_request');
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});

const inputElement = await screen.findByTestId('nav-search-input');

expect(document.activeElement).toEqual(inputElement);
it('supports keyboard shortcuts', async () => {
render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);
act(() => {
fireEvent.keyDown(window, { key: '/', ctrlKey: true, metaKey: true });
});

const inputElement = await screen.findByTestId('nav-search-input');

expect(document.activeElement).toEqual(inputElement);

expect(trackUiMetric).nthCalledWith(1, 'count', 'shortcut_used');
expect(trackUiMetric).nthCalledWith(2, 'count', 'search_focus');
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});

expect(trackUiMetric).nthCalledWith(1, 'count', 'shortcut_used');
expect(trackUiMetric).nthCalledWith(2, 'count', 'search_focus');
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});
it('only display results from the last search', async () => {
const firstSearchTrigger = new BehaviorSubject<boolean>(false);
const firstSearch = firstSearchTrigger.pipe(
filter((event) => event),
map(() => {
return createBatch('Discover', 'Canvas');
})
);
const secondSearch = of(createBatch('Visualize', 'Map'));

searchService.find.mockReturnValueOnce(firstSearch).mockReturnValueOnce(secondSearch);

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
//
simulateTypeChar('d');
await assertSearchResults(['Visualize • Kibana', 'Map • Kibana']);

firstSearchTrigger.next(true);

update();

await assertSearchResults(['Visualize • Kibana', 'Map • Kibana']);
});

it('only display results from the last search', async () => {
const firstSearchTrigger = new BehaviorSubject<boolean>(false);
const firstSearch = firstSearchTrigger.pipe(
filter((event) => event),
map(() => {
return createBatch('Discover', 'Canvas');
})
);
const secondSearch = of(createBatch('Visualize', 'Map'));

searchService.find.mockReturnValueOnce(firstSearch).mockReturnValueOnce(secondSearch);

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
//
simulateTypeChar('d');
await assertSearchResults(['Visualize • Kibana', 'Map • Kibana']);

firstSearchTrigger.next(true);

update();

await assertSearchResults(['Visualize • Kibana', 'Map • Kibana']);
it('tracks the application navigated to', async () => {
searchService.find.mockReturnValueOnce(
of(createBatch('Discover', { id: 'My Dashboard', type: 'test' }))
);

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

expect(searchService.find).toHaveBeenCalledTimes(0);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
expect(searchService.find).toHaveBeenCalledWith({}, {});
await assertSearchResults(['Discover • Kibana']);

const navSearchOptionToClick = await screen.findByTestId('nav-search-option');
act(() => {
fireEvent.click(navSearchOptionToClick);
});

expect(trackUiMetric).nthCalledWith(1, 'count', 'search_focus');
expect(trackUiMetric).nthCalledWith(2, 'click', [
'user_navigated_to_application',
'user_navigated_to_application_discover',
]);
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});
});

it('tracks the application navigated to', async () => {
searchService.find.mockReturnValueOnce(
of(createBatch('Discover', { id: 'My Dashboard', type: 'test' }))
);

render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

expect(searchService.find).toHaveBeenCalledTimes(0);

await focusAndUpdate();

expect(searchService.find).toHaveBeenCalledTimes(1);
expect(searchService.find).toHaveBeenCalledWith({}, {});
await assertSearchResults(['Discover • Kibana']);

const navSearchOptionToClick = await screen.findByTestId('nav-search-option');
act(() => {
fireEvent.click(navSearchOptionToClick);
describe('chromeStyle: project', () => {
const chromeStyle$ = of<ChromeStyle>('project');

it('supports keyboard shortcuts', async () => {
render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

act(() => {
fireEvent.keyDown(window, { key: '/', ctrlKey: true, metaKey: true });
});

const inputElement = await screen.findByTestId('nav-search-input');

expect(document.activeElement).toEqual(inputElement);

fireEvent.click(await screen.findByTestId('nav-search-conceal'));
expect(screen.queryAllByTestId('nav-search-input')).toHaveLength(0);

expect(trackUiMetric).nthCalledWith(1, 'count', 'shortcut_used');
expect(trackUiMetric).nthCalledWith(2, 'count', 'search_focus');
expect(trackUiMetric).toHaveBeenCalledTimes(2);
});

expect(trackUiMetric).nthCalledWith(1, 'count', 'search_focus');
expect(trackUiMetric).nthCalledWith(2, 'click', [
'user_navigated_to_application',
'user_navigated_to_application_discover',
]);
expect(trackUiMetric).toHaveBeenCalledTimes(2);
it('supports show/hide', async () => {
render(
<IntlProvider locale="en">
<SearchBar
globalSearch={searchService}
navigateToUrl={applications.navigateToUrl}
basePathUrl={basePathUrl}
darkMode={darkMode}
chromeStyle$={chromeStyle$}
trackUiMetric={trackUiMetric}
/>
</IntlProvider>
);

fireEvent.click(await screen.findByTestId('nav-search-reveal'));
expect(await screen.findByTestId('nav-search-input')).toBeVisible();

fireEvent.click(await screen.findByTestId('nav-search-conceal'));
expect(screen.queryAllByTestId('nav-search-input')).toHaveLength(0);

expect(trackUiMetric).nthCalledWith(1, 'count', 'search_focus');
});
});
});
Loading

0 comments on commit ec3b60b

Please sign in to comment.