Skip to content

Commit b9c884d

Browse files
authoredMay 3, 2021
feat(ts): migrate toggleRefinement & connectToggleRefinement (#4743)
DX-1707
1 parent 24a889b commit b9c884d

File tree

9 files changed

+627
-522
lines changed

9 files changed

+627
-522
lines changed
 

‎src/components/ToggleRefinement/ToggleRefinement.js

-45
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/** @jsx h */
2+
3+
import { h } from 'preact';
4+
import {
5+
ToggleRefinementRenderState,
6+
ToggleRefinementValue,
7+
} from '../../connectors/toggle-refinement/connectToggleRefinement';
8+
import { PreparedTemplateProps } from '../../lib/utils/prepareTemplateProps';
9+
import {
10+
ToggleRefinementTemplates,
11+
ToggleRefinementComponentCSSClasses,
12+
} from '../../widgets/toggle-refinement/toggle-refinement';
13+
14+
import Template from '../Template/Template';
15+
export type ToggleRefinementProps = {
16+
currentRefinement: ToggleRefinementValue;
17+
refine: ToggleRefinementRenderState['refine'];
18+
cssClasses: ToggleRefinementComponentCSSClasses;
19+
templateProps: PreparedTemplateProps<ToggleRefinementTemplates>;
20+
};
21+
22+
const ToggleRefinement = ({
23+
currentRefinement,
24+
refine,
25+
cssClasses,
26+
templateProps,
27+
}: ToggleRefinementProps) => (
28+
<div className={cssClasses.root}>
29+
<label className={cssClasses.label}>
30+
<input
31+
className={cssClasses.checkbox}
32+
type="checkbox"
33+
checked={currentRefinement.isRefined}
34+
onChange={event =>
35+
refine({ isRefined: !(event.target as HTMLInputElement).checked })
36+
}
37+
/>
38+
39+
<Template
40+
{...templateProps}
41+
rootTagName="span"
42+
rootProps={{ className: cssClasses.labelText }}
43+
templateKey="labelText"
44+
data={currentRefinement}
45+
/>
46+
</label>
47+
</div>
48+
);
49+
50+
export default ToggleRefinement;

‎src/connectors/toggle-refinement/__tests__/connectToggleRefinement-test.js ‎src/connectors/toggle-refinement/__tests__/connectToggleRefinement-test.ts

+248-209
Large diffs are not rendered by default.

‎src/connectors/toggle-refinement/connectToggleRefinement.js ‎src/connectors/toggle-refinement/connectToggleRefinement.ts

+159-112
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import {
2+
AlgoliaSearchHelper,
3+
SearchParameters,
4+
SearchResults,
5+
} from 'algoliasearch-helper';
16
import {
27
checkRendering,
38
escapeRefinement,
@@ -7,6 +12,12 @@ import {
712
noop,
813
toArray,
914
} from '../../lib/utils';
15+
import {
16+
Connector,
17+
CreateURL,
18+
InstantSearch,
19+
WidgetRenderState,
20+
} from '../../types';
1021

1122
const withUsage = createDocumentationMessageGenerator({
1223
name: 'toggle-refinement',
@@ -15,56 +26,118 @@ const withUsage = createDocumentationMessageGenerator({
1526

1627
const $$type = 'ais.toggleRefinement';
1728

18-
const createSendEvent = ({ instantSearchInstance, attribute, on, helper }) => (
19-
...args
20-
) => {
21-
if (args.length === 1) {
22-
instantSearchInstance.sendEventToInsights(args[0]);
23-
return;
24-
}
25-
const [eventType, isRefined, eventName = 'Filter Applied'] = args;
26-
if (eventType !== 'click' || on === undefined) {
27-
return;
28-
}
29-
// Checking
30-
if (!isRefined) {
31-
instantSearchInstance.sendEventToInsights({
32-
insightsMethod: 'clickedFilters',
33-
widgetType: $$type,
34-
eventType,
35-
payload: {
36-
eventName,
37-
index: helper.getIndex(),
38-
filters: on.map(value => `${attribute}:${value}`),
39-
},
40-
attribute,
41-
});
42-
}
29+
type BuiltInSendEventForToggle = (
30+
eventType: string,
31+
isRefined: boolean,
32+
eventName?: string
33+
) => void;
34+
type CustomSendEventForToggle = (customPayload: any) => void;
35+
36+
export type SendEventForToggle = BuiltInSendEventForToggle &
37+
CustomSendEventForToggle;
38+
39+
const createSendEvent = ({
40+
instantSearchInstance,
41+
helper,
42+
attribute,
43+
on,
44+
}: {
45+
instantSearchInstance: InstantSearch;
46+
helper: AlgoliaSearchHelper;
47+
attribute: string;
48+
on: string[] | undefined;
49+
}) => {
50+
const sendEventForToggle: SendEventForToggle = (...args) => {
51+
if (args.length === 1) {
52+
instantSearchInstance.sendEventToInsights(args[0]);
53+
return;
54+
}
55+
const [eventType, isRefined, eventName = 'Filter Applied'] = args;
56+
if (eventType !== 'click' || on === undefined) {
57+
return;
58+
}
59+
60+
// only send an event when the refinement gets applied,
61+
// not when it gets removed
62+
if (!isRefined) {
63+
instantSearchInstance.sendEventToInsights({
64+
insightsMethod: 'clickedFilters',
65+
widgetType: $$type,
66+
eventType,
67+
payload: {
68+
eventName,
69+
index: helper.getIndex(),
70+
filters: on.map(value => `${attribute}:${value}`),
71+
},
72+
attribute,
73+
});
74+
}
75+
};
76+
return sendEventForToggle;
4377
};
4478

45-
/**
46-
* @typedef {Object} ToggleValue
47-
* @property {boolean} isRefined `true` if the toggle is on.
48-
* @property {number} count Number of results matched after applying the toggle refinement.
49-
* @property {Object} onFacetValue Value of the toggle when it's on.
50-
* @property {Object} offFacetValue Value of the toggle when it's off.
51-
*/
79+
export type ToggleRefinementValue = {
80+
/** whether this option is enabled */
81+
isRefined: boolean;
82+
/** number of result if this option is enabled */
83+
count: number | null;
84+
};
5285

53-
/**
54-
* @typedef {Object} CustomToggleWidgetParams
55-
* @property {string} attribute Name of the attribute for faceting (eg. "free_shipping").
56-
* @property {Object} [on = true] Value to filter on when toggled.
57-
* @property {Object} [off] Value to filter on when not toggled.
58-
*/
86+
export type ToggleRefinementConnectorParams = {
87+
/** Name of the attribute for faceting (eg. "free_shipping"). */
88+
attribute: string;
89+
/**
90+
* Value to filter on when toggled.
91+
* @default "true"
92+
*/
93+
on?: string | string[] | boolean | boolean[];
94+
/**
95+
* Value to filter on when not toggled.
96+
*/
97+
off?: string | string[] | boolean | boolean[];
98+
};
5999

60-
/**
61-
* @typedef {Object} ToggleRenderingOptions
62-
* @property {ToggleValue} value The current toggle value.
63-
* @property {function():string} createURL Creates an URL for the next state.
64-
* @property {boolean} canRefine Indicates if search state can be refined.
65-
* @property {function(value)} refine Updates to the next state by applying the toggle refinement.
66-
* @property {Object} widgetParams All original `CustomToggleWidgetParams` forwarded to the `renderFn`.
67-
*/
100+
export type ToggleRefinementRenderState = {
101+
/** The current toggle value */
102+
value: {
103+
name: string;
104+
isRefined: boolean;
105+
count: number | null;
106+
onFacetValue: ToggleRefinementValue;
107+
offFacetValue: ToggleRefinementValue;
108+
};
109+
/** Creates an URL for the next state. */
110+
createURL: CreateURL<string>;
111+
/** send a "facet clicked" insights event */
112+
sendEvent: SendEventForToggle;
113+
/** Indicates if search state can be refined. */
114+
canRefine: boolean;
115+
/** Updates to the next state by applying the toggle refinement. */
116+
refine: (value: { isRefined: boolean }) => void;
117+
};
118+
119+
export type ToggleRefinementWidgetDescription = {
120+
$$type: 'ais.toggleRefinement';
121+
renderState: ToggleRefinementRenderState;
122+
indexRenderState: {
123+
toggleRefinement: {
124+
[attribute: string]: WidgetRenderState<
125+
ToggleRefinementRenderState,
126+
ToggleRefinementConnectorParams
127+
>;
128+
};
129+
};
130+
indexUiState: {
131+
toggle: {
132+
[attribute: string]: boolean;
133+
};
134+
};
135+
};
136+
137+
export type ToggleRefinementConnector = Connector<
138+
ToggleRefinementWidgetDescription,
139+
ToggleRefinementConnectorParams
140+
>;
68141

69142
/**
70143
* **Toggle** connector provides the logic to build a custom widget that will provide
@@ -73,95 +146,66 @@ const createSendEvent = ({ instantSearchInstance, attribute, on, helper }) => (
73146
* Two modes are implemented in the custom widget:
74147
* - with or without the value filtered
75148
* - switch between two values.
76-
*
77-
* @type {Connector}
78-
* @param {function(ToggleRenderingOptions, boolean)} renderFn Rendering function for the custom **Toggle** widget.
79-
* @param {function} unmountFn Unmount function called when the widget is disposed.
80-
* @return {function(CustomToggleWidgetParams)} Re-usable widget factory for a custom **Toggle** widget.
81-
* @example
82-
* // custom `renderFn` to render the custom ClearAll widget
83-
* function renderFn(ToggleRenderingOptions, isFirstRendering) {
84-
* ToggleRenderingOptions.widgetParams.containerNode
85-
* .find('a')
86-
* .off('click');
87-
*
88-
* var buttonHTML = `
89-
* <a href="${ToggleRenderingOptions.createURL()}">
90-
* <input
91-
* type="checkbox"
92-
* value="${ToggleRenderingOptions.value.name}"
93-
* ${ToggleRenderingOptions.value.isRefined ? 'checked' : ''}
94-
* />
95-
* ${ToggleRenderingOptions.value.name} (${ToggleRenderingOptions.value.count})
96-
* </a>
97-
* `;
98-
*
99-
* ToggleRenderingOptions.widgetParams.containerNode.html(buttonHTML);
100-
* ToggleRenderingOptions.widgetParams.containerNode
101-
* .find('a')
102-
* .on('click', function(event) {
103-
* event.preventDefault();
104-
* event.stopPropagation();
105-
*
106-
* ToggleRenderingOptions.refine(ToggleRenderingOptions.value);
107-
* });
108-
* }
109-
*
110-
* // connect `renderFn` to Toggle logic
111-
* var customToggle = instantsearch.connectors.connectToggleRefinement(renderFn);
112-
*
113-
* // mount widget on the page
114-
* search.addWidgets([
115-
* customToggle({
116-
* containerNode: $('#custom-toggle-container'),
117-
* attribute: 'free_shipping',
118-
* })
119-
* ]);
120149
*/
121-
export default function connectToggleRefinement(renderFn, unmountFn = noop) {
150+
const connectToggleRefinement: ToggleRefinementConnector = function connectToggleRefinement(
151+
renderFn,
152+
unmountFn = noop
153+
) {
122154
checkRendering(renderFn, withUsage());
123155

124-
return (widgetParams = {}) => {
125-
const { attribute, on: userOn = true, off: userOff } = widgetParams;
156+
return widgetParams => {
157+
const { attribute, on: userOn = true, off: userOff } = widgetParams || {};
126158

127159
if (!attribute) {
128160
throw new Error(withUsage('The `attribute` option is required.'));
129161
}
130162

131163
const hasAnOffValue = userOff !== undefined;
132-
const hasAnOnValue = userOn !== undefined;
133-
const on = hasAnOnValue ? toArray(userOn).map(escapeRefinement) : undefined;
164+
const on = toArray(userOn).map(escapeRefinement);
134165
const off = hasAnOffValue
135166
? toArray(userOff).map(escapeRefinement)
136167
: undefined;
137168

138-
let sendEvent;
169+
let sendEvent: SendEventForToggle;
139170

140-
const toggleRefinementFactory = helper => ({ isRefined } = {}) => {
141-
// Checking
171+
const toggleRefinementFactory = (helper: AlgoliaSearchHelper) => (
172+
{
173+
isRefined,
174+
}: {
175+
isRefined: boolean;
176+
} = { isRefined: false }
177+
) => {
142178
if (!isRefined) {
143179
sendEvent('click', isRefined);
144180
if (hasAnOffValue) {
145-
off.forEach(v =>
181+
off!.forEach(v =>
146182
helper.removeDisjunctiveFacetRefinement(attribute, v)
147183
);
148184
}
149185

150186
on.forEach(v => helper.addDisjunctiveFacetRefinement(attribute, v));
151187
} else {
152-
// Unchecking
153188
on.forEach(v => helper.removeDisjunctiveFacetRefinement(attribute, v));
154189

155190
if (hasAnOffValue) {
156-
off.forEach(v => helper.addDisjunctiveFacetRefinement(attribute, v));
191+
off!.forEach(v => helper.addDisjunctiveFacetRefinement(attribute, v));
157192
}
158193
}
159194

160195
helper.search();
161196
};
162197

163198
const connectorState = {
164-
createURLFactory: (isRefined, { state, createURL }) => () => {
199+
createURLFactory: (
200+
isRefined: boolean,
201+
{
202+
state,
203+
createURL,
204+
}: {
205+
state: SearchParameters;
206+
createURL(parameters: SearchParameters): string;
207+
}
208+
) => () => {
165209
state = state.resetPage();
166210

167211
const valuesToRemove = isRefined ? on : off;
@@ -233,28 +277,29 @@ export default function connectToggleRefinement(renderFn, unmountFn = noop) {
233277
instantSearchInstance,
234278
}) {
235279
const isRefined = results
236-
? on?.every(v => helper.state.isDisjunctiveFacetRefined(attribute, v))
237-
: on?.every(v => state.isDisjunctiveFacetRefined(attribute, v));
280+
? on.every(v => helper.state.isDisjunctiveFacetRefined(attribute, v))
281+
: on.every(v => state.isDisjunctiveFacetRefined(attribute, v));
238282

239-
let onFacetValue = {
283+
let onFacetValue: ToggleRefinementValue = {
240284
isRefined,
241285
count: 0,
242286
};
243287

244-
let offFacetValue = {
288+
let offFacetValue: ToggleRefinementValue = {
245289
isRefined: hasAnOffValue && !isRefined,
246290
count: 0,
247291
};
248292

249293
if (results) {
250294
const offValue = toArray(off || false);
251-
const allFacetValues = results.getFacetValues(attribute) || [];
295+
const allFacetValues = (results.getFacetValues(attribute, {}) ||
296+
[]) as SearchResults.FacetValue[];
252297

253298
const onData = on
254-
?.map(v =>
299+
.map(v =>
255300
find(allFacetValues, ({ name }) => name === unescapeRefinement(v))
256301
)
257-
.filter(v => v !== undefined);
302+
.filter((v): v is SearchResults.FacetValue => v !== undefined);
258303

259304
const offData = hasAnOffValue
260305
? offValue
@@ -264,7 +309,7 @@ export default function connectToggleRefinement(renderFn, unmountFn = noop) {
264309
({ name }) => name === unescapeRefinement(v)
265310
)
266311
)
267-
.filter(v => v !== undefined)
312+
.filter((v): v is SearchResults.FacetValue => v !== undefined)
268313
: [];
269314

270315
onFacetValue = {
@@ -285,7 +330,7 @@ export default function connectToggleRefinement(renderFn, unmountFn = noop) {
285330
);
286331
}
287332

288-
helper.setPage(helper.state.page);
333+
helper.setPage(helper.state.page!);
289334
}
290335

291336
if (!sendEvent) {
@@ -380,4 +425,6 @@ export default function connectToggleRefinement(renderFn, unmountFn = noop) {
380425
},
381426
};
382427
};
383-
}
428+
};
429+
430+
export default connectToggleRefinement;

‎src/types/render-state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { RelevantSortWidgetDescription } from '../connectors/relevant-sort/conne
2121
import { SearchBoxWidgetDescription } from '../connectors/search-box/connectSearchBox';
2222
import { SortByWidgetDescription } from '../connectors/sort-by/connectSortBy';
2323
import { StatsWidgetDescription } from '../connectors/stats/connectStats';
24-
import { ToggleRefinementWidgetDescription } from '../connectors/toggle-refinement/types';
24+
import { ToggleRefinementWidgetDescription } from '../connectors/toggle-refinement/connectToggleRefinement';
2525
import { VoiceSearchWidgetDescription } from '../connectors/voice-search/connectVoiceSearch';
2626
import { AnalyticsWidgetDescription } from '../widgets/analytics/analytics';
2727
import { PlacesWidgetDescription } from '../widgets/places/places';

‎src/types/ui-state.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { RefinementListWidgetDescription } from '../connectors/refinement-list/c
1313
import { RelevantSortWidgetDescription } from '../connectors/relevant-sort/connectRelevantSort';
1414
import { SearchBoxWidgetDescription } from '../connectors/search-box/connectSearchBox';
1515
import { SortByWidgetDescription } from '../connectors/sort-by/connectSortBy';
16-
import { ToggleRefinementWidgetDescription } from '../connectors/toggle-refinement/types';
16+
import { ToggleRefinementWidgetDescription } from '../connectors/toggle-refinement/connectToggleRefinement';
1717
import { VoiceSearchWidgetDescription } from '../connectors/voice-search/connectVoiceSearch';
1818
import { PlacesWidgetDescription } from '../widgets/places/places';
1919

‎src/widgets/toggle-refinement/__tests__/__snapshots__/toggle-refinement-test.js.snap

-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
exports[`toggleRefinement() Lifecycle render supports negative numeric off or on values 1`] = `
44
Object {
5-
"createURL": [Function],
65
"cssClasses": Object {
76
"checkbox": "ais-ToggleRefinement-checkbox",
87
"label": "ais-ToggleRefinement-label",
@@ -37,7 +36,6 @@ Object {
3736

3837
exports[`toggleRefinement() Lifecycle render supports negative numeric off or on values 2`] = `
3938
Object {
40-
"createURL": [Function],
4139
"cssClasses": Object {
4240
"checkbox": "ais-ToggleRefinement-checkbox",
4341
"label": "ais-ToggleRefinement-label",
@@ -72,7 +70,6 @@ Object {
7270

7371
exports[`toggleRefinement() Lifecycle render understands cssClasses 1`] = `
7472
Object {
75-
"createURL": [Function],
7673
"cssClasses": Object {
7774
"checkbox": "ais-ToggleRefinement-checkbox test-checkbox",
7875
"label": "ais-ToggleRefinement-label test-label",
@@ -107,7 +104,6 @@ Object {
107104

108105
exports[`toggleRefinement() Lifecycle render when refined 1`] = `
109106
Object {
110-
"createURL": [Function],
111107
"cssClasses": Object {
112108
"checkbox": "ais-ToggleRefinement-checkbox",
113109
"label": "ais-ToggleRefinement-label",
@@ -142,7 +138,6 @@ Object {
142138

143139
exports[`toggleRefinement() Lifecycle render when refined 2`] = `
144140
Object {
145-
"createURL": [Function],
146141
"cssClasses": Object {
147142
"checkbox": "ais-ToggleRefinement-checkbox",
148143
"label": "ais-ToggleRefinement-label",
@@ -177,7 +172,6 @@ Object {
177172

178173
exports[`toggleRefinement() Lifecycle render with facet values 1`] = `
179174
Object {
180-
"createURL": [Function],
181175
"cssClasses": Object {
182176
"checkbox": "ais-ToggleRefinement-checkbox",
183177
"label": "ais-ToggleRefinement-label",
@@ -212,7 +206,6 @@ Object {
212206

213207
exports[`toggleRefinement() Lifecycle render with facet values 2`] = `
214208
Object {
215-
"createURL": [Function],
216209
"cssClasses": Object {
217210
"checkbox": "ais-ToggleRefinement-checkbox",
218211
"label": "ais-ToggleRefinement-label",
@@ -247,7 +240,6 @@ Object {
247240

248241
exports[`toggleRefinement() Lifecycle render without facet values 1`] = `
249242
Object {
250-
"createURL": [Function],
251243
"cssClasses": Object {
252244
"checkbox": "ais-ToggleRefinement-checkbox",
253245
"label": "ais-ToggleRefinement-label",
@@ -282,7 +274,6 @@ Object {
282274

283275
exports[`toggleRefinement() Lifecycle render without facet values 2`] = `
284276
Object {
285-
"createURL": [Function],
286277
"cssClasses": Object {
287278
"checkbox": "ais-ToggleRefinement-checkbox",
288279
"label": "ais-ToggleRefinement-label",

‎src/widgets/toggle-refinement/toggle-refinement.js

-145
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/** @jsx h */
2+
3+
import { h, render } from 'preact';
4+
import cx from 'classnames';
5+
import ToggleRefinement from '../../components/ToggleRefinement/ToggleRefinement';
6+
import connectToggleRefinement, {
7+
ToggleRefinementConnectorParams,
8+
ToggleRefinementWidgetDescription,
9+
ToggleRefinementValue,
10+
ToggleRefinementRenderState,
11+
} from '../../connectors/toggle-refinement/connectToggleRefinement';
12+
import defaultTemplates from './defaultTemplates';
13+
import {
14+
getContainerNode,
15+
prepareTemplateProps,
16+
createDocumentationMessageGenerator,
17+
} from '../../lib/utils';
18+
import { RendererOptions, Template, WidgetFactory } from '../../types';
19+
import { component } from '../../lib/suit';
20+
import { PreparedTemplateProps } from '../../lib/utils/prepareTemplateProps';
21+
22+
const withUsage = createDocumentationMessageGenerator({
23+
name: 'toggle-refinement',
24+
});
25+
const suit = component('ToggleRefinement');
26+
27+
const renderer = ({
28+
containerNode,
29+
cssClasses,
30+
renderState,
31+
templates,
32+
}: {
33+
containerNode: HTMLElement;
34+
cssClasses: ToggleRefinementComponentCSSClasses;
35+
renderState: {
36+
templateProps?: PreparedTemplateProps<ToggleRefinementTemplates>;
37+
};
38+
templates: Partial<ToggleRefinementTemplates>;
39+
}) => (
40+
{
41+
value,
42+
refine,
43+
instantSearchInstance,
44+
}: ToggleRefinementRenderState &
45+
RendererOptions<ToggleRefinementConnectorParams>,
46+
isFirstRendering: boolean
47+
) => {
48+
if (isFirstRendering) {
49+
renderState.templateProps = prepareTemplateProps({
50+
defaultTemplates,
51+
templatesConfig: instantSearchInstance.templatesConfig,
52+
templates,
53+
});
54+
55+
return;
56+
}
57+
58+
render(
59+
<ToggleRefinement
60+
cssClasses={cssClasses}
61+
currentRefinement={value}
62+
templateProps={renderState.templateProps!}
63+
refine={refine}
64+
/>,
65+
containerNode
66+
);
67+
};
68+
69+
export type ToggleRefinementCSSClasses = {
70+
/** CSS class to add to the root element. */
71+
root: string | string[];
72+
/** CSS class to add to the label wrapping element. */
73+
label: string | string[];
74+
/** CSS class to add to the checkbox. */
75+
checkbox: string | string[];
76+
/** CSS class to add to the label text. */
77+
labelText: string | string[];
78+
};
79+
80+
export type ToggleRefinementComponentCSSClasses = {
81+
[key in keyof ToggleRefinementCSSClasses]: string;
82+
};
83+
84+
export type ToggleRefinementTemplates = {
85+
/** the text that describes the toggle action */
86+
labelText: Template<ToggleRefinementValue>;
87+
};
88+
89+
export type ToggleRefinementWidgetParams = {
90+
/**
91+
* CSS Selector or HTMLElement to insert the widget.
92+
*/
93+
container: string | HTMLElement;
94+
95+
/**
96+
* Templates to use for the widget.
97+
*/
98+
templates?: Partial<ToggleRefinementTemplates>;
99+
100+
/**
101+
* CSS classes to be added.
102+
*/
103+
cssClasses?: Partial<ToggleRefinementCSSClasses>;
104+
};
105+
106+
export type ToggleRefinementWidget = WidgetFactory<
107+
ToggleRefinementWidgetDescription & {
108+
$$widgetType: 'ais.toggleRefinement';
109+
},
110+
ToggleRefinementConnectorParams,
111+
ToggleRefinementWidgetParams
112+
>;
113+
114+
/**
115+
* The toggleRefinement widget lets the user either:
116+
* - switch between two values for a single facetted attribute (free_shipping / not_free_shipping)
117+
* - toggleRefinement a faceted value on and off (only 'canon' for brands)
118+
*
119+
* This widget is particularly useful if you have a boolean value in the records.
120+
*
121+
* @requirements
122+
* The attribute passed to `attribute` must be declared as an
123+
* [attribute for faceting](https://www.algolia.com/doc/guides/searching/faceting/#declaring-attributes-for-faceting)
124+
* in your Algolia settings.
125+
*/
126+
const toggleRefinement = function toggleRefinement(widgetParams) {
127+
const {
128+
container,
129+
attribute,
130+
cssClasses: userCssClasses = {},
131+
templates = defaultTemplates,
132+
on = true,
133+
off,
134+
} = widgetParams || {};
135+
if (!container) {
136+
throw new Error(withUsage('The `container` option is required.'));
137+
}
138+
139+
const containerNode = getContainerNode(container);
140+
141+
const cssClasses = {
142+
root: cx(suit(), userCssClasses.root),
143+
label: cx(suit({ descendantName: 'label' }), userCssClasses.label),
144+
checkbox: cx(suit({ descendantName: 'checkbox' }), userCssClasses.checkbox),
145+
labelText: cx(
146+
suit({ descendantName: 'labelText' }),
147+
userCssClasses.labelText
148+
),
149+
};
150+
151+
const specializedRenderer = renderer({
152+
containerNode,
153+
cssClasses,
154+
renderState: {},
155+
templates,
156+
});
157+
158+
const makeWidget = connectToggleRefinement(specializedRenderer, () =>
159+
render(null, containerNode)
160+
);
161+
162+
return {
163+
...makeWidget({ attribute, on, off }),
164+
$$widgetType: 'ais.toggleRefinement',
165+
};
166+
};
167+
168+
export default toggleRefinement;

0 commit comments

Comments
 (0)
Please sign in to comment.