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

Commit 41ee905

Browse files
Haroenvshortcuts
andauthored
feat(ais-dynamic-widgets): add implementation (#922)
* feat(ais-dynamic-widgets): add implementation Adds a new widget _ais-experimental-dynamic-widgets_ that can be used to conditionally render other widgets. This condition is based on the search results. At the moment there isn't yet a default way to enforce facet ordering in the Algolia engine, thus the data available to `transformItems` is an empty array. This will change once the Algolia engine adds support for facet ordering and this widget will move out of experimental mode. ```vue <template> <ais-experimental-dynamic-widgets :transform-items="transformItems"> <ais-refinement-list attribute="test1" /> <ais-menu attribute="test2" /> <ais-panel> <ais-hierarchical-menu :attributes="hierarchicalAttributes" /> </ais-panel> </ais-experimental-dynamic-widgets> </template> <script> export default { data() { return { hierarchicalAttributes: ["test3", "unused"], }; }, methods: { transformItems(_attributes, { results }) { // add a condition based on the results, eg. if you add the ordering via a query rule: return results.userData[0].facetOrdering; } }, }; </script> ``` See also: algolia/instantsearch#4687 Co-authored-by: Clément Vannicatte <[email protected]>
1 parent 730902d commit 41ee905

File tree

7 files changed

+568
-18
lines changed

7 files changed

+568
-18
lines changed

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
},
4747
"dependencies": {
4848
"algoliasearch-helper": "^3.1.0",
49-
"instantsearch.js": "^4.20.0"
49+
"instantsearch.js": "^4.25.0"
5050
},
5151
"peerDependencies": {
5252
"algoliasearch": ">= 3.32.0 < 5",
@@ -114,11 +114,11 @@
114114
"bundlesize": [
115115
{
116116
"path": "./dist/vue-instantsearch.js",
117-
"maxSize": "53.00 kB"
117+
"maxSize": "54 kB"
118118
},
119119
{
120120
"path": "./dist/vue-instantsearch.common.js",
121-
"maxSize": "16.50 kB"
121+
"maxSize": "16.75 kB"
122122
}
123123
],
124124
"resolutions": {

src/__tests__/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ it('should have `name` the same as the suit class name everywhere', () => {
3434
expect(installedName).toBe(name);
3535
if (name === 'AisInstantSearchSsr') {
3636
expect(suitClass).toBe(`ais-InstantSearch`);
37+
} else if (name === 'AisExperimentalDynamicWidgets') {
38+
expect(suitClass).toBe(`ais-DynamicWidgets`);
3739
} else {
3840
expect(suitClass).toBe(`ais-${name.substr(3)}`);
3941
}

src/components/DynamicWidgets.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { createWidgetMixin } from '../mixins/widget';
2+
import { EXPERIMENTAL_connectDynamicWidgets } from 'instantsearch.js/es/connectors';
3+
import { createSuitMixin } from '../mixins/suit';
4+
5+
function getWidgetAttribute(vnode) {
6+
const props = vnode.componentOptions && vnode.componentOptions.propsData;
7+
if (props) {
8+
if (props.attribute) {
9+
return props.attribute;
10+
}
11+
if (Array.isArray(props.attributes)) {
12+
return props.attributes[0];
13+
}
14+
}
15+
16+
const children =
17+
vnode.componentOptions && vnode.componentOptions.children
18+
? vnode.componentOptions.children
19+
: vnode.children;
20+
21+
if (Array.isArray(children)) {
22+
// return first child with a truthy attribute
23+
return children.reduce(
24+
(acc, curr) => acc || getWidgetAttribute(curr),
25+
undefined
26+
);
27+
}
28+
29+
return undefined;
30+
}
31+
32+
export default {
33+
name: 'AisExperimentalDynamicWidgets',
34+
mixins: [
35+
createWidgetMixin({ connector: EXPERIMENTAL_connectDynamicWidgets }),
36+
createSuitMixin({ name: 'DynamicWidgets' }),
37+
],
38+
props: {
39+
transformItems: {
40+
type: Function,
41+
default: undefined,
42+
},
43+
},
44+
render(createElement) {
45+
const components = new Map();
46+
(this.$slots.default || []).forEach(vnode => {
47+
const attribute = getWidgetAttribute(vnode);
48+
if (attribute) {
49+
components.set(
50+
attribute,
51+
createElement(
52+
'div',
53+
{ key: attribute, class: [this.suit('widget')] },
54+
[vnode]
55+
)
56+
);
57+
}
58+
});
59+
60+
// by default, render everything, but hidden so that the routing doesn't disappear
61+
if (!this.state) {
62+
const allComponents = [];
63+
components.forEach(component => allComponents.push(component));
64+
65+
return createElement(
66+
'div',
67+
{ attrs: { hidden: true }, class: [this.suit()] },
68+
allComponents
69+
);
70+
}
71+
72+
return createElement(
73+
'div',
74+
{ class: [this.suit()] },
75+
this.state.attributesToRender.map(attribute => components.get(attribute))
76+
);
77+
},
78+
computed: {
79+
widgetParams() {
80+
return {
81+
transformItems: this.transformItems,
82+
// we do not pass "widgets" to the connector, since Vue is in charge of rendering
83+
widgets: [],
84+
};
85+
},
86+
},
87+
};

0 commit comments

Comments
 (0)