Skip to content

Commit

Permalink
Merge pull request elastic#36 from Elastic-AWP-Platform/search-bar-te…
Browse files Browse the repository at this point in the history
…sts-and-nav

Search Bar improvements
  • Loading branch information
mitodrummer authored Jan 26, 2022
2 parents 72e580c + a211bbf commit b9c11a9
Show file tree
Hide file tree
Showing 7 changed files with 219 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,7 @@ export const useProcessTree = ({ sessionEntityId, data, searchQuery }: UseProces
useEffect(() => {
setSearchResults(searchProcessTree(processMap, searchQuery));
autoExpandProcessTree(processMap);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchQuery]);
}, [searchQuery, processMap]);

// set new orphans array on the session leader
const sessionLeader = processMap[sessionEntityId];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface ProcessTreeDeps {
// currently selected process
selectedProcess?: Process | null;
onProcessSelected?: (process: Process) => void;
setSearchResults?: (results: Process[]) => void;
}

export const ProcessTree = ({
Expand All @@ -48,10 +49,11 @@ export const ProcessTree = ({
searchQuery,
selectedProcess,
onProcessSelected,
setSearchResults,
}: ProcessTreeDeps) => {
const styles = useStyles();

const { sessionLeader, processMap } = useProcessTree({
const { sessionLeader, processMap, searchResults } = useProcessTree({
sessionEntityId,
data,
searchQuery,
Expand All @@ -60,6 +62,12 @@ export const ProcessTree = ({
const scrollerRef = useRef<HTMLDivElement>(null);
const selectionAreaRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (setSearchResults) {
setSearchResults(searchResults);
}
}, [searchResults, setSearchResults]);

useScroll({
div: scrollerRef.current,
handler: (pos: number, endReached: boolean) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export const useStyles = ({ depth, hasAlerts }: StylesDeps) => {
};

const searchHighlight = `
background-color: ${colors.highlight};
color: ${colors.text};
background-color: yellow;
color: black;
border-radius: ${border.radius.medium};
`;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
*/
import React, { useState } from 'react';
import {
EuiSearchBar,
EuiEmptyPrompt,
EuiButton,
EuiSplitPanel,
Expand All @@ -18,8 +17,9 @@ import { SectionLoading } from '../../shared_imports';
import { ProcessTree } from '../ProcessTree';
import { Process, ProcessEvent } from '../../../common/types/process_tree';
import { SessionViewDetailPanel } from '../SessionViewDetailPanel';
import { SessionViewSearchBar } from '../SessionViewSearchBar';
import { useStyles } from './styles';
import { useSearchQuery, useFetchSessionViewProcessEvents } from './hooks';
import { useFetchSessionViewProcessEvents } from './hooks';

interface SessionViewDeps {
// the root node of the process tree to render. e.g process.entry.entity_id or process.session.entity_id
Expand All @@ -44,7 +44,8 @@ export const SessionView = ({ sessionEntityId, height, jumpToEvent }: SessionVie
}
};

const { onSearch, searchQuery } = useSearchQuery();
const [searchQuery, setSearchQuery] = useState('');
const [searchResults, setSearchResults ] = useState<Process[] | null>(null);

const {
data,
Expand Down Expand Up @@ -105,6 +106,7 @@ export const SessionView = ({ sessionEntityId, height, jumpToEvent }: SessionVie
hasNextPage={hasNextPage}
fetchNextPage={fetchNextPage}
fetchPreviousPage={fetchPreviousPage}
setSearchResults={setSearchResults}
/>
</div>
);
Expand Down Expand Up @@ -139,8 +141,13 @@ export const SessionView = ({ sessionEntityId, height, jumpToEvent }: SessionVie
return (
<>
<EuiFlexGroup>
<EuiFlexItem data-test-subj="sessionViewProcessEventsSearch">
<EuiSearchBar query={searchQuery} onChange={onSearch} />
<EuiFlexItem data-test-subj="sessionViewProcessEventsSearch" css={{ position: 'relative' }}>
<SessionViewSearchBar
searchQuery={searchQuery}
setSearchQuery={setSearchQuery}
setSelectedProcess={setSelectedProcess}
searchResults={searchResults}
/>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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 { processMock } from '../../../common/mocks/constants/session_view_process.mock';
import { AppContextTestRender, createAppRootMockRenderer } from '../../test';
import { SessionViewSearchBar } from './index';
import userEvent from '@testing-library/user-event';
import { fireEvent } from '@testing-library/dom';

describe('SessionViewSearchBar component', () => {
let render: () => ReturnType<AppContextTestRender['render']>;
let renderResult: ReturnType<typeof render>;
let mockedContext: AppContextTestRender;

beforeEach(() => {
mockedContext = createAppRootMockRenderer();
});

it('handles a typed search query', async () => {
const mockSetSearchQuery = jest.fn((query) => query);
const mockSetSelectedProcess = jest.fn((process) => process);

renderResult = mockedContext.render(
<SessionViewSearchBar
searchQuery="ls"
searchResults={[]}
setSelectedProcess={mockSetSelectedProcess}
setSearchQuery={mockSetSearchQuery}
/>
);

const searchInput = renderResult.getByTestId('searchInput').querySelector('input');

expect(searchInput?.value).toEqual('ls');

if (searchInput) {
userEvent.type(searchInput, ' -la');
fireEvent.keyUp(searchInput, { key: 'Enter', code: 'Enter' });
}

expect(searchInput?.value).toEqual('ls -la');
expect(mockSetSearchQuery.mock.calls.length).toBe(1);
expect(mockSetSearchQuery.mock.results[0].value).toBe('ls -la');
});

it('shows a results navigator when searchResults provided', async () => {
const processMock2 = { ...processMock };
const processMock3 = { ...processMock };
const mockResults = [processMock, processMock2, processMock3];
const mockSetSearchQuery = jest.fn((query) => query);
const mockSetSelectedProcess = jest.fn((process) => process);

renderResult = mockedContext.render(
<SessionViewSearchBar
searchQuery="ls"
searchResults={mockResults}
setSelectedProcess={mockSetSelectedProcess}
setSearchQuery={mockSetSearchQuery}
/>
);

const searchPagination = renderResult.getByTestId('searchPagination');
expect(searchPagination).toBeTruthy();

const paginationTextClass = '.euiPagination__compressedText';
expect(searchPagination.querySelector(paginationTextClass)?.textContent).toEqual('1 of 3');

userEvent.click(renderResult.getByTestId('pagination-button-next'));
expect(searchPagination.querySelector(paginationTextClass)?.textContent).toEqual('2 of 3');

const searchInput = renderResult.getByTestId('searchInput').querySelector('input');

if (searchInput) {
userEvent.type(searchInput, ' -la');
fireEvent.keyUp(searchInput, { key: 'Enter', code: 'Enter' });
}

// after search is changed, results index should reset to 1
expect(searchPagination.querySelector(paginationTextClass)?.textContent).toEqual('1 of 3');

// setSelectedProcess should be called 3 times:
// 1. searchResults is set so auto select first item
// 2. next button hit, so call with 2nd item
// 3. search changed, so call with first result.
expect(mockSetSelectedProcess.mock.calls.length).toBe(3);
expect(mockSetSelectedProcess.mock.results[0].value).toEqual(processMock);
expect(mockSetSelectedProcess.mock.results[1].value).toEqual(processMock2);
expect(mockSetSelectedProcess.mock.results[1].value).toEqual(processMock);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* 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, { useState, useEffect } from 'react';
import { EuiSearchBar, EuiPagination } from '@elastic/eui';
import { EuiSearchBarOnChangeArgs } from '@elastic/eui';
import { Process } from '../../../common/types/process_tree';
import { useStyles } from './styles';

interface SessionViewSearchBarDeps {
searchQuery: string;
setSearchQuery(val: string): void;
searchResults: Process[] | null;
setSelectedProcess(process: Process): void;
}

/**
* The main wrapper component for the session view.
*/
export const SessionViewSearchBar = ({
searchQuery,
setSearchQuery,
setSelectedProcess,
searchResults,
}: SessionViewSearchBarDeps) => {
const styles = useStyles();

const [selectedResult, setSelectedResult] = useState(0);

const onSearch = ({ query }: EuiSearchBarOnChangeArgs) => {
setSelectedResult(0);

if (query) {
setSearchQuery(query.text);
} else {
setSearchQuery('');
}
};

const renderSearchResults = () => {
if (searchResults?.length) {
return (
<EuiPagination
data-test-subj="searchPagination"
css={styles.pagination}
pageCount={searchResults.length}
activePage={selectedResult}
onPageClick={setSelectedResult}
compressed
/>
);
}
};

useEffect(() => {
if (searchResults) {
const process = searchResults[selectedResult];

setSelectedProcess(process);
}
}, [searchResults, setSelectedProcess, selectedResult]);

return (
<div data-test-subj="searchInput" css={{ position: 'relative' }}>
<EuiSearchBar query={searchQuery} onChange={onSearch} />
{renderSearchResults()}
</div>
);
};
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 { useMemo } from 'react';
import { useEuiTheme } from '@elastic/eui';
import { CSSObject } from '@emotion/react';

export const useStyles = () => {
const { euiTheme } = useEuiTheme();

const cached = useMemo(() => {
const pagination: CSSObject = {
position: 'absolute',
top: euiTheme.size.s,
right: euiTheme.size.xxl,
};

return {
pagination,
};
}, [euiTheme]);

return cached;
};

0 comments on commit b9c11a9

Please sign in to comment.