Skip to content
This repository has been archived by the owner on Dec 30, 2022. It is now read-only.

Commit

Permalink
feat(hooks): introduce useSortBy (#3190)
Browse files Browse the repository at this point in the history
* feat(hooks): introduce `useSortBy`

* chore(examples): add useSortBy in hooks example

Co-authored-by: François Chalifour <francoischalifour@users.noreply.github.com>
  • Loading branch information
dhayab and francoischalifour committed Nov 3, 2021
1 parent dd37ab4 commit 5cce33b
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 15 deletions.
28 changes: 28 additions & 0 deletions examples/hooks/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,34 @@ body {
margin: 0 auto;
}

.Container {
display: grid;
align-items: flex-start;
grid-template-columns: 200px 1fr;
gap: .5rem;
}

.Search {
display: grid;
gap: .5rem;
}

.Search-header {
display: grid;
align-items: flex-start;
grid-template-columns: 1fr auto;
gap: .5rem;
}

.Hit-label {
flex: 1;
margin-right: 1rem;
}

.Hit-price {
color: lightslategray;
}

.Pagination {
padding: 1rem 0;
margin: 0 auto;
Expand Down
38 changes: 23 additions & 15 deletions examples/hooks/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
Pagination,
RefinementList,
SearchBox,
SortBy,
} from './components';

import './App.css';
Expand All @@ -21,16 +22,21 @@ const searchClient = algoliasearch(
type HitProps = {
hit: AlgoliaHit<{
name: string;
price: number;
}>;
};

function Hit({ hit }: HitProps) {
return (
<span
dangerouslySetInnerHTML={{
__html: (hit._highlightResult as any).name.value,
}}
/>
<>
<span
className="Hit-label"
dangerouslySetInnerHTML={{
__html: (hit._highlightResult as any).name.value,
}}
/>
<span className="Hit-price">${hit.price}</span>
</>
);
}

Expand All @@ -39,14 +45,7 @@ export function App() {
<InstantSearch searchClient={searchClient} indexName="instant_search">
<Configure hitsPerPage={15} />

<div
style={{
display: 'grid',
alignItems: 'flex-start',
gridTemplateColumns: '200px 1fr',
gap: '0.5rem',
}}
>
<div className="Container">
<div>
<RefinementList
attribute="brand"
Expand All @@ -55,8 +54,17 @@ export function App() {
showMore={true}
/>
</div>
<div style={{ display: 'grid', gap: '.5rem' }}>
<SearchBox placeholder="Search" />
<div className="Search">
<div className="Search-header">
<SearchBox placeholder="Search" />
<SortBy
items={[
{ label: 'Relevance', value: 'instant_search' },
{ label: 'Price (asc)', value: 'instant_search_price_asc' },
{ label: 'Price (desc)', value: 'instant_search_price_desc' },
]}
/>
</div>
<Hits hitComponent={Hit} />
<Pagination className="Pagination" />
</div>
Expand Down
24 changes: 24 additions & 0 deletions examples/hooks/components/SortBy.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

import { useSortBy, UseSortByProps } from 'react-instantsearch-hooks';

export type SortByProps = React.ComponentProps<'div'> & UseSortByProps;
export function SortBy(props: SortByProps) {
const { currentRefinement, options, refine } = useSortBy(props);

return (
<div className="ais-SortBy">
<select
className="ais-SortBy-select"
onChange={(event) => refine(event.target.value)}
defaultValue={currentRefinement}
>
{options.map(({ label, value }) => (
<option className="ais-SortBy-option" key={value} value={value}>
{label}
</option>
))}
</select>
</div>
);
}
1 change: 1 addition & 0 deletions examples/hooks/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './Hits';
export * from './Pagination';
export * from './RefinementList';
export * from './SearchBox';
export * from './SortBy';
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { renderHook } from '@testing-library/react-hooks';

import { createInstantSearchTestWrapper } from '../../../../test/utils';
import { useSortBy } from '../useSortBy';

const items = [
{ label: 'Featured', value: 'indexName' },
{ label: 'Price (asc)', value: 'indexName_price_asc' },
{ label: 'Price (desc)', value: 'indexName_price_desc' },
];

describe('useSortBy', () => {
test('returns the connector render state', async () => {
const wrapper = createInstantSearchTestWrapper();
const { result, waitForNextUpdate } = renderHook(
() => useSortBy({ items }),
{ wrapper }
);

expect(result.current).toEqual({
currentRefinement: 'indexName',
options: items,
refine: expect.any(Function),
hasNoResults: expect.any(Boolean),
});

await waitForNextUpdate();

expect(result.current).toEqual({
currentRefinement: 'indexName',
options: items,
refine: expect.any(Function),
hasNoResults: expect.any(Boolean),
});
});
});
1 change: 1 addition & 0 deletions packages/react-instantsearch-hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './useHits';
export * from './usePagination';
export * from './useRefinementList';
export * from './useSearchBox';
export * from './useSortBy';
17 changes: 17 additions & 0 deletions packages/react-instantsearch-hooks/src/useSortBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import connectSortBy from 'instantsearch.js/es/connectors/sort-by/connectSortBy';

import { useConnector } from './useConnector';

import type {
SortByConnectorParams,
SortByWidgetDescription,
} from 'instantsearch.js/es/connectors/sort-by/connectSortBy';

export type UseSortByProps = SortByConnectorParams;

export function useSortBy(props: UseSortByProps) {
return useConnector<SortByConnectorParams, SortByWidgetDescription>(
connectSortBy,
props
);
}

0 comments on commit 5cce33b

Please sign in to comment.