Skip to content

Commit

Permalink
Adds selected filters in the url as a query parameter
Browse files Browse the repository at this point in the history
This patch updates url based on the selected filters,
search query and sort by

Signed-off-by: Shiv Verma <shverma@redhat.com>
  • Loading branch information
pratap0007 committed Mar 19, 2021
1 parent c302526 commit 9125fa3
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 49 deletions.
7 changes: 7 additions & 0 deletions ui/src/common/params.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export enum Params {
Query = 'query',
SortBY = 'sortBy',
Category = 'category',
Kind = 'kind',
Catalog = 'catalog'
}
23 changes: 13 additions & 10 deletions ui/src/containers/App/__snapshots__/App.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ exports[`App should render the component correctly and match the snapshot 1`] =
<BrowserRouter>
<Router history={{...}}>
<ParseUrl>
</ParseUrl>
<Page header={{...}} className=\\"hub-page\\" isManagedSidebar={false} isBreadcrumbWidthLimited={false} defaultManagedSidebarIsOpen={true} onPageResize={[Function: onPageResize]} mainTabIndex={-1} isNotificationDrawerExpanded={false} onNotificationDrawerExpand={[Function: onNotificationDrawerExpand]}>
<div className=\\"pf-c-page hub-page\\">
<Memo(wrappedComponent)>
Expand Down Expand Up @@ -98,13 +101,13 @@ exports[`App should render the component correctly and match the snapshot 1`] =
<div className=\\"hub-sort\\">
<Select variant=\\"typeahead\\" typeAheadAriaLabel=\\"Sort By\\" onToggle={[Function: onToggle]} onSelect={[Function: onSelect]} onClear={[Function: clearSelection]} isOpen={false} selections=\\"\\" placeholderText=\\"Sort By\\" className=\\"\\" direction=\\"down\\" toggleId={{...}} isGrouped={false} isPlain={false} isDisabled={false} isCreatable={false} aria-label=\\"\\" aria-labelledby=\\"\\" clearSelectionsAriaLabel=\\"Clear all\\" toggleAriaLabel=\\"Options menu\\" removeSelectionAriaLabel=\\"Remove\\" createText=\\"Create\\" noResultsFoundText=\\"No results found\\" width=\\"\\" onCreateOption={[Function: onCreateOption]} toggleIcon={{...}} onFilter={{...}} customContent={{...}} hasInlineFilter={false} inlineFilterPlaceholderText={{...}} customBadgeText={{...}} inputIdPrefix=\\"\\" menuAppendTo=\\"inline\\" ouiaSafe={true}>
<GenerateId prefix=\\"pf-random-id-\\">
<div className=\\"pf-c-select\\" data-ouia-component-type=\\"PF4/Select\\" data-ouia-safe={true} data-ouia-component-id={0}>
<SelectToggle id=\\"pf-select-toggle-id-0\\" parentRef={{...}} menuRef={{...}} isOpen={false} isPlain={false} onToggle={[Function: onToggle]} onEnter={[Function (anonymous)]} onClose={[Function (anonymous)]} variant=\\"typeahead\\" aria-labelledby=\\" pf-select-toggle-id-0\\" aria-label=\\"Options menu\\" handleTypeaheadKeys={[Function (anonymous)]} isDisabled={false} hasClearButton={true} className=\\"\\" isActive={false} type=\\"button\\">
<div className=\\"pf-c-select\\" data-ouia-component-type=\\"PF4/Select\\" data-ouia-safe={true} data-ouia-component-id={9}>
<SelectToggle id=\\"pf-select-toggle-id-2\\" parentRef={{...}} menuRef={{...}} isOpen={false} isPlain={false} onToggle={[Function: onToggle]} onEnter={[Function (anonymous)]} onClose={[Function (anonymous)]} variant=\\"typeahead\\" aria-labelledby=\\" pf-select-toggle-id-2\\" aria-label=\\"Options menu\\" handleTypeaheadKeys={[Function (anonymous)]} isDisabled={false} hasClearButton={true} className=\\"\\" isActive={false} type=\\"button\\">
<div className=\\"pf-c-select__toggle pf-m-typeahead\\" onClick={[Function: onClick]} onKeyDown={[Function (anonymous)]}>
<div className=\\"pf-c-select__toggle-wrapper\\">
<input className=\\"pf-c-form-control pf-c-select__toggle-typeahead\\" aria-activedescendant={{...}} id=\\"pf-select-toggle-id-0-select-typeahead\\" aria-label=\\"Sort By\\" placeholder=\\"Sort By\\" value=\\"\\" type=\\"text\\" onClick={[Function (anonymous)]} onChange={[Function (anonymous)]} onFocus={[Function (anonymous)]} autoComplete=\\"off\\" disabled={false} />
<input className=\\"pf-c-form-control pf-c-select__toggle-typeahead\\" aria-activedescendant={{...}} id=\\"pf-select-toggle-id-2-select-typeahead\\" aria-label=\\"Sort By\\" placeholder=\\"Sort By\\" value=\\"\\" type=\\"text\\" onClick={[Function (anonymous)]} onChange={[Function (anonymous)]} onFocus={[Function (anonymous)]} autoComplete=\\"off\\" disabled={false} />
</div>
<button id=\\"pf-select-toggle-id-0\\" aria-labelledby=\\" pf-select-toggle-id-0\\" aria-expanded={false} aria-haspopup=\\"listbox\\" type=\\"button\\" className=\\"pf-c-button pf-c-select__toggle-button pf-m-plain\\" aria-label=\\"Options menu\\" onClick={[Function: onClick]} tabIndex={-1} disabled={false}>
<button id=\\"pf-select-toggle-id-2\\" aria-labelledby=\\" pf-select-toggle-id-2\\" aria-expanded={false} aria-haspopup=\\"listbox\\" type=\\"button\\" className=\\"pf-c-button pf-c-select__toggle-button pf-m-plain\\" aria-label=\\"Options menu\\" onClick={[Function: onClick]} tabIndex={-1} disabled={false}>
<CaretDownIcon className=\\"pf-c-select__toggle-arrow\\" color=\\"currentColor\\" size=\\"sm\\" noVerticalAlign={false}>
<svg style={{...}} fill=\\"currentColor\\" height=\\"1em\\" width=\\"1em\\" viewBox=\\"0 0 320 512\\" aria-labelledby={{...}} aria-hidden={true} role=\\"img\\" className=\\"pf-c-select__toggle-arrow\\">
<path d=\\"M31.3 192h257.3c17.8 0 26.7 21.5 14.1 34.1L174.1 354.8c-7.8 7.8-20.5 7.8-28.3 0L17.2 226.1C4.6 213.5 13.5 192 31.3 192z\\" transform=\\"\\" />
Expand Down Expand Up @@ -139,7 +142,7 @@ exports[`App should render the component correctly and match the snapshot 1`] =
<GridItem span={3}>
<div className=\\"pf-l-grid__item pf-m-3-col\\">
<Button variant=\\"plain\\" aria-label=\\"Clear\\" onClick={[Function: res]}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={1}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={6}>
<TimesIcon color=\\"currentColor\\" size=\\"sm\\" noVerticalAlign={false}>
<svg style={{...}} fill=\\"currentColor\\" height=\\"1em\\" width=\\"1em\\" viewBox=\\"0 0 352 512\\" aria-labelledby={{...}} aria-hidden={true} role=\\"img\\">
<path d=\\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\\" transform=\\"\\" />
Expand Down Expand Up @@ -178,7 +181,7 @@ exports[`App should render the component correctly and match the snapshot 1`] =
<GridItem span={3}>
<div className=\\"pf-l-grid__item pf-m-3-col\\">
<Button variant=\\"plain\\" aria-label=\\"Clear\\" onClick={[Function: res]}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={2}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={7}>
<TimesIcon color=\\"currentColor\\" size=\\"sm\\" noVerticalAlign={false}>
<svg style={{...}} fill=\\"currentColor\\" height=\\"1em\\" width=\\"1em\\" viewBox=\\"0 0 352 512\\" aria-labelledby={{...}} aria-hidden={true} role=\\"img\\">
<path d=\\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\\" transform=\\"\\" />
Expand Down Expand Up @@ -217,7 +220,7 @@ exports[`App should render the component correctly and match the snapshot 1`] =
<GridItem span={3}>
<div className=\\"pf-l-grid__item pf-m-3-col\\">
<Button variant=\\"plain\\" aria-label=\\"Clear\\" onClick={[Function: res]}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={3}>
<button onClick={[Function: res]} aria-disabled={false} aria-label=\\"Clear\\" className=\\"pf-c-button pf-m-plain\\" disabled={false} tabIndex={[undefined]} type=\\"button\\" data-ouia-component-type=\\"PF4/Button\\" data-ouia-safe={true} data-ouia-component-id={8}>
<TimesIcon color=\\"currentColor\\" size=\\"sm\\" noVerticalAlign={false}>
<svg style={{...}} fill=\\"currentColor\\" height=\\"1em\\" width=\\"1em\\" viewBox=\\"0 0 352 512\\" aria-labelledby={{...}} aria-hidden={true} role=\\"img\\">
<path d=\\"M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z\\" transform=\\"\\" />
Expand Down Expand Up @@ -245,16 +248,16 @@ exports[`App should render the component correctly and match the snapshot 1`] =
</GridItem>
<GridItem span={10} rowSpan={1}>
<div className=\\"pf-l-grid__item pf-m-10-col pf-m-1-row\\">
<Route exact={true} path=\\"/\\" component={[Function: Resources]}>
<Resources history={{...}} location={{...}} match={{...}} staticContext={[undefined]}>
<Route exact={true} path=\\"/\\" component={{...}}>
<Memo(wrappedComponent) history={{...}} location={{...}} match={{...}} staticContext={[undefined]}>
<Spinner className=\\"hub-spinner\\">
<span className=\\"pf-c-spinner pf-m-xl hub-spinner\\" role=\\"progressbar\\" aria-valuetext=\\"Loading...\\">
<span className=\\"pf-c-spinner__clipper\\" />
<span className=\\"pf-c-spinner__lead-ball\\" />
<span className=\\"pf-c-spinner__tail-ball\\" />
</span>
</Spinner>
</Resources>
</Memo(wrappedComponent)>
</Route>
</div>
</GridItem>
Expand Down
2 changes: 2 additions & 0 deletions ui/src/containers/App/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Footer from '../../components/Footer';
import Resources from '../Resources';
import Authentication from '../../containers/Authentication';
import Details from '../Details';
import ParseUrl from '../ParseUrl';
import { createProvider } from '../../store/root';
import './App.css';

