vue-instantsearch 4.0.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +46 -3
  2. package/package.json +8 -8
  3. package/src/components/DynamicWidgets.js +3 -3
  4. package/src/components/ExperimentalDynamicWidgets.js +10 -0
  5. package/src/components/__tests__/ExperimentalDynamicWidgets.js +41 -0
  6. package/src/instantsearch.umd.js +3 -2
  7. package/src/mixins/widget.js +1 -1
  8. package/src/util/__tests__/createServerRootMixin.test.js +143 -61
  9. package/src/util/createServerRootMixin.js +37 -107
  10. package/src/util/testutils/helper.js +2 -2
  11. package/src/widgets.js +3 -0
  12. package/vue2/cjs/index.js +1 -1
  13. package/vue2/cjs/index.js.map +1 -1
  14. package/vue2/es/package.json.js +1 -1
  15. package/vue2/es/src/components/DynamicWidgets.js +1 -1
  16. package/vue2/es/src/components/DynamicWidgets.js.map +1 -1
  17. package/vue2/es/src/components/ExperimentalDynamicWidgets.js +2 -0
  18. package/vue2/es/src/components/ExperimentalDynamicWidgets.js.map +1 -0
  19. package/vue2/es/src/instantsearch.js +1 -1
  20. package/vue2/es/src/mixins/widget.js +1 -1
  21. package/vue2/es/src/mixins/widget.js.map +1 -1
  22. package/vue2/es/src/util/createServerRootMixin.js +1 -1
  23. package/vue2/es/src/util/createServerRootMixin.js.map +1 -1
  24. package/vue2/es/src/widgets.js +1 -1
  25. package/vue2/umd/index.js +1 -1
  26. package/vue2/umd/index.js.map +1 -1
  27. package/vue3/cjs/index.js +1 -1
  28. package/vue3/cjs/index.js.map +1 -1
  29. package/vue3/es/package.json.js +1 -1
  30. package/vue3/es/src/components/DynamicWidgets.js +1 -1
  31. package/vue3/es/src/components/DynamicWidgets.js.map +1 -1
  32. package/vue3/es/src/components/ExperimentalDynamicWidgets.js +2 -0
  33. package/vue3/es/src/components/ExperimentalDynamicWidgets.js.map +1 -0
  34. package/vue3/es/src/instantsearch.js +1 -1
  35. package/vue3/es/src/mixins/widget.js +1 -1
  36. package/vue3/es/src/mixins/widget.js.map +1 -1
  37. package/vue3/es/src/util/createServerRootMixin.js +1 -1
  38. package/vue3/es/src/util/createServerRootMixin.js.map +1 -1
  39. package/vue3/es/src/widgets.js +1 -1
  40. package/vue3/umd/index.js +1 -1
  41. package/vue3/umd/index.js.map +1 -1
