Skip to content

Commit 86de1e0

Browse files
authoredMar 30, 2021
feat(ts): convert sortBy, connectSortBy (#4700)
* feat(ts): convert sortBy, connectSortBy
1 parent 24d9ded commit 86de1e0

File tree

8 files changed

+421
-388
lines changed

8 files changed

+421
-388
lines changed
 

‎src/connectors/sort-by/__tests__/connectSortBy-test.js ‎src/connectors/sort-by/__tests__/connectSortBy-test.ts

+62-43
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import algoliasearchHelper, {
33
SearchParameters,
44
} from 'algoliasearch-helper';
55

6-
import connectSortBy from '../connectSortBy';
6+
import connectSortBy, { SortByRendererOptions } from '../connectSortBy';
77
import index from '../../../widgets/index/index';
88
import { createSearchClient } from '../../../../test/mock/createSearchClient';
99
import { createInstantSearch } from '../../../../test/mock/createInstantSearch';
@@ -17,6 +17,7 @@ describe('connectSortBy', () => {
1717
describe('Usage', () => {
1818
it('throws without render function', () => {
1919
expect(() => {
20+
// @ts-expect-error
2021
connectSortBy()({});
2122
}).toThrowErrorMatchingInlineSnapshot(`
2223
"The render function is not valid (received type Undefined).
@@ -27,6 +28,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
2728

2829
it('throws without items', () => {
2930
expect(() => {
31+
// @ts-expect-error
3032
connectSortBy(() => {})({ items: undefined });
3133
}).toThrowErrorMatchingInlineSnapshot(`
3234
"The \`items\` option expects an array of objects.
@@ -37,6 +39,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
3739

3840
it('throws with non-array items', () => {
3941
expect(() => {
42+
// @ts-expect-error
4043
connectSortBy(() => {})({ items: 'items' });
4144
}).toThrowErrorMatchingInlineSnapshot(`
4245
"The \`items\` option expects an array of objects.
@@ -80,10 +83,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
8083
];
8184
const widget = makeWidget({ items });
8285

83-
const helper = algoliasearchHelper({}, items[0].value);
86+
const helper = algoliasearchHelper(createSearchClient(), items[0].value);
8487
helper.search = jest.fn();
8588

86-
widget.init(
89+
widget.init!(
8790
createInitOptions({
8891
helper,
8992
state: helper.state,
@@ -105,9 +108,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
105108
true
106109
);
107110

108-
widget.render(
111+
widget.render!(
109112
createRenderOptions({
110-
results: new SearchResults(helper.state, [{}]),
113+
results: new SearchResults(helper.state, [
114+
createSingleSearchResponse(),
115+
]),
111116
state: helper.state,
112117
helper,
113118
})
@@ -136,9 +141,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
136141
{ label: 'Sort products by price', value: 'priceASC' },
137142
];
138143
const widget = makeWidget({ items });
139-
const helper = algoliasearchHelper({}, items[0].value);
144+
const helper = algoliasearchHelper(createSearchClient(), items[0].value);
140145

141-
expect(() => widget.dispose({ helper, state: helper.state })).not.toThrow();
146+
expect(() =>
147+
widget.dispose!({ helper, state: helper.state })
148+
).not.toThrow();
142149
});
143150

144151
it('Renders with transformed items', () => {
@@ -158,10 +165,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
158165
allItems.map(item => ({ ...item, label: 'transformed' })),
159166
});
160167

161-
const helper = algoliasearchHelper({}, items[0].value);
168+
const helper = algoliasearchHelper(createSearchClient(), items[0].value);
162169
helper.search = jest.fn();
163170

164-
widget.init(
171+
widget.init!(
165172
createInitOptions({
166173
helper,
167174
state: helper.state,
@@ -179,9 +186,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
179186
expect.anything()
180187
);
181188

182-
widget.render(
189+
widget.render!(
183190
createRenderOptions({
184-
results: new SearchResults(helper.state, [{}]),
191+
results: new SearchResults(helper.state, [
192+
createSingleSearchResponse(),
193+
]),
185194
helper,
186195
state: helper.state,
187196
instantSearchInstance,
@@ -214,10 +223,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
214223
items,
215224
});
216225

217-
const helper = algoliasearchHelper({}, items[0].value);
226+
const helper = algoliasearchHelper(createSearchClient(), items[0].value);
218227
helper.search = jest.fn();
219228

220-
widget.init(
229+
widget.init!(
221230
createInitOptions({
222231
helper,
223232
state: helper.state,
@@ -237,9 +246,11 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
237246
expect(helper.search).toHaveBeenCalledTimes(1);
238247
}
239248

240-
widget.render(
249+
widget.render!(
241250
createRenderOptions({
242-
results: new SearchResults(helper.state, [{}]),
251+
results: new SearchResults(helper.state, [
252+
createSingleSearchResponse(),
253+
]),
243254
state: helper.state,
244255
helper,
245256
})
@@ -279,9 +290,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
279290
);
280291

281292
const renderState1 = sortBy.getRenderState(
282-
{
283-
sortBy: {},
284-
},
293+
{},
285294
createInitOptions({ helper })
286295
);
287296

@@ -303,11 +312,13 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
303312
},
304313
});
305314

306-
sortBy.init(createInitOptions({ helper }));
307-
sortBy.getWidgetRenderState({ helper }).refine('index_desc');
315+
sortBy.init!(createInitOptions({ helper }));
316+
sortBy
317+
.getWidgetRenderState(createInitOptions({ helper }))
318+
.refine('index_desc');
308319

309320
const renderState2 = sortBy.getRenderState(
310-
{ sortBy: {} },
321+
{},
311322
createRenderOptions({
312323
helper,
313324
state: helper.state,
@@ -382,8 +393,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
382393
},
383394
});
384395

385-
sortBy.init(createInitOptions({ helper }));
386-
sortBy.getWidgetRenderState({ helper }).refine('index_default');
396+
sortBy.init!(createInitOptions({ helper }));
397+
sortBy
398+
.getWidgetRenderState(createInitOptions({ helper }))
399+
.refine('index_default');
387400

388401
const renderState2 = sortBy.getWidgetRenderState(
389402
createRenderOptions({
@@ -429,7 +442,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
429442
const instantSearchInstance = createInstantSearch({
430443
indexName: '',
431444
});
432-
const helper = algoliasearchHelper({}, 'index_featured');
445+
const helper = algoliasearchHelper(
446+
createSearchClient(),
447+
'index_featured'
448+
);
433449
helper.search = jest.fn();
434450

435451
const items = [
@@ -439,7 +455,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
439455
];
440456
const widget = customSortBy({ items });
441457

442-
widget.init(
458+
widget.init!(
443459
createInitOptions({
444460
helper,
445461
state: helper.state,
@@ -459,7 +475,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
459475
const instantSearchInstance = createInstantSearch({
460476
indexName: '',
461477
});
462-
const helper = algoliasearchHelper({}, 'index_initial');
478+
const helper = algoliasearchHelper(
479+
createSearchClient(),
480+
'index_initial'
481+
);
463482
helper.search = jest.fn();
464483

465484
const items = [
@@ -470,7 +489,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
470489
const widget = customSortBy({ items });
471490

472491
expect(() => {
473-
widget.init(
492+
widget.init!(
474493
createInitOptions({
475494
helper,
476495
state: helper.state,
@@ -491,7 +510,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
491510

492511
describe('routing', () => {
493512
const getInitializedWidget = (config = {}) => {
494-
const rendering = jest.fn();
513+
const rendering = jest.fn<any, [SortByRendererOptions, boolean]>();
495514
const makeWidget = connectSortBy(rendering);
496515
const instantSearchInstance = createInstantSearch({
497516
indexName: 'relevance',
@@ -506,10 +525,10 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
506525
...config,
507526
});
508527

509-
const helper = algoliasearchHelper({}, 'relevance');
528+
const helper = algoliasearchHelper(createSearchClient(), 'relevance');
510529
helper.search = jest.fn();
511530

512-
widget.init(
531+
widget.init!(
513532
createInitOptions({
514533
helper,
515534
state: helper.state,
@@ -519,15 +538,15 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
519538

520539
const { refine } = rendering.mock.calls[0][0];
521540

522-
return [widget, helper, refine];
541+
return [widget, helper, refine] as const;
523542
};
524543

525544
describe('getWidgetUiState', () => {
526545
test('should return the same `uiState` when the default value is selected', () => {
527546
const [widget, helper] = getInitializedWidget();
528547

529548
const uiStateBefore = {};
530-
const uiStateAfter = widget.getWidgetUiState(uiStateBefore, {
549+
const uiStateAfter = widget.getWidgetUiState!(uiStateBefore, {
531550
searchParameters: helper.state,
532551
helper,
533552
});
@@ -541,7 +560,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
541560
refine('priceASC');
542561

543562
const uiStateBefore = {};
544-
const uiStateAfter = widget.getWidgetUiState(uiStateBefore, {
563+
const uiStateAfter = widget.getWidgetUiState!(uiStateBefore, {
545564
searchParameters: helper.state,
546565
helper,
547566
});
@@ -556,14 +575,14 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
556575

557576
refine('priceASC');
558577

559-
const uiStateBefore = widget.getWidgetUiState(
578+
const uiStateBefore = widget.getWidgetUiState!(
560579
{},
561580
{
562581
searchParameters: helper.state,
563582
helper,
564583
}
565584
);
566-
const uiStateAfter = widget.getWidgetUiState(uiStateBefore, {
585+
const uiStateAfter = widget.getWidgetUiState!(uiStateBefore, {
567586
searchParameters: helper.state,
568587
helper,
569588
});
@@ -594,15 +613,15 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
594613
// Simulate an URLSync
595614
helper.setQueryParameter('index', 'indexNamePrice');
596615

597-
widget.init(
616+
widget.init!(
598617
createInitOptions({
599618
helper,
600619
state: helper.state,
601620
instantSearchInstance,
602621
})
603622
);
604623

605-
const actual = widget.getWidgetUiState(
624+
const actual = widget.getWidgetUiState!(
606625
{},
607626
{
608627
searchParameters: helper.state,
@@ -639,7 +658,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
639658
);
640659
helper.search = jest.fn();
641660

642-
widget.init(
661+
widget.init!(
643662
createInitOptions({
644663
helper,
645664
state: helper.state,
@@ -648,7 +667,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
648667
})
649668
);
650669

651-
const actual = widget.getWidgetUiState(
670+
const actual = widget.getWidgetUiState!(
652671
{},
653672
{
654673
searchParameters: helper.state,
@@ -666,7 +685,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
666685

667686
const uiState = {};
668687
const searchParametersBefore = SearchParameters.make(helper.state);
669-
const searchParametersAfter = widget.getWidgetSearchParameters(
688+
const searchParametersAfter = widget.getWidgetSearchParameters!(
670689
searchParametersBefore,
671690
{ uiState }
672691
);
@@ -682,7 +701,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
682701
};
683702

684703
const searchParametersBefore = SearchParameters.make(helper.state);
685-
const searchParametersAfter = widget.getWidgetSearchParameters(
704+
const searchParametersAfter = widget.getWidgetSearchParameters!(
686705
searchParametersBefore,
687706
{ uiState }
688707
);
@@ -701,7 +720,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
701720

702721
const uiState = {};
703722
const searchParametersBefore = new SearchParameters(helper.state);
704-
const searchParametersAfter = widget.getWidgetSearchParameters(
723+
const searchParametersAfter = widget.getWidgetSearchParameters!(
705724
searchParametersBefore,
706725
{ uiState }
707726
);
@@ -724,7 +743,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/sort-by/js/
724743
],
725744
});
726745

727-
const actual = widget.getWidgetSearchParameters(
746+
const actual = widget.getWidgetSearchParameters!(
728747
new SearchParameters({
729748
index: 'relevance',
730749
}),

‎src/connectors/sort-by/connectSortBy.js

-195
This file was deleted.
+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
import {
2+
checkRendering,
3+
createDocumentationMessageGenerator,
4+
find,
5+
warning,
6+
noop,
7+
} from '../../lib/utils';
8+
import { Connector, TransformItems } from '../../types';
9+
10+
const withUsage = createDocumentationMessageGenerator({
11+
name: 'sort-by',
12+
connector: true,
13+
});
14+
15+
/**
16+
* The **SortBy** connector provides the logic to build a custom widget that will display a
17+
* list of indices. With Algolia, this is most commonly used for changing ranking strategy. This allows
18+
* a user to change how the hits are being sorted.
19+
*/
20+
21+
export type SortByItem = {
22+
/**
23+
* The name of the index to target.
24+
*/
25+
value: string;
26+
/**
27+
* The label of the index to display.
28+
*/
29+
label: string;
30+
};
31+
32+
export type SortByConnectorParams = {
33+
/**
34+
* Array of objects defining the different indices to choose from.
35+
*/
36+
items: SortByItem[];
37+
/**
38+
* Function to transform the items passed to the templates.
39+
*/
40+
transformItems?: TransformItems<SortByItem>;
41+
};
42+
43+
export type SortByRendererOptions = {
44+
/**
45+
* The initially selected index.
46+
*/
47+
initialIndex?: string;
48+
/**
49+
* The currently selected index.
50+
*/
51+
currentRefinement: string;
52+
/**
53+
* All the available indices
54+
*/
55+
options: SortByItem[];
56+
/**
57+
* Switches indices and triggers a new search.
58+
*/
59+
refine: (value: string) => void;
60+
/**
61+
* `true` if the last search contains no result.
62+
*/
63+
hasNoResults: boolean;
64+
};
65+
66+
export type SortByConnector = Connector<
67+
SortByRendererOptions,
68+
SortByConnectorParams
69+
>;
70+
71+
const connectSortBy: SortByConnector = function connectSortBy(
72+
renderFn,
73+
unmountFn = noop
74+
) {
75+
checkRendering(renderFn, withUsage());
76+
77+
const connectorState: ConnectorState = {};
78+
79+
type ConnectorState = {
80+
setIndex?(indexName: string): void;
81+
initialIndex?: string;
82+
};
83+
84+
return widgetParams => {
85+
const { items, transformItems = (x => x) as TransformItems<SortByItem> } =
86+
widgetParams || {};
87+
88+
if (!Array.isArray(items)) {
89+
throw new Error(
90+
withUsage('The `items` option expects an array of objects.')
91+
);
92+
}
93+
94+
return {
95+
$$type: 'ais.sortBy',
96+
97+
init(initOptions) {
98+
const { instantSearchInstance } = initOptions;
99+
100+
const widgetRenderState = this.getWidgetRenderState(initOptions);
101+
const currentIndex = widgetRenderState.currentRefinement;
102+
const isCurrentIndexInItems = find(
103+
items,
104+
item => item.value === currentIndex
105+
);
106+
107+
warning(
108+
isCurrentIndexInItems !== undefined,
109+
`The index named "${currentIndex}" is not listed in the \`items\` of \`sortBy\`.`
110+
);
111+
112+
renderFn(
113+
{
114+
...widgetRenderState,
115+
instantSearchInstance,
116+
},
117+
true
118+
);
119+
},
120+
121+
render(renderOptions) {
122+
const { instantSearchInstance } = renderOptions;
123+
renderFn(
124+
{
125+
...this.getWidgetRenderState(renderOptions),
126+
instantSearchInstance,
127+
},
128+
false
129+
);
130+
},
131+
132+
dispose({ state }) {
133+
unmountFn();
134+
135+
return connectorState.initialIndex
136+
? state.setIndex(connectorState.initialIndex)
137+
: state;
138+
},
139+
140+
getRenderState(renderState, renderOptions) {
141+
return {
142+
...renderState,
143+
sortBy: this.getWidgetRenderState(renderOptions),
144+
};
145+
},
146+
147+
getWidgetRenderState({ results, helper, parent }) {
148+
if (!connectorState.initialIndex && parent) {
149+
connectorState.initialIndex = parent.getIndexName();
150+
}
151+
if (!connectorState.setIndex) {
152+
connectorState.setIndex = indexName => {
153+
helper.setIndex(indexName).search();
154+
};
155+
}
156+
157+
return {
158+
currentRefinement: helper.state.index,
159+
options: transformItems(items),
160+
refine: connectorState.setIndex,
161+
hasNoResults: results ? results.nbHits === 0 : true,
162+
widgetParams,
163+
};
164+
},
165+
166+
getWidgetUiState(uiState, { searchParameters }) {
167+
const currentIndex = searchParameters.index;
168+
169+
if (currentIndex === connectorState.initialIndex) {
170+
return uiState;
171+
}
172+
173+
return {
174+
...uiState,
175+
sortBy: currentIndex,
176+
};
177+
},
178+
179+
getWidgetSearchParameters(searchParameters, { uiState }) {
180+
return searchParameters.setQueryParameter(
181+
'index',
182+
uiState.sortBy ||
183+
connectorState.initialIndex ||
184+
searchParameters.index
185+
);
186+
},
187+
};
188+
};
189+
};
190+
191+
export default connectSortBy;

‎src/types/widget.ts

+5
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ import {
8787
StatsConnectorParams,
8888
StatsRendererOptions,
8989
} from '../connectors/stats/connectStats';
90+
import {
91+
SortByConnectorParams,
92+
SortByRendererOptions,
93+
} from '../connectors/sort-by/connectSortBy';
9094

9195
export type ScopedResult = {
9296
indexId: string;
@@ -357,6 +361,7 @@ export type IndexRenderState = Partial<{
357361
RelevantSortRendererOptions,
358362
RelevantSortConnectorParams
359363
>;
364+
sortBy: WidgetRenderState<SortByRendererOptions, SortByConnectorParams>;
360365
stats: WidgetRenderState<StatsRendererOptions, StatsConnectorParams>;
361366
}>;
362367

‎src/widgets/sort-by/__tests__/__snapshots__/sort-by-test.js.snap

-29
This file was deleted.

‎src/widgets/sort-by/__tests__/sort-by-test.js ‎src/widgets/sort-by/__tests__/sort-by-test.ts

+33-13
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render } from 'preact';
1+
import { render as preactRender } from 'preact';
22
import algoliasearchHelper from 'algoliasearch-helper';
33
import sortBy from '../sort-by';
44
import { createSearchClient } from '../../../../test/mock/createSearchClient';
@@ -7,18 +7,19 @@ import {
77
createInitOptions,
88
createRenderOptions,
99
} from '../../../../test/mock/createWidget';
10+
import { castToJestMock } from '../../../../test/utils/castToJestMock';
1011

12+
const render = castToJestMock(preactRender);
1113
jest.mock('preact', () => {
1214
const module = jest.requireActual('preact');
13-
1415
module.render = jest.fn();
15-
1616
return module;
1717
});
1818

1919
describe('Usage', () => {
2020
it('throws without container', () => {
2121
expect(() => {
22+
// @ts-expect-error
2223
sortBy({ container: undefined });
2324
}).toThrowErrorMatchingInlineSnapshot(`
2425
"The \`container\` option is required.
@@ -51,7 +52,7 @@ describe('sortBy()', () => {
5152
cssClasses = {
5253
root: ['custom-root', 'cx'],
5354
select: 'custom-select',
54-
item: 'custom-item',
55+
option: 'custom-option',
5556
};
5657
widget = sortBy({ container, items, cssClasses });
5758

@@ -71,11 +72,36 @@ describe('sortBy()', () => {
7172
widget.render(createRenderOptions({ helper, results }));
7273

7374
const [firstRender, secondRender] = render.mock.calls;
75+
// @ts-expect-error
7476
const { children, ...rootProps } = firstRender[0].props;
7577

7678
expect(render).toHaveBeenCalledTimes(2);
77-
expect(rootProps).toMatchSnapshot();
78-
expect(children.props).toMatchSnapshot();
79+
expect(rootProps).toMatchInlineSnapshot(`
80+
Object {
81+
"className": "ais-SortBy custom-root cx",
82+
}
83+
`);
84+
expect(children.props).toMatchInlineSnapshot(`
85+
Object {
86+
"cssClasses": Object {
87+
"option": "ais-SortBy-option custom-option",
88+
"root": "ais-SortBy custom-root cx",
89+
"select": "ais-SortBy-select custom-select",
90+
},
91+
"currentValue": "index-a",
92+
"options": Array [
93+
Object {
94+
"label": "Index A",
95+
"value": "index-a",
96+
},
97+
Object {
98+
"label": "Index B",
99+
"value": "index-b",
100+
},
101+
],
102+
"setValue": [Function],
103+
}
104+
`);
79105
expect(firstRender[1]).toEqual(container);
80106
expect(secondRender[1]).toEqual(container);
81107
});
@@ -93,6 +119,7 @@ describe('sortBy()', () => {
93119

94120
const [firstRender] = render.mock.calls;
95121

122+
// @ts-expect-error
96123
expect(firstRender[0].props.children.props.options).toEqual([
97124
{
98125
label: 'Index A',
@@ -106,11 +133,4 @@ describe('sortBy()', () => {
106133
},
107134
]);
108135
});
109-
110-
it('sets the underlying index', () => {
111-
widget.setIndex('index-b');
112-
113-
expect(helper.setIndex).toHaveBeenCalledTimes(1);
114-
expect(helper.search).toHaveBeenCalledTimes(1);
115-
});
116136
});

‎src/widgets/sort-by/sort-by.js

-108
This file was deleted.

‎src/widgets/sort-by/sort-by.tsx

+130
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/** @jsx h */
2+
3+
import { h, render } from 'preact';
4+
import cx from 'classnames';
5+
import Selector from '../../components/Selector/Selector';
6+
import connectSortBy, {
7+
SortByConnectorParams,
8+
SortByItem,
9+
SortByRendererOptions,
10+
} from '../../connectors/sort-by/connectSortBy';
11+
import {
12+
getContainerNode,
13+
createDocumentationMessageGenerator,
14+
} from '../../lib/utils';
15+
import { component } from '../../lib/suit';
16+
import { Renderer, TransformItems, WidgetFactory } from '../../types';
17+
18+
const withUsage = createDocumentationMessageGenerator({ name: 'sort-by' });
19+
const suit = component('SortBy');
20+
21+
export type SortByWidgetCssClasses = {
22+
/**
23+
* CSS classes added to the outer `<div>`.
24+
*/
25+
root: string | string[];
26+
/**
27+
* CSS classes added to the parent `<select>`.
28+
*/
29+
select: string | string[];
30+
/**
31+
* CSS classes added to each `<option>`.
32+
*/
33+
option: string | string[];
34+
};
35+
36+
export type SortByIndexDefinition = {
37+
/**
38+
* The name of the index to target.
39+
*/
40+
value: string;
41+
/**
42+
* The label of the index to display.
43+
*/
44+
label: string;
45+
};
46+
47+
export type SortByWidgetParams = {
48+
/**
49+
* CSS Selector or HTMLElement to insert the widget.
50+
*/
51+
container: string | HTMLElement;
52+
/**
53+
* Array of objects defining the different indices to choose from.
54+
*/
55+
items: SortByIndexDefinition[];
56+
/**
57+
* CSS classes to be added.
58+
*/
59+
cssClasses?: Partial<SortByWidgetCssClasses>;
60+
/**
61+
* Function to transform the items passed to the templates.
62+
*/
63+
transformItems?: TransformItems<SortByItem>;
64+
};
65+
66+
export type SortByWidget = WidgetFactory<
67+
SortByRendererOptions,
68+
SortByConnectorParams,
69+
SortByWidgetParams
70+
>;
71+
72+
const renderer = ({
73+
containerNode,
74+
cssClasses,
75+
}): Renderer<SortByRendererOptions, SortByWidgetParams> => (
76+
{ currentRefinement, options, refine },
77+
isFirstRendering
78+
) => {
79+
if (isFirstRendering) {
80+
return;
81+
}
82+
83+
render(
84+
<div className={cssClasses.root}>
85+
<Selector
86+
cssClasses={cssClasses}
87+
currentValue={currentRefinement}
88+
options={options}
89+
setValue={refine}
90+
/>
91+
</div>,
92+
containerNode
93+
);
94+
};
95+
96+
/**
97+
* Sort by selector is a widget used for letting the user choose between different
98+
* indices that contains the same data with a different order / ranking formula.
99+
*/
100+
const sortBy: SortByWidget = widgetParams => {
101+
const { container, items, cssClasses: userCssClasses = {}, transformItems } =
102+
widgetParams || {};
103+
if (!container) {
104+
throw new Error(withUsage('The `container` option is required.'));
105+
}
106+
107+
const containerNode = getContainerNode(container);
108+
109+
const cssClasses = {
110+
root: cx(suit(), userCssClasses.root),
111+
select: cx(suit({ descendantName: 'select' }), userCssClasses.select),
112+
option: cx(suit({ descendantName: 'option' }), userCssClasses.option),
113+
};
114+
115+
const specializedRenderer = renderer({
116+
containerNode,
117+
cssClasses,
118+
});
119+
120+
const makeWidget = connectSortBy(specializedRenderer, () =>
121+
render(null, containerNode)
122+
);
123+
124+
return {
125+
...makeWidget({ container: containerNode, items, transformItems }),
126+
$$widgetType: 'ais.sortBy',
127+
};
128+
};
129+
130+
export default sortBy;

0 commit comments

Comments
 (0)
Please sign in to comment.