Invoke search only when SearchModal is visible
Signed-off-by: Vincenzo Scamporlino <me@vinzscam.dev>
This commit is contained in:
@@ -14,12 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, {
|
||||
useContext,
|
||||
ReactNode,
|
||||
PropsWithChildren,
|
||||
useEffect,
|
||||
} from 'react';
|
||||
import React, { useContext, ReactNode, PropsWithChildren } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ApiHolder } from '@backstage/core-plugin-api';
|
||||
import { ApiAggregator } from './ApiAggregator';
|
||||
|
||||
@@ -169,13 +169,16 @@ export type SearchBarProps = Partial<SearchBarBaseProps>;
|
||||
export const SearchBar = ({ onChange, ...props }: SearchBarProps) => {
|
||||
const { term, setTerm } = useSearch();
|
||||
|
||||
const handleChange = (newValue: string) => {
|
||||
if (onChange) {
|
||||
onChange(newValue);
|
||||
} else {
|
||||
setTerm(newValue);
|
||||
}
|
||||
};
|
||||
const handleChange = useCallback(
|
||||
(newValue: string) => {
|
||||
if (onChange) {
|
||||
onChange(newValue);
|
||||
} else {
|
||||
setTerm(newValue);
|
||||
}
|
||||
},
|
||||
[onChange, setTerm],
|
||||
);
|
||||
|
||||
return <SearchBarBase value={term} onChange={handleChange} {...props} />;
|
||||
};
|
||||
|
||||
@@ -24,7 +24,6 @@ import { rootRouteRef } from '../../plugin';
|
||||
import { searchApiRef } from '../../apis';
|
||||
|
||||
import { SearchModal } from './SearchModal';
|
||||
import { SearchContextProvider } from '../SearchContext';
|
||||
|
||||
describe('SearchModal', () => {
|
||||
const query = jest.fn().mockResolvedValue({ results: [] });
|
||||
@@ -34,14 +33,16 @@ describe('SearchModal', () => {
|
||||
[searchApiRef, { query }],
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
query.mockClear();
|
||||
});
|
||||
|
||||
const toggleModal = jest.fn();
|
||||
|
||||
it('Should render the Modal correctly', async () => {
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<SearchContextProvider>
|
||||
<SearchModal open toggleModal={toggleModal} />
|
||||
</SearchContextProvider>
|
||||
<SearchModal open hidden={false} toggleModal={toggleModal} />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
@@ -51,14 +52,13 @@ describe('SearchModal', () => {
|
||||
);
|
||||
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
||||
expect(query).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Calls toggleModal handler', async () => {
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<SearchContextProvider>
|
||||
<SearchModal open toggleModal={toggleModal} />
|
||||
</SearchContextProvider>
|
||||
<SearchModal open toggleModal={toggleModal} />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
@@ -66,7 +66,25 @@ describe('SearchModal', () => {
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(query).toHaveBeenCalledTimes(1);
|
||||
userEvent.keyboard('{esc}');
|
||||
expect(toggleModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should render SearchModal hiding its content', async () => {
|
||||
const { getByTestId } = await renderInTestApp(
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<SearchModal open hidden toggleModal={toggleModal} />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/search': rootRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(getByTestId('search-bar-next')).toBeInTheDocument();
|
||||
expect(getByTestId('search-bar-next')).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,6 +39,7 @@ import { rootRouteRef } from '../../plugin';
|
||||
|
||||
export interface SearchModalProps {
|
||||
open?: boolean;
|
||||
hidden?: boolean;
|
||||
toggleModal: () => void;
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ const useStyles = makeStyles(theme => ({
|
||||
viewResultsLink: { verticalAlign: '0.5em' },
|
||||
}));
|
||||
|
||||
export const Modal = ({ open = true, toggleModal }: SearchModalProps) => {
|
||||
export const Modal = ({ toggleModal }: SearchModalProps) => {
|
||||
const getSearchLink = useRouteRef(rootRouteRef);
|
||||
const classes = useStyles();
|
||||
|
||||
@@ -75,16 +76,7 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
classes={{
|
||||
paperFullWidth: classes.paperFullWidth,
|
||||
}}
|
||||
onClose={toggleModal}
|
||||
aria-labelledby="search-modal-title"
|
||||
open={open}
|
||||
fullWidth
|
||||
maxWidth="lg"
|
||||
>
|
||||
<>
|
||||
<DialogTitle>
|
||||
<Paper className={classes.container}>
|
||||
<SearchBar className={classes.input} />
|
||||
@@ -139,14 +131,34 @@ export const Modal = ({ open = true, toggleModal }: SearchModalProps) => {
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const SearchModal = ({ open = true, toggleModal }: SearchModalProps) => {
|
||||
export const SearchModal = ({
|
||||
open = true,
|
||||
hidden,
|
||||
toggleModal,
|
||||
}: SearchModalProps) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<SearchContextProvider>
|
||||
<Modal open={open} toggleModal={toggleModal} />
|
||||
</SearchContextProvider>
|
||||
<Dialog
|
||||
classes={{
|
||||
paperFullWidth: classes.paperFullWidth,
|
||||
}}
|
||||
onClose={toggleModal}
|
||||
aria-labelledby="search-modal-title"
|
||||
fullWidth
|
||||
maxWidth="lg"
|
||||
open={open}
|
||||
hidden={hidden}
|
||||
>
|
||||
{open && (
|
||||
<SearchContextProvider>
|
||||
<Modal toggleModal={toggleModal} />
|
||||
</SearchContextProvider>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,30 +13,42 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import { SidebarItem } from '@backstage/core-components';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { SearchModal } from '../SearchModal';
|
||||
import { useSearch } from '../SearchContext';
|
||||
|
||||
export type SidebarSearchModalProps = {
|
||||
icon?: IconComponent;
|
||||
};
|
||||
|
||||
export const SidebarSearchModal = (props: SidebarSearchModalProps) => {
|
||||
const { open, toggleModal } = useSearch();
|
||||
const [state, setState] = useState({ hidden: true, opened: false });
|
||||
const Icon = props.icon ? props.icon : SearchIcon;
|
||||
|
||||
const handleToggleModal = useCallback(
|
||||
() =>
|
||||
setState(previousState => ({
|
||||
opened: true,
|
||||
hidden: !previousState.hidden,
|
||||
})),
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarItem
|
||||
className="search-icon"
|
||||
icon={Icon}
|
||||
text="Search"
|
||||
onClick={toggleModal}
|
||||
onClick={handleToggleModal}
|
||||
/>
|
||||
<SearchModal
|
||||
open={state.opened}
|
||||
hidden={state.hidden}
|
||||
toggleModal={handleToggleModal}
|
||||
/>
|
||||
<SearchModal open={open} toggleModal={toggleModal} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user