package/CHANGELOG.md CHANGED
@@ -1,11 +1,54 @@
1
- # [4.0.0](https://github.com/algolia/vue-instantsearch/compare/v3.8.1...v4.0.0) (2021-08-23)
1
+ # [4.2.0](https://github.com/algolia/vue-instantsearch/compare/v4.1.1...v4.2.0) (2021-12-13)
2
+
3
+
4
+ ### Features
5
+
6
+ * **dynamicWidgets:** release as production ([#1086](https://github.com/algolia/vue-instantsearch/issues/1086)) ([9e0902b](https://github.com/algolia/vue-instantsearch/commit/9e0902bc7a3afe3eb2211b78e681e65b3677dd78))
7
+ * **instantsearch:** update version ([dbf485f](https://github.com/algolia/vue-instantsearch/commit/dbf485f6263d9821aaf169e682ff1ab94157d15f))
8
+ * **ssr:** prevent initial network request ([#1090](https://github.com/algolia/vue-instantsearch/issues/1090)) ([d97eea2](https://github.com/algolia/vue-instantsearch/commit/d97eea21fbd059b0ca0634a6984a6a8b02acdd3d))
9
+
10
+
11
+
12
+ ## [4.1.1](https://github.com/algolia/vue-instantsearch/compare/v4.1.0...v4.1.1) (2021-10-27)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * **vue3:** disable automatic plugin registration ([#1081](https://github.com/algolia/vue-instantsearch/issues/1081)) ([6077413](https://github.com/algolia/vue-instantsearch/commit/607741312db620242b62a1fa2181563d87e69705))
18
+
19
+
20
+
21
+ # [4.1.0](https://github.com/algolia/vue-instantsearch/compare/v4.0.1...v4.1.0) (2021-10-26)
2
22
 
3
23
 
4
24
  ### Features
5
25
 
6
- * **vue 3:** support vue 3 ([#990](https://github.com/algolia/vue-instantsearch/issues/990))
26
+ * **dependencies:** update downstream ([#1075](https://github.com/algolia/vue-instantsearch/issues/1075)) ([ede11a5](https://github.com/algolia/vue-instantsearch/commit/ede11a5aa907f33123df7f19013680633228d0cf))
27
+
28
+
29
+
30
+ ## [4.0.1](https://github.com/algolia/vue-instantsearch/compare/v4.0.0...v4.0.1) (2021-09-15)
31
+
32
+
33
+ ### Bug Fixes
34
+
35
+ * **dynamic-widgets:** use non-experimental connector ([5629957](https://github.com/algolia/vue-instantsearch/commit/56299575f1fdd1d8cd777358f77543d5b0211b18))
36
+ * **ssr:** pass parent index to render in __forceRender ([25284aa](https://github.com/algolia/vue-instantsearch/commit/25284aafdb95f4684387744953c2a38241da7ad1))
37
+
38
+
39
+
40
+ # [4.0.0](https://github.com/algolia/vue-instantsearch/compare/v3.8.1...v4.0.0) (2021-08-23)
41
+
42
+ Vue InstantSearch now supports Vue 3 ([#990](https://github.com/algolia/vue-instantsearch/issues/990)).
43
+
44
+ It has a few breaking changes that you can easily migrate. You can read [the upgrade guide](https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/vue/#upgrade-from-v3-to-v4).
45
+
46
+ You can try these examples:
7
47
 
8
- Since v4, Vue InstantSearch supports Vue 3. There's a couple of breaking changes that you can easily migrate to the new version. See more here: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/vue/#upgrade-from-v3-to-v4
48
+ * Vue 3 + Vue CLI: [GitHub](https://github.com/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/vue3-vue-cli) | [CodeSandbox](codesandbox.io/s/github/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/vue3-vue-cli)
49
+ * Vue 3 + Vite: [GitHub](https://github.com/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/vue3-vite)
50
+ * Vue 3 + SSR + Vue CLI: [GitHub](https://github.com/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/vue3-ssr-vue-cli)
51
+ * Vue 3 + SSR + Vite: [GitHub](https://github.com/algolia/doc-code-samples/tree/master/Vue%20InstantSearch/vue3-ssr-vite)
9
52
 
10
53
 
11
54
  ## [3.8.1](https://github.com/algolia/vue-instantsearch/compare/v3.8.0...v3.8.1) (2021-07-12)
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "autocomplete"
17
17
  ],
18
18
  "license": "MIT",
19
- "version": "4.0.0",
19
+ "version": "4.2.0",
20
20
  "files": [
21
21
  "vue2",
22
22
  "vue3",
@@ -48,8 +48,7 @@
48
48
  "release": "shipjs prepare"
49
49
  },
50
50
  "dependencies": {
51
- "algoliasearch-helper": "^3.1.0",
52
- "instantsearch.js": "^4.27.2",
51
+ "instantsearch.js": "^4.35.0",
53
52
  "mitt": "^2.1.0"
54
53
  },
55
54
  "peerDependencies": {
@@ -82,6 +81,7 @@
82
81
  "@wdio/spec-reporter": "^5.11.7",
83
82
  "@wdio/static-server-service": "^5.11.0",
84
83
  "algoliasearch": "4.0.1",
84
+ "algoliasearch-helper": "3.7.0",
85
85
  "babel-eslint": "10.0.1",
86
86
  "babel-jest": "23.6.0",
87
87
  "babel-preset-es2015": "6.24.1",
@@ -132,15 +132,15 @@
132
132
  "bundlesize": [
133
133
  {
134
134
  "path": "./vue2/umd/index.js",
135
- "maxSize": "55.00 kB"
135
+ "maxSize": "56.00 kB"
136
136
  },
137
137
  {
138
- "path": "./vue2/cjs/index.js",
139
- "maxSize": "17.00 kB"
138
+ "path": "./vue3/umd/index.js",
139
+ "maxSize": "57.00 kB"
140
140
  },
141
141
  {
142
- "path": "./vue3/umd/index.js",
143
- "maxSize": "56.50 kB"
142
+ "path": "./vue2/cjs/index.js",
143
+ "maxSize": "17.00 kB"
144
144
  },
145
145
  {
146
146
  "path": "./vue3/cjs/index.js",
@@ -1,5 +1,5 @@
1
1
  import { createWidgetMixin } from '../mixins/widget';
2
- import { EXPERIMENTAL_connectDynamicWidgets } from 'instantsearch.js/es/connectors';
2
+ import { connectDynamicWidgets } from 'instantsearch.js/es/connectors';
3
3
  import { createSuitMixin } from '../mixins/suit';
4
4
  import { _objectSpread } from '../util/polyfills';
5
5
  import { isVue3, renderCompat, getDefaultSlot } from '../util/vue-compat';
@@ -40,9 +40,9 @@ function getWidgetAttribute(vnode) {
40
40
  }
41
41
 
42
42
  export default {
43
- name: 'AisExperimentalDynamicWidgets',
43
+ name: 'AisDynamicWidgets',
44
44
  mixins: [
45
- createWidgetMixin({ connector: EXPERIMENTAL_connectDynamicWidgets }),
45
+ createWidgetMixin({ connector: connectDynamicWidgets }),
46
46
  createSuitMixin({ name: 'DynamicWidgets' }),
47
47
  ],
48
48
  props: {
@@ -0,0 +1,10 @@
1
+ import AisDynamicWidgets from './DynamicWidgets';
2
+ import { warn } from '../util/warn';
3
+
4
+ // @MAJOR remove this file
5
+ export default Object.assign({}, AisDynamicWidgets, {
6
+ name: 'AisExperimentalDynamicWidgets',
7
+ mounted() {
8
+ warn('Use AisDynamicWidgets instead of AisExperimentalDynamicWidgets.');
9
+ },
10
+ });
@@ -0,0 +1,41 @@
1
+ import { mount } from '../../../test/utils';
2
+ import ExperimentalDynamicWidgets from '../ExperimentalDynamicWidgets';
3
+ import { warn } from '../../util/warn';
4
+ import { __setState } from '../../mixins/widget';
5
+ import DynamicWidgets from '../DynamicWidgets';
6
+
7
+ jest.mock('../../mixins/widget');
8
+ jest.mock('../../util/warn', () => ({ warn: jest.fn() }));
9
+
10
+ it('warns on mount', () => {
11
+ __setState(null);
12
+
13
+ mount({
14
+ template: '<ExperimentalDynamicWidgets />',
15
+ components: {
16
+ ExperimentalDynamicWidgets,
17
+ },
18
+ });
19
+ expect(warn).toHaveBeenCalledTimes(1);
20
+ expect(warn.mock.calls[0][0]).toMatchInlineSnapshot(
21
+ `"Use AisDynamicWidgets instead of AisExperimentalDynamicWidgets."`
22
+ );
23
+ });
24
+
25
+ it('behaves the same as DynamicWidgets', () => {
26
+ Object.keys(ExperimentalDynamicWidgets).forEach(key => {
27
+ if (key === 'name') {
28
+ // name is different
29
+ expect(ExperimentalDynamicWidgets[key]).toBe(
30
+ 'AisExperimentalDynamicWidgets'
31
+ );
32
+ } else if (key === 'mounted') {
33
+ // mounted has the warning
34
+ expect(ExperimentalDynamicWidgets[key]).toEqual(expect.any(Function));
35
+ } else if (key[0] === '_') {
36
+ // private Vue behavior, not tested
37
+ } else {
38
+ expect(ExperimentalDynamicWidgets[key]).toEqual(DynamicWidgets[key]);
39
+ }
40
+ });
41
+ });
@@ -1,7 +1,8 @@
1
1
  import { plugin } from './plugin';
2
+ import { isVue2 } from './util/vue-compat';
2
3
 
3
- // Automatically register Algolia Search components if Vue is available globally
4
- if (typeof window !== 'undefined' && window.Vue) {
4
+ // Automatically register Algolia Search components if Vue 2.x is available globally.
5
+ if (typeof window !== 'undefined' && window.Vue && isVue2) {
5
6
  window.Vue.use(plugin);
6
7
  }
7
8
 
@@ -31,7 +31,7 @@ export const createWidgetMixin = ({ connector } = {}) => ({
31
31
  this.getParentIndex().addWidgets([this.widget]);
32
32
 
33
33
  if (
34
- this.instantSearchInstance.__initialSearchResults &&
34
+ this.instantSearchInstance._initialResults &&
35
35
  !this.instantSearchInstance.started
36
36
  ) {
37
37
  if (typeof this.instantSearchInstance.__forceRender !== 'function') {
@@ -11,9 +11,9 @@ import { createFakeClient } from '../testutils/client';
11
11
  import { createSerializedState } from '../testutils/helper';
12
12
  import { isVue3, isVue2, Vue2, renderCompat } from '../vue-compat';
13
13
  import {
14
- SearchResults,
15
- SearchParameters,
16
14
  AlgoliaSearchHelper,
15
+ SearchParameters,
16
+ SearchResults,
17
17
  } from 'algoliasearch-helper';
18
18
 
19
19
  jest.unmock('instantsearch.js/es');
@@ -57,9 +57,11 @@ describe('createServerRootMixin', () => {
57
57
  }),
58
58
  ],
59
59
  })
60
- ).toThrowErrorMatchingInlineSnapshot(
61
- `"createServerRootMixin requires \`searchClient\` and \`indexName\` in the first argument"`
62
- );
60
+ ).toThrowErrorMatchingInlineSnapshot(`
61
+ "The \`searchClient\` option is required.
62
+
63
+ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/"
64
+ `);
63
65
  });
64
66
 
65
67
  it('requires indexName', () => {
@@ -72,9 +74,11 @@ describe('createServerRootMixin', () => {
72
74
  }),
73
75
  ],
74
76
  })
75
- ).toThrowErrorMatchingInlineSnapshot(
76
- `"createServerRootMixin requires \`searchClient\` and \`indexName\` in the first argument"`
77
- );
77
+ ).toThrowErrorMatchingInlineSnapshot(`
78
+ "The \`indexName\` option is required.
79
+
80
+ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsearch/js/"
81
+ `);
78
82
  });
79
83
 
80
84
  it('creates an instantsearch instance on "data"', () => {
@@ -301,14 +305,13 @@ Array [
301
305
  renderToString,
302
306
  });
303
307
  expect(state).toEqual({
304
- __identifier: 'stringified',
305
308
  hello: {
306
- _rawResults: [
309
+ results: [
307
310
  {
308
311
  query: '',
309
312
  },
310
313
  ],
311
- _state: {
314
+ state: {
312
315
  disjunctiveFacets: [],
313
316
  disjunctiveFacetsRefinements: {},
314
317
  facets: [],
@@ -534,7 +537,7 @@ Array [
534
537
  this.instantsearch.mainIndex.getWidgets().map(w => w.$$type)
535
538
  ).toEqual(['ais.configure']);
536
539
 
537
- expect(res.hello._state.hitsPerPage).toBe(100);
540
+ expect(res.hello.state.hitsPerPage).toBe(100);
538
541
  })
539
542
  // jest throws an error we need to catch, since stuck in the flow
540
543
  .catch(e => {
@@ -656,11 +659,73 @@ Array [
656
659
 
657
660
  await renderToString(wrapper);
658
661
  });
662
+
663
+ it('searches only once', async () => {
664
+ const searchClient = createFakeClient();
665
+ const app = {
666
+ mixins: [
667
+ forceIsServerMixin,
668
+ createServerRootMixin({
669
+ searchClient,
670
+ indexName: 'hello',
671
+ }),
672
+ ],
673
+ render: renderCompat(h =>
674
+ /**
675
+ * This code triggers this warning in Vue 3:
676
+ * > Non-function value encountered for default slot. Prefer function slots for better performance.
677
+ *
678
+ * To fix it, replace the third argument
679
+ * > [h(...), h(...)]
680
+ * with
681
+ * > { default: () => [h(...), h(...)] }
682
+ *
683
+ * but it's not important (and not compatible in vue2), we're leaving it as-is.
684
+ */
685
+ h(InstantSearchSsr, {}, [
686
+ h(Configure, {
687
+ attrs: {
688
+ hitsPerPage: 100,
689
+ },
690
+ }),
691
+ h(SearchBox),
692
+ ])
693
+ ),
694
+ serverPrefetch() {
695
+ return this.instantsearch.findResultsState({
696
+ component: this,
697
+ renderToString,
698
+ });
699
+ },
700
+ };
701
+
702
+ const wrapper = createSSRApp({
703
+ mixins: [forceIsServerMixin],
704
+ render: renderCompat(h => h(app)),
705
+ });
706
+
707
+ await renderToString(wrapper);
708
+
709
+ expect(searchClient.search).toHaveBeenCalledTimes(1);
710
+ expect(searchClient.search.mock.calls[0][0]).toMatchInlineSnapshot(`
711
+ Array [
712
+ Object {
713
+ "indexName": "hello",
714
+ "params": Object {
715
+ "facets": Array [],
716
+ "hitsPerPage": 100,
717
+ "query": "",
718
+ "tagFilters": "",
719
+ },
720
+ },
721
+ ]
722
+ `);
723
+ });
659
724
  }
660
725
  });
661
726
 
662
727
  describe('hydrate', () => {
663
- it('sets __initialSearchResults', () => {
728
+ it('sets _initialResults', () => {
664
729
  const serialized = createSerializedState();
665
730
 
666
731
  const app = {
@@ -683,7 +748,6 @@ Array [
683
748
  // in test, beforeCreated doesn't have $data yet, but IRL it does
684
749
  created() {
685
750
  this.instantsearch.hydrate({
686
- __identifier: 'stringified',
687
751
  hello: serialized,
688
752
  });
689
753
  },
@@ -693,57 +757,20 @@ Array [
693
757
  vm: { instantsearch },
694
758
  } = mount(app);
695
759
 
696
- expect(instantsearch.__initialSearchResults).toEqual(
697
- expect.objectContaining({ hello: expect.any(SearchResults) })
760
+ expect(instantsearch._initialResults).toEqual(
761
+ expect.objectContaining({
762
+ hello: {
763
+ state: expect.any(Object),
764
+ results: expect.any(Object),
765
+ },
766
+ })
698
767
  );
699
768
 
700
- expect(instantsearch.__initialSearchResults.hello).toEqual(
769
+ expect(instantsearch._initialResults.hello).toEqual(
701
770
  expect.objectContaining(serialized)
702
771
  );
703
772
  });
704
773
 
705
- it('accepts non-stringified results', () => {
706
- const serialized = createSerializedState();
707
- const nonSerialized = new SearchResults(
708
- new SearchParameters(serialized._state),
709
- serialized._rawResults
710
- );
711
-
712
- const app = {
713
- mixins: [
714
- createServerRootMixin({
715
- searchClient: createFakeClient(),
716
- indexName: 'movies',
717
- }),
718
- ],
719
- render: renderCompat(h =>
720
- h(InstantSearchSsr, {}, [
721
- h(Configure, {
722
- attrs: {
723
- hitsPerPage: 100,
724
- },
725
- }),
726
- h(SearchBox),
727
- ])
728
- ),
729
- created() {
730
- this.instantsearch.hydrate({
731
- movies: nonSerialized,
732
- });
733
-
734
- expect(this.instantsearch.__initialSearchResults).toEqual(
735
- expect.objectContaining({ movies: expect.any(SearchResults) })
736
- );
737
-
738
- expect(this.instantsearch.__initialSearchResults.movies).toEqual(
739
- nonSerialized
740
- );
741
- },
742
- };
743
-
744
- mount(app);
745
- });
746
-
747
774
  it('inits the main index', () => {
748
775
  const serialized = createSerializedState();
749
776
 
@@ -773,7 +800,6 @@ Array [
773
800
  expect(instantsearch.mainIndex.getHelper()).toBe(null);
774
801
 
775
802
  instantsearch.hydrate({
776
- __identifier: 'stringified',
777
803
  hello: serialized,
778
804
  });
779
805
 
@@ -812,7 +838,6 @@ Array [
812
838
  expect(instantsearch.mainHelper).toBe(null);
813
839
 
814
840
  instantsearch.hydrate({
815
- __identifier: 'stringified',
816
841
  hello: serialized,
817
842
  });
818
843
 
@@ -834,6 +859,7 @@ Array [
834
859
  created() {
835
860
  instantSearchInstance = this.instantsearch;
836
861
  },
862
+ render() {},
837
863
  });
838
864
 
839
865
  const widget = {
@@ -866,6 +892,7 @@ Array [
866
892
  results: expect.anything(),
867
893
  }),
868
894
  ]),
895
+ parent: expect.anything(),
869
896
  state: expect.anything(),
870
897
  instantSearchInstance: expect.anything(),
871
898
  },
@@ -874,6 +901,7 @@ Object {
874
901
  "createURL": [Function],
875
902
  "helper": Anything,
876
903
  "instantSearchInstance": Anything,
904
+ "parent": Anything,
877
905
  "results": Anything,
878
906
  "scopedResults": ArrayContaining [
879
907
  ObjectContaining {
@@ -892,6 +920,58 @@ Object {
892
920
  );
893
921
  });
894
922
 
923
+ it('uses the results passed to hydrate for rendering', () => {
924
+ let instantSearchInstance;
925
+ mount({
926
+ mixins: [
927
+ createServerRootMixin({
928
+ searchClient: createFakeClient(),
929
+ indexName: 'lol',
930
+ }),
931
+ ],
932
+ created() {
933
+ instantSearchInstance = this.instantsearch;
934
+ },
935
+ render() {},
936
+ });
937
+
938
+ const widget = {
939
+ init: jest.fn(),
940
+ render: jest.fn(),
941
+ };
942
+
943
+ const resultsState = createSerializedState();
944
+ const state = new SearchParameters(resultsState.state);
945
+ const results = new SearchResults(state, resultsState.results);
946
+
947
+ instantSearchInstance.hydrate({
948
+ lol: resultsState,
949
+ });
950
+
951
+ instantSearchInstance.__forceRender(
952
+ widget,
953
+ instantSearchInstance.mainIndex
954
+ );
955
+
956
+ expect(widget.init).toHaveBeenCalledTimes(0);
957
+ expect(widget.render).toHaveBeenCalledTimes(1);
958
+
959
+ const renderArgs = widget.render.mock.calls[0][0];
960
+
961
+ expect(renderArgs).toEqual(
962
+ expect.objectContaining({
963
+ state,
964
+ results,
965
+ scopedResults: [
966
+ expect.objectContaining({
967
+ indexId: 'lol',
968
+ results,
969
+ }),
970
+ ],
971
+ })
972
+ );
973
+ });
974
+
895
975
  describe('createURL', () => {
896
976
  it('returns # if instantsearch has no routing', () => {
897
977
  let instantSearchInstance;
@@ -905,6 +985,7 @@ Object {
905
985
  created() {
906
986
  instantSearchInstance = this.instantsearch;
907
987
  },
988
+ render() {},
908
989
  });
909
990
 
910
991
  const widget = {
@@ -938,6 +1019,7 @@ Object {
938
1019
  created() {
939
1020
  instantSearchInstance = this.instantsearch;
940
1021
  },
1022
+ render() {},
941
1023
  });
942
1024
 
943
1025
  const widget = {