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

Commit

Permalink
feat(ais-dynamic-widgets): add implementation (#922)
Browse files Browse the repository at this point in the history
* 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 <20689156+shortcuts@users.noreply.github.com>
  • Loading branch information
Haroenv and shortcuts authored Jul 9, 2021
1 parent 730902d commit 41ee905
Show file tree
Hide file tree
Showing 7 changed files with 568 additions and 18 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
},
"dependencies": {
"algoliasearch-helper": "^3.1.0",
"instantsearch.js": "^4.20.0"
"instantsearch.js": "^4.25.0"
},
"peerDependencies": {
"algoliasearch": ">= 3.32.0 < 5",
Expand Down Expand Up @@ -114,11 +114,11 @@
"bundlesize": [
{
"path": "./dist/vue-instantsearch.js",
"maxSize": "53.00 kB"
"maxSize": "54 kB"
},
{
"path": "./dist/vue-instantsearch.common.js",
"maxSize": "16.50 kB"
"maxSize": "16.75 kB"
}
],
"resolutions": {
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ it('should have `name` the same as the suit class name everywhere', () => {
expect(installedName).toBe(name);
if (name === 'AisInstantSearchSsr') {
expect(suitClass).toBe(`ais-InstantSearch`);
} else if (name === 'AisExperimentalDynamicWidgets') {
expect(suitClass).toBe(`ais-DynamicWidgets`);
} else {
expect(suitClass).toBe(`ais-${name.substr(3)}`);
}
Expand Down
87 changes: 87 additions & 0 deletions src/components/DynamicWidgets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { createWidgetMixin } from '../mixins/widget';
import { EXPERIMENTAL_connectDynamicWidgets } from 'instantsearch.js/es/connectors';
import { createSuitMixin } from '../mixins/suit';

function getWidgetAttribute(vnode) {
const props = vnode.componentOptions && vnode.componentOptions.propsData;
if (props) {
if (props.attribute) {
return props.attribute;
}
if (Array.isArray(props.attributes)) {
return props.attributes[0];
}
}

const children =
vnode.componentOptions && vnode.componentOptions.children
? vnode.componentOptions.children
: vnode.children;

if (Array.isArray(children)) {
// return first child with a truthy attribute
return children.reduce(
(acc, curr) => acc || getWidgetAttribute(curr),
undefined
);
}

return undefined;
}

export default {
name: 'AisExperimentalDynamicWidgets',
mixins: [
createWidgetMixin({ connector: EXPERIMENTAL_connectDynamicWidgets }),
createSuitMixin({ name: 'DynamicWidgets' }),
],
props: {
transformItems: {
type: Function,
default: undefined,
},
},
render(createElement) {
const components = new Map();
(this.$slots.default || []).forEach(vnode => {
const attribute = getWidgetAttribute(vnode);
if (attribute) {
components.set(
attribute,
createElement(
'div',
{ key: attribute, class: [this.suit('widget')] },
[vnode]
)
);
}
});

// by default, render everything, but hidden so that the routing doesn't disappear
if (!this.state) {
const allComponents = [];
components.forEach(component => allComponents.push(component));

return createElement(
'div',
{ attrs: { hidden: true }, class: [this.suit()] },
allComponents
);
}

return createElement(
'div',
{ class: [this.suit()] },
this.state.attributesToRender.map(attribute => components.get(attribute))
);
},
computed: {
widgetParams() {
return {
transformItems: this.transformItems,
// we do not pass "widgets" to the connector, since Vue is in charge of rendering
widgets: [],
};
},
},
};
Loading

0 comments on commit 41ee905

Please sign in to comment.