Expand All @@ -19,6 +20,7 @@ const App: React.FC = observer(() => {
return (
<Provider>
<Router>
<ParseUrl />
<Page header={<Header />} className="hub-page">
<Route exact path="/" component={Background} />
<PageSection>
Expand Down
24 changes: 24 additions & 0 deletions ui/src/containers/ParseUrl/ParseUrl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { when } from 'mobx';
import { FakeHub } from '../../api/testutil';
import { createProviderAndStore } from '../../store/root';

const TESTDATA_DIR = `src/store/testdata`;
const api = new FakeHub(TESTDATA_DIR);
const { root } = createProviderAndStore(api);

describe('ParseUrl component', () => {
it('it can set url params to resource store', (done) => {
const { resources } = root;
when(
() => {
return !resources.isLoading;
},
() => {
resources.setURLParams('?/query=ansible');
expect(resources.urlParams).toBe('?/query=ansible');

done();
}
);
});
});
23 changes: 23 additions & 0 deletions ui/src/containers/ParseUrl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import { useMst } from '../../store/root';
import { Params } from '../../common/params';

const ParseUrl: React.FC = () => {
const { resources } = useMst();

if (window.location.search) {
const searchParams = new URLSearchParams(window.location.search);
if (searchParams.has(Params.Query)) {
resources.setSearch(searchParams.get(Params.Query));
}
if (searchParams.has(Params.SortBY)) {
resources.setSortBy(searchParams.get(Params.SortBY));
}

// Storing url params to store inorder to parse the url only after successfully resource load
resources.setURLParams(window.location.search);
}

return <> </>;
};
export default ParseUrl;
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ exports[`Resource Component should render the resources component 1`] = `
<BrowserRouter>
<Router history={{...}}>
<Resources>
<Memo(wrappedComponent)>
<Gallery hasGutter={true} className=\\"hub-resource\\">
<div className=\\"pf-l-gallery pf-m-gutter hub-resource\\">
<Cards items={{...}}>
Expand Down Expand Up @@ -740,7 +740,7 @@ exports[`Resource Component should render the resources component 1`] = `
</Cards>
</div>
</Gallery>
</Resources>
</Memo(wrappedComponent)>
</Router>
</BrowserRouter>
Expand Down
65 changes: 53 additions & 12 deletions ui/src/containers/Resources/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { useObserver } from 'mobx-react';
import React, { useEffect } from 'react';
import { observer } from 'mobx-react';
import {
EmptyState,
EmptyStateIcon,
Expand All @@ -12,14 +12,57 @@ import {
import CubesIcon from '@patternfly/react-icons/dist/js/icons/cubes-icon';
import { useHistory } from 'react-router-dom';
import { useMst } from '../../store/root';
import { IResource } from '../../store/resource';
import { IResource, SortByFields } from '../../store/resource';
import Cards from '../../components/Cards';
import { Params } from '../../common/params';
import './Resources.css';

const Resources = () => {
const { resources } = useMst();
const Resources: React.FC = observer(() => {
const { resources, categories } = useMst();
const { catalogs, kinds, search, sortBy } = resources;

const history = useHistory();

const updateURL = (
search: string,
sort: string,
categories: string,
kinds: string,
catalogs: string
) => {
const searchParams = new URLSearchParams(window.location.search);
if (!search && searchParams.has(Params.Query)) searchParams.delete(Params.Query);

if (search) searchParams.set(Params.Query, search);

if (sort === SortByFields.Unknown && searchParams.has(Params.SortBY))
searchParams.delete(Params.SortBY);
if (sort !== SortByFields.Unknown) searchParams.set(Params.SortBY, sort);

if (!categories && searchParams.has(Params.Category)) searchParams.delete(Params.Category);

if (categories) searchParams.set(Params.Category, categories);

if (!kinds && searchParams.has(Params.Kind)) searchParams.delete(Params.Kind);

if (kinds) searchParams.set(Params.Kind, kinds);

if (!catalogs && searchParams.has(Params.Catalog)) searchParams.delete(Params.Catalog);

if (catalogs) searchParams.set(Params.Catalog, catalogs);

return searchParams.toString();
};

useEffect(() => {
const selectedcategories = categories.selected.join(',');
const selectedKinds = [...kinds.selected].join(',');
const selectedCatalogs = catalogs.selectedByName.join(',');

const url = updateURL(search, sortBy, selectedcategories, selectedKinds, selectedCatalogs);
history.replace(`?${url}`);
}, [search, sortBy, categories.selected, kinds.selected, catalogs.selected]);

const clearFilter = () => {
resources.clearAllFilters();
history.push('/');
Expand All @@ -43,12 +86,10 @@ const Resources = () => {
);
};

return useObserver(() =>
resources.resources.size === 0 ? (
<Spinner className="hub-spinner" />
) : (
<React.Fragment>{checkResources(resources.filteredResources)}</React.Fragment>
)
return resources.resources.size === 0 ? (
<Spinner className="hub-spinner" />
) : (
<React.Fragment>{checkResources(resources.filteredResources)}</React.Fragment>
);
};
});
export default Resources;
27 changes: 2 additions & 25 deletions ui/src/containers/Search/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React from 'react';
import { useHistory } from 'react-router-dom';
import { useObserver } from 'mobx-react';
import { TextInput } from '@patternfly/react-core';
Expand All @@ -9,35 +9,13 @@ import './Search.css';
const Search: React.FC = () => {
const { resources } = useMst();

// to get query params from the url
const searchParams = new URLSearchParams(window.location.search);
const query = searchParams.get('query') || ' ';

useEffect(() => {
if (query !== ' ') {
resources.setSearch(query);
}
}, [query, resources]);

const setParams = ({ query = '' }) => {
const searchParams = new URLSearchParams();
searchParams.set('query', query);
return searchParams.toString();
};

const updateURL = (text: string) => {
const url = setParams({ query: text });
if (window.location.pathname === '/') history.replace(`?${url}`);
};

const onSearchChange = useDebounce(resources.search, 400);

const history = useHistory();
const onSearchKeyPress = (e: React.KeyboardEvent<HTMLElement>) => {
if (e.key === 'Enter') {
e.preventDefault();
history.push('/');
updateURL(resources.search);
if (window.location.pathname !== '/') history.push('/');
}
return;
};
Expand All @@ -48,7 +26,6 @@ const Search: React.FC = () => {
type="search"
onChange={(resourceName: string) => {
resources.setSearch(resourceName);
updateURL(resourceName);
return onSearchChange;
}}
onKeyPress={onSearchKeyPress}
Expand Down
Loading

0 comments on commit 9125fa3

Please sign in to comment.