1
0
mirror of https://github.com/SomboChea/ui synced 2026-01-18 09:06:14 +07:00

Search Component - Replaced class by func. comp (#339)

This commit is contained in:
Priscila Oliveira
2019-12-04 17:09:02 +01:00
committed by GitHub
parent 09b831a40d
commit 6ba721446b
18 changed files with 449 additions and 483 deletions

View File

@@ -1,272 +1,147 @@
import React from 'react';
import { BrowserRouter } from 'react-router-dom';
import { BrowserRouter as Router } from 'react-router-dom';
import '@testing-library/jest-dom/extend-expect';
import { mount, shallow } from '../../utils/test-enzyme';
import { render, fireEvent, waitForElement } from '../../utils/test-react-testing-library';
import api from '../../utils/api';
import Search from './Search';
const SEARCH_FILE_PATH = './Search';
const API_FILE_PATH = '../../utils/calls';
const URL_FILE_PATH = '../../utils/url';
/* eslint-disable verdaccio/jsx-spread */
const ComponentToBeRendered: React.FC = () => (
<Router>
<Search />
</Router>
);
// Global mocks
const event = {
stopPropagation: jest.fn(),
};
window.location.assign = jest.fn();
describe('<Search /> component test', () => {
let routerWrapper;
let wrapper;
describe('<Search /> component', () => {
beforeEach(() => {
routerWrapper = mount(
<BrowserRouter>
<Search />
</BrowserRouter>
jest.resetModules();
jest.resetAllMocks();
jest.spyOn(api, 'request').mockImplementation(() =>
Promise.resolve([
{
name: '@verdaccio/types',
version: '8.4.2',
},
{
name: 'verdaccio',
version: '4.3.5',
},
])
);
});
test('should load the component in default state', () => {
expect(routerWrapper.html()).toMatchSnapshot();
const { container } = render(<ComponentToBeRendered />);
expect(container.firstChild).toMatchSnapshot();
});
test('handleSearch: when user type package name in search component, show suggestions', async () => {
const { getByPlaceholderText, getAllByText } = render(<ComponentToBeRendered />);
const autoCompleteInput = getByPlaceholderText('Search Packages');
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
const suggestionsElements = await waitForElement(() => getAllByText('verdaccio', { exact: true }));
expect(suggestionsElements).toHaveLength(2);
expect(api.request).toHaveBeenCalledTimes(1);
});
test('onBlur: should cancel all search requests', async () => {
const Search = require(SEARCH_FILE_PATH).Search;
const { getByPlaceholderText, getByRole, getAllByText } = render(<ComponentToBeRendered />);
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
const autoCompleteInput = getByPlaceholderText('Search Packages');
wrapper = routerWrapper.find(Search).dive();
const { handleOnBlur, requestList, setState } = wrapper.instance();
const spyCancelAllSearchRequests = jest.spyOn(wrapper.instance(), 'cancelAllSearchRequests');
setState({ search: 'verdaccio' });
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
const request = {
abort: jest.fn(),
};
// adds a request for AbortController
wrapper.instance().requestList = [request];
const suggestionsElements = await waitForElement(() => getAllByText('verdaccio', { exact: true }));
expect(suggestionsElements).toHaveLength(2);
expect(api.request).toHaveBeenCalledTimes(1);
handleOnBlur(event);
expect(request.abort).toHaveBeenCalled();
expect(event.stopPropagation).toHaveBeenCalled();
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeFalsy();
expect(spyCancelAllSearchRequests).toHaveBeenCalled();
expect(requestList).toEqual([]);
fireEvent.blur(autoCompleteInput);
const listBoxElement = await waitForElement(() => getByRole('listbox'));
expect(listBoxElement).toBeEmpty();
});
test('handleSearch: when user type package name in search component and set loading to true', () => {
const Search = require(SEARCH_FILE_PATH).Search;
test('handleSearch: cancel all search requests when there is no value in search component with type method', async () => {
const { getByPlaceholderText, getByRole } = render(<ComponentToBeRendered />);
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
const autoCompleteInput = getByPlaceholderText('Search Packages');
wrapper = routerWrapper.find(Search).dive();
const { handleSearch } = wrapper.instance();
const newValue = 'verdaccio';
handleSearch(event, { newValue, method: 'type' });
expect(event.stopPropagation).toHaveBeenCalled();
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeTruthy();
expect(wrapper.state('search')).toEqual(newValue);
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: ' ', method: 'type' } });
expect(autoCompleteInput).toHaveAttribute('value', '');
const listBoxElement = await waitForElement(() => getByRole('listbox'));
expect(listBoxElement).toBeEmpty();
expect(api.request).toHaveBeenCalledTimes(0);
});
test('handleSearch: cancel all search requests when there is no value in search component with type method', () => {
const Search = require(SEARCH_FILE_PATH).Search;
test('handleSearch: when method is not type method', async () => {
const { getByPlaceholderText, getByRole } = render(<ComponentToBeRendered />);
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
const autoCompleteInput = getByPlaceholderText('Search Packages');
wrapper = routerWrapper.find(Search).dive();
const { handleSearch, requestList } = wrapper.instance();
const spy = jest.spyOn(wrapper.instance(), 'cancelAllSearchRequests');
const newValue = '';
handleSearch(event, { newValue, method: 'type' });
expect(event.stopPropagation).toHaveBeenCalled();
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeTruthy();
expect(wrapper.state('search')).toEqual(newValue);
expect(spy).toHaveBeenCalled();
expect(requestList).toEqual([]);
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: ' ', method: 'click' } });
expect(autoCompleteInput).toHaveAttribute('value', '');
const listBoxElement = await waitForElement(() => getByRole('listbox'));
expect(listBoxElement).toBeEmpty();
expect(api.request).toHaveBeenCalledTimes(0);
});
test('handleSearch: when method is not type method', () => {
const Search = require(SEARCH_FILE_PATH).Search;
test('handleSearch: loading is been displayed', async () => {
const { getByPlaceholderText, getByRole, getByText } = render(<ComponentToBeRendered />);
const autoCompleteInput = getByPlaceholderText('Search Packages');
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
wrapper = routerWrapper.find(Search).dive();
const { handleSearch } = wrapper.instance();
const newValue = '';
handleSearch(event, { newValue, method: 'click' });
expect(event.stopPropagation).toHaveBeenCalled();
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeFalsy();
expect(wrapper.state('search')).toEqual(newValue);
const loadingElement = await waitForElement(() => getByText('Loading...'));
expect(loadingElement).toBeTruthy();
});
test('handlePackagesClearRequested: should clear suggestions', () => {
const Search = require(SEARCH_FILE_PATH).Search;
test('handlePackagesClearRequested: should clear suggestions', async () => {
const { getByPlaceholderText, getAllByText, getByRole } = render(<ComponentToBeRendered />);
const autoCompleteInput = getByPlaceholderText('Search Packages');
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
wrapper = routerWrapper.find(Search).dive();
const suggestionsElements = await waitForElement(() => getAllByText('verdaccio', { exact: true }));
expect(suggestionsElements).toHaveLength(2);
const { handlePackagesClearRequested } = wrapper.instance();
fireEvent.change(autoCompleteInput, { target: { value: ' ' } });
const listBoxElement = await waitForElement(() => getByRole('listbox'));
expect(listBoxElement).toBeEmpty();
handlePackagesClearRequested();
expect(wrapper.state('suggestions')).toEqual([]);
expect(api.request).toHaveBeenCalledTimes(2);
});
describe('<Search /> component: mocks specific tests ', () => {
beforeEach(() => {
jest.resetModules();
jest.doMock('lodash/debounce', () => {
return function debounceMock(fn) {
return fn;
};
});
});
test('handleClickSearch: should change the window location on click or return key', async () => {
const { getByPlaceholderText, getAllByText, getByRole } = render(<ComponentToBeRendered />);
const autoCompleteInput = getByPlaceholderText('Search Packages');
test('handleFetchPackages: should load the packages from API', async () => {
const apiResponse = [{ name: 'verdaccio' }, { name: 'verdaccio-htpasswd' }];
const suggestions = [{ name: 'verdaccio' }, { name: 'verdaccio-htpasswd' }];
fireEvent.focus(autoCompleteInput);
fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
jest.doMock(API_FILE_PATH, () => ({
callSearch(url: string) {
return Promise.resolve(apiResponse);
},
}));
const suggestionsElements = await waitForElement(() => getAllByText('verdaccio', { exact: true }));
expect(suggestionsElements).toHaveLength(2);
const Search = require(SEARCH_FILE_PATH).Search;
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
wrapper = routerWrapper.find(Search).dive();
wrapper.setState({ search: 'verdaccio' });
const { handleFetchPackages } = wrapper.instance();
await handleFetchPackages({ value: 'verdaccio' });
expect(wrapper.state('suggestions')).toEqual(suggestions);
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeTruthy();
expect(wrapper.state('loading')).toBeFalsy();
});
test('handleFetchPackages: when browser cancel a request', async () => {
const apiResponse = { name: 'AbortError' };
jest.doMock(API_FILE_PATH, () => ({ callSearch: jest.fn(() => Promise.reject(apiResponse)) }));
const Search = require(SEARCH_FILE_PATH).Search;
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
wrapper = routerWrapper.find(Search).dive();
const { handleFetchPackages, setState } = wrapper.instance();
setState({ search: 'verdaccio' });
await handleFetchPackages({ value: 'verdaccio' });
expect(wrapper.state('error')).toBeFalsy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeFalsy();
});
test('handleFetchPackages: when API server failed request', async () => {
const apiResponse = { name: 'BAD_REQUEST' };
jest.doMock(API_FILE_PATH, () => ({
callSearch(url) {
return Promise.reject(apiResponse);
},
}));
const Search = require(SEARCH_FILE_PATH).Search;
const routerWrapper = shallow(
<BrowserRouter>
<Search />
</BrowserRouter>
);
wrapper = routerWrapper.find(Search).dive();
wrapper.setState({ search: 'verdaccio' });
const { handleFetchPackages } = wrapper.instance();
await handleFetchPackages({ value: 'verdaccio' });
expect(wrapper.state('error')).toBeTruthy();
expect(wrapper.state('loaded')).toBeFalsy();
expect(wrapper.state('loading')).toBeFalsy();
});
test('handleClickSearch: should change the window location on click or return key', () => {
const getDetailPageURL = jest.fn(() => 'detail/page/url');
jest.doMock(URL_FILE_PATH, () => ({ getDetailPageURL }));
const suggestionValue = [];
const Search = require(SEARCH_FILE_PATH).Search;
const pushHandler = jest.fn();
const routerWrapper = shallow(
<BrowserRouter>
<Search history={{ push: pushHandler }} />
</BrowserRouter>
);
wrapper = routerWrapper.find(Search).dive();
const { handleClickSearch } = wrapper.instance();
// click
handleClickSearch(event, { suggestionValue, method: 'click' });
expect(event.stopPropagation).toHaveBeenCalled();
expect(pushHandler).toHaveBeenCalledTimes(1);
// return key
handleClickSearch(event, { suggestionValue, method: 'enter' });
expect(event.stopPropagation).toHaveBeenCalled();
expect(pushHandler).toHaveBeenCalledTimes(2);
});
// click on the second suggestion
fireEvent.click(suggestionsElements[1]);
const listBoxElement = await waitForElement(() => getByRole('listbox'));
// when the page redirects, the list box should be empty again
expect(listBoxElement).toBeEmpty();
});
});

View File

@@ -1,32 +1,12 @@
import React, { KeyboardEvent, Component, ReactElement } from 'react';
import { withRouter, RouteComponentProps } from 'react-router-dom';
import { SuggestionSelectedEventData, ChangeEvent } from 'react-autosuggest';
import styled from '@emotion/styled';
import { default as IconSearch } from '@material-ui/icons/Search';
import React, { useState, FormEvent, useCallback } from 'react';
import debounce from 'lodash/debounce';
import { RouteComponentProps, withRouter } from 'react-router';
import { SuggestionSelectedEventData } from 'react-autosuggest';
import InputAdornment from '../../muiComponents/InputAdornment';
import AutoComplete from '../AutoComplete';
import { callSearch } from '../../utils/calls';
import { Theme } from '../../design-tokens/theme';
export interface State {
search: string;
suggestions: unknown[];
loading: boolean;
loaded: boolean;
error: boolean;
}
export type cancelAllSearchRequests = () => void;
export type handlePackagesClearRequested = () => void;
export type handleSearch = (event: React.FormEvent<HTMLInputElement>, { newValue, method }: ChangeEvent) => void;
export type handleClickSearch = (
event: KeyboardEvent<HTMLInputElement>,
{ suggestionValue, method }: { suggestionValue: object[]; method: string }
) => void;
export type handleFetchPackages = ({ value: string }) => Promise<void>;
export type onBlur = (event: React.FormEvent<HTMLInputElement>) => void;
import SearchAdornment from './SearchAdornment';
const CONSTANTS = {
API_DELAY: 300,
@@ -34,168 +14,142 @@ const CONSTANTS = {
ABORT_ERROR: 'AbortError',
};
const StyledInputAdornment = styled(InputAdornment)<{ theme?: Theme }>(props => ({
color: props.theme && props.theme.palette.white,
}));
export class Search extends Component<RouteComponentProps<{}>, State> {
constructor(props: RouteComponentProps<{}>) {
super(props);
this.state = {
search: '',
suggestions: [],
// loading: A boolean value to indicate that request is in pending state.
loading: false,
// loaded: A boolean value to indicate that result has been loaded.
loaded: false,
// error: A boolean value to indicate API error.
error: false,
};
this.requestList = [];
}
public render(): ReactElement<HTMLElement> {
const { suggestions, search, loaded, loading, error } = this.state;
return (
<AutoComplete
onBlur={this.handleOnBlur}
onChange={this.handleSearch}
onCleanSuggestions={this.handlePackagesClearRequested}
onClick={this.handleClickSearch}
onSuggestionsFetch={debounce(this.handleFetchPackages, CONSTANTS.API_DELAY)}
placeholder={CONSTANTS.PLACEHOLDER_TEXT}
startAdornment={this.getAdorment()}
suggestions={suggestions}
suggestionsError={error}
suggestionsLoaded={loaded}
suggestionsLoading={loading}
value={search}
/>
);
}
const Search: React.FC<RouteComponentProps> = ({ history }) => {
const [suggestions, setSuggestions] = useState([]);
const [loaded, setLoaded] = useState(false);
const [search, setSearch] = useState('');
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const [requestList, setRequestList] = useState<Array<{ abort: () => void }>>([]);
/**
* Cancel all the requests which are in pending state.
*/
private cancelAllSearchRequests: cancelAllSearchRequests = () => {
this.requestList.forEach(request => request.abort());
this.requestList = [];
};
/**
* Cancel all the request from list and make request list empty.
*/
private handlePackagesClearRequested: handlePackagesClearRequested = () => {
this.setState({
suggestions: [],
});
};
/**
* onChange method for the input element.
*/
private handleSearch: handleSearch = (event, { newValue, method }) => {
// stops event bubbling
event.stopPropagation();
if (method === 'type') {
const value = newValue.trim();
this.setState(
{
search: value,
loading: true,
loaded: false,
error: false,
},
() => {
/**
* A use case where User keeps adding and removing value in input field,
* so we cancel all the existing requests when input is empty.
*/
if (value.length === 0) {
this.cancelAllSearchRequests();
}
}
);
}
};
/**
* When an user select any package by clicking or pressing return key.
*/
private handleClickSearch = (
event: React.FormEvent<HTMLInputElement>,
{ suggestionValue, method }: SuggestionSelectedEventData<unknown>
): void | undefined => {
const { history } = this.props;
// stops event bubbling
event.stopPropagation();
switch (method) {
case 'click':
case 'enter':
this.setState({ search: '' });
history.push(`/-/web/detail/${suggestionValue}`);
break;
}
};
/**
* Fetch packages from API.
* For AbortController see: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
*/
private handleFetchPackages: handleFetchPackages = async ({ value }) => {
try {
const controller = new window.AbortController();
const signal = controller.signal;
// Keep track of search requests.
this.requestList.push(controller);
const suggestions = await callSearch(value, signal);
// @ts-ignore
this.setState({
suggestions,
loaded: true,
});
} catch (error) {
/**
* AbortError is not the API error.
* It means browser has cancelled the API request.
*/
if (error.name === CONSTANTS.ABORT_ERROR) {
this.setState({ error: false, loaded: false });
} else {
this.setState({ error: true, loaded: false });
}
} finally {
this.setState({ loading: false });
}
};
private requestList: AbortController[];
public getAdorment(): JSX.Element {
return (
<StyledInputAdornment position={'start'}>
<IconSearch />
</StyledInputAdornment>
);
}
const cancelAllSearchRequests = useCallback(() => {
requestList.forEach(request => request.abort());
setRequestList([]);
}, [requestList, setRequestList]);
/**
* As user focuses out from input, we cancel all the request from requestList
* and set the API state parameters to default boolean values.
*/
private handleOnBlur: onBlur = event => {
// stops event bubbling
event.stopPropagation();
this.setState(
{
loaded: false,
loading: false,
error: false,
},
() => this.cancelAllSearchRequests()
);
};
}
const handleOnBlur = useCallback(
(event: FormEvent<HTMLInputElement>) => {
// stops event bubbling
event.stopPropagation();
setLoaded(false);
setLoading(false);
setError(false);
cancelAllSearchRequests();
},
[setLoaded, setLoading, cancelAllSearchRequests, setError]
);
/**
* onChange method for the input element.
*/
const handleSearch = useCallback(
(event: FormEvent<HTMLInputElement>, { newValue, method }) => {
// stops event bubbling
event.stopPropagation();
if (method === 'type') {
const value = newValue.trim();
setLoading(true);
setError(false);
setSearch(value);
setLoaded(false);
/**
* A use case where User keeps adding and removing value in input field,
* so we cancel all the existing requests when input is empty.
*/
if (value.length === 0) {
cancelAllSearchRequests();
}
}
},
[cancelAllSearchRequests]
);
/**
* Cancel all the request from list and make request list empty.
*/
const handlePackagesClearRequested = useCallback(() => {
setSuggestions([]);
}, [setSuggestions]);
/**
* When an user select any package by clicking or pressing return key.
*/
const handleClickSearch = useCallback(
(
event: FormEvent<HTMLInputElement>,
{ suggestionValue, method }: SuggestionSelectedEventData<unknown>
): void | undefined => {
// stops event bubbling
event.stopPropagation();
switch (method) {
case 'click':
case 'enter':
setSearch('');
history.push(`/-/web/detail/${suggestionValue}`);
break;
}
},
[history]
);
/**
* Fetch packages from API.
* For AbortController see: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
*/
const handleFetchPackages = useCallback(
async ({ value }: { value: string }) => {
try {
const controller = new window.AbortController();
const signal = controller.signal;
// Keep track of search requests.
setRequestList([...requestList, controller]);
const suggestions = await callSearch(value, signal);
// @ts-ignore FIXME: Argument of type 'unknown' is not assignable to parameter of type 'SetStateAction<never[]>'
setSuggestions(suggestions);
setLoaded(true);
} catch (error) {
/**
* AbortError is not the API error.
* It means browser has cancelled the API request.
*/
if (error.name === CONSTANTS.ABORT_ERROR) {
setError(false);
setLoaded(false);
} else {
setError(true);
setLoaded(false);
}
} finally {
setLoading(false);
}
},
[requestList, setRequestList, setSuggestions, setLoaded, setError, setLoading]
);
return (
<AutoComplete
onBlur={handleOnBlur}
onChange={handleSearch}
onCleanSuggestions={handlePackagesClearRequested}
onClick={handleClickSearch}
onSuggestionsFetch={debounce(handleFetchPackages, CONSTANTS.API_DELAY)}
placeholder={CONSTANTS.PLACEHOLDER_TEXT}
startAdornment={<SearchAdornment />}
suggestions={suggestions}
suggestionsError={error}
suggestionsLoaded={loaded}
suggestionsLoading={loading}
value={search}
/>
);
};
export default withRouter(Search);

View File

@@ -0,0 +1,18 @@
import React from 'react';
import Search from '@material-ui/icons/Search';
import styled from '@emotion/styled';
import InputAdornment from '../../muiComponents/InputAdornment';
import { Theme } from '../../design-tokens/theme';
const StyledInputAdornment = styled(InputAdornment)<{ theme?: Theme }>(props => ({
color: props.theme && props.theme.palette.white,
}));
const SearchAdornment: React.FC = () => (
<StyledInputAdornment position={'start'}>
<Search />
</StyledInputAdornment>
);
export default SearchAdornment;

View File

@@ -1,3 +1,87 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Search /> component test should load the component in default state 1`] = `"<div class=\\"css-pnwf4z-Wrapper e1rflf270\\"><div role=\\"combobox\\" aria-haspopup=\\"listbox\\" aria-owns=\\"react-autowhatever-1\\" aria-expanded=\\"false\\" class=\\"react-autosuggest__container\\"><div class=\\"MuiFormControl-root MuiTextField-root react-autosuggest__input css-ae5nkp-StyledTextField e1rflf271 MuiFormControl-fullWidth\\" aria-autocomplete=\\"list\\" aria-controls=\\"react-autowhatever-1\\"><div class=\\"MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedStart\\"><div class=\\"MuiInputAdornment-root css-1wub48n-StyledInputAdornment e1n3ivvz0 MuiInputAdornment-positionStart\\"><svg class=\\"MuiSvgIcon-root\\" focusable=\\"false\\" viewBox=\\"0 0 24 24\\" aria-hidden=\\"true\\" role=\\"presentation\\"><path d=\\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\\"></path></svg></div><input aria-invalid=\\"false\\" autocomplete=\\"off\\" placeholder=\\"Search Packages\\" type=\\"text\\" class=\\"MuiInputBase-input MuiInput-input MuiInputBase-inputAdornedStart\\" value=\\"\\"></div></div><div class=\\"MuiPaper-root MuiPaper-elevation1 react-autosuggest__suggestions-container css-kc7cda-SuggestionContainer e1rflf272\\" id=\\"react-autowhatever-1\\" role=\\"listbox\\"></div></div></div>"`;
exports[`<Search /> component should load the component in default state 1`] = `
.emotion-6 {
width: 100%;
height: 32px;
position: relative;
z-index: 1;
}
.emotion-2 .MuiInputBase-root:before {
content: '';
border: none;
}
.emotion-2 .MuiInputBase-root:after {
border-color: #fff;
}
.emotion-2 .MuiInputBase-root:hover:before {
content: none;
}
.emotion-2 .MuiInputBase-input {
color: #fff;
}
.emotion-0 {
color: #fff;
}
.emotion-4 {
max-height: 500px;
overflow-y: auto;
}
<div
class="emotion-6 emotion-7"
>
<div
aria-expanded="false"
aria-haspopup="listbox"
aria-owns="react-autowhatever-1"
class="react-autosuggest__container"
role="combobox"
>
<div
aria-autocomplete="list"
aria-controls="react-autowhatever-1"
class="MuiFormControl-root MuiTextField-root react-autosuggest__input emotion-2 emotion-3 MuiFormControl-fullWidth"
>
<div
class="MuiInputBase-root MuiInput-root MuiInput-underline MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedStart"
>
<div
class="MuiInputAdornment-root emotion-0 emotion-1 MuiInputAdornment-positionStart"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
role="presentation"
viewBox="0 0 24 24"
>
<path
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
/>
</svg>
</div>
<input
aria-invalid="false"
autocomplete="off"
class="MuiInputBase-input MuiInput-input MuiInputBase-inputAdornedStart"
placeholder="Search Packages"
type="text"
value=""
/>
</div>
</div>
<div
class="MuiPaper-root MuiPaper-elevation1 react-autosuggest__suggestions-container emotion-4 emotion-5"
id="react-autowhatever-1"
role="listbox"
/>
</div>
</div>
`;