vue-instantsearch 4.8.9 → 4.9.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 (44) hide show
  1. package/package.json +3 -3
  2. package/src/__tests__/common.test.js +143 -1
  3. package/src/components/Hits.vue +2 -0
  4. package/src/components/InfiniteHits.vue +2 -0
  5. package/src/components/InstantSearch.js +7 -0
  6. package/src/components/__tests__/Hits.js +1 -0
  7. package/src/components/__tests__/InfiniteHits.js +1 -0
  8. package/src/util/__tests__/createServerRootMixin.test.js +3 -1
  9. package/src/util/createServerRootMixin.js +6 -44
  10. package/src/util/vue-compat/index-vue3.js +24 -3
  11. package/vue2/cjs/index.js +1 -1
  12. package/vue2/cjs/index.js.map +1 -1
  13. package/vue2/es/package.json.js +1 -1
  14. package/vue2/es/src/components/Hits.vue.js +1 -1
  15. package/vue2/es/src/components/InfiniteHits.vue.js +1 -1
  16. package/vue2/es/src/components/InstantSearch.js +1 -1
  17. package/vue2/es/src/components/InstantSearch.js.map +1 -1
  18. package/vue2/es/src/util/createServerRootMixin.js +1 -1
  19. package/vue2/es/src/util/createServerRootMixin.js.map +1 -1
  20. package/vue2/umd/index.js +1 -1
  21. package/vue2/umd/index.js.map +1 -1
  22. package/vue3/cjs/index.js +1 -1
  23. package/vue3/cjs/index.js.map +1 -1
  24. package/vue3/es/package.json.js +1 -1
  25. package/vue3/es/src/components/Hits.vue.js +1 -1
  26. package/vue3/es/src/components/Hits.vue_vue&type=script&lang.js.map +1 -1
  27. package/vue3/es/src/components/Hits.vue_vue&type=template&id=19b8dc8f&lang.js +2 -0
  28. package/vue3/es/src/components/Hits.vue_vue&type=template&id=19b8dc8f&lang.js.map +1 -0
  29. package/vue3/es/src/components/InfiniteHits.vue.js +1 -1
  30. package/vue3/es/src/components/InfiniteHits.vue_vue&type=script&lang.js.map +1 -1
  31. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=09224cc0&lang.js +2 -0
  32. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=09224cc0&lang.js.map +1 -0
  33. package/vue3/es/src/components/InstantSearch.js +1 -1
  34. package/vue3/es/src/components/InstantSearch.js.map +1 -1
  35. package/vue3/es/src/util/createServerRootMixin.js +1 -1
  36. package/vue3/es/src/util/createServerRootMixin.js.map +1 -1
  37. package/vue3/es/src/util/vue-compat/index-vue3.js +1 -1
  38. package/vue3/es/src/util/vue-compat/index-vue3.js.map +1 -1
  39. package/vue3/umd/index.js +1 -1
  40. package/vue3/umd/index.js.map +1 -1
  41. package/vue3/es/src/components/Hits.vue_vue&type=template&id=5bc4cfef&lang.js +0 -2
  42. package/vue3/es/src/components/Hits.vue_vue&type=template&id=5bc4cfef&lang.js.map +0 -1
  43. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=0e2ba520&lang.js +0 -2
  44. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=0e2ba520&lang.js.map +0 -1
package/package.json CHANGED
@@ -16,7 +16,7 @@
16
16
  "autocomplete"
17
17
  ],
18
18
  "license": "MIT",
19
- "version": "4.8.9",
19
+ "version": "4.9.0",
20
20
  "files": [
21
21
  "vue2",
22
22
  "vue3",
@@ -35,7 +35,7 @@
35
35
  "test:exports:vue3": "node ./test/module/vue3/is-es-module.mjs && node ./test/module/vue3/is-cjs-module.cjs"
36
36
  },
37
37
  "dependencies": {
38
- "instantsearch.js": "4.54.0",
38
+ "instantsearch.js": "4.55.0",
39
39
  "mitt": "^2.1.0"
40
40
  },
41
41
  "peerDependencies": {
@@ -85,5 +85,5 @@
85
85
  "vuex": "3.5.1",
86
86
  "vuex4": "npm:vuex@4.0.0"
87
87
  },
88
- "gitHead": "419aa9869727dceeb49dfc5289a0b9241e0e5827"
88
+ "gitHead": "c483ae31e1e615be61dffb5d0bc04a4927960a67"
89
89
  }
@@ -8,6 +8,7 @@ import {
8
8
  createMenuTests,
9
9
  createPaginationTests,
10
10
  createInfiniteHitsTests,
11
+ createHitsTests,
11
12
  } from '@instantsearch/tests';
12
13
 
13
14
  import { nextTick, mountApp } from '../../test/utils';
@@ -22,6 +23,8 @@ import {
22
23
  AisInfiniteHits,
23
24
  AisSearchBox,
24
25
  createWidgetMixin,
26
+ AisHits,
27
+ AisIndex,
25
28
  } from '../instantsearch';
26
29
  jest.unmock('instantsearch.js/es');
27
30
 
@@ -128,7 +131,146 @@ createInfiniteHitsTests(async ({ instantSearchOptions, widgetParams }) => {
128
131
  render: renderCompat((h) =>
129
132
  h(AisInstantSearch, { props: instantSearchOptions }, [
130
133
  h(AisSearchBox),
131
- h(AisInfiniteHits, { props: widgetParams }),
134
+ h(AisInfiniteHits, {
135
+ attrs: { id: 'main-hits' },
136
+ props: widgetParams,
137
+ scopedSlots: {
138
+ item: ({ item: hit, sendEvent }) =>
139
+ h(
140
+ 'div',
141
+ {
142
+ attrs: {
143
+ 'data-testid': `main-hits-top-level-${hit.__position}`,
144
+ },
145
+ },
146
+ [
147
+ hit.objectID,
148
+ h('button', {
149
+ attrs: {
150
+ 'data-testid': `main-hits-convert-${hit.__position}`,
151
+ },
152
+ on: {
153
+ click: () => sendEvent('conversion', hit, 'Converted'),
154
+ },
155
+ }),
156
+ h('button', {
157
+ attrs: {
158
+ 'data-testid': `main-hits-click-${hit.__position}`,
159
+ },
160
+ on: {
161
+ click: () => sendEvent('click', hit, 'Clicked'),
162
+ },
163
+ }),
164
+ ]
165
+ ),
166
+ },
167
+ }),
168
+ h(AisIndex, { props: { indexName: 'nested' } }, [
169
+ h(AisInfiniteHits, {
170
+ attrs: { id: 'nested-hits' },
171
+ scopedSlots: {
172
+ item: ({ item: hit, sendEvent }) =>
173
+ h(
174
+ 'div',
175
+ {
176
+ attrs: {
177
+ 'data-testid': `nested-hits-top-level-${hit.__position}`,
178
+ },
179
+ },
180
+ [
181
+ hit.objectID,
182
+ h('button', {
183
+ attrs: {
184
+ 'data-testid': `nested-hits-click-${hit.__position}`,
185
+ },
186
+ on: {
187
+ click: () =>
188
+ sendEvent('click', hit, 'Clicked nested'),
189
+ },
190
+ }),
191
+ ]
192
+ ),
193
+ },
194
+ }),
195
+ ]),
196
+ h(GlobalErrorSwallower),
197
+ ])
198
+ ),
199
+ },
200
+ document.body.appendChild(document.createElement('div'))
201
+ );
202
+
203
+ await nextTick();
204
+ });
205
+
206
+ createHitsTests(async ({ instantSearchOptions, widgetParams }) => {
207
+ mountApp(
208
+ {
209
+ render: renderCompat((h) =>
210
+ h(AisInstantSearch, { props: instantSearchOptions }, [
211
+ h(AisSearchBox),
212
+ h(AisHits, {
213
+ attrs: { id: 'main-hits' },
214
+ props: widgetParams,
215
+ scopedSlots: {
216
+ item: ({ item: hit, sendEvent }) =>
217
+ h(
218
+ 'div',
219
+ {
220
+ attrs: {
221
+ 'data-testid': `main-hits-top-level-${hit.__position}`,
222
+ },
223
+ },
224
+ [
225
+ hit.objectID,
226
+ h('button', {
227
+ attrs: {
228
+ 'data-testid': `main-hits-convert-${hit.__position}`,
229
+ },
230
+ on: {
231
+ click: () => sendEvent('conversion', hit, 'Converted'),
232
+ },
233
+ }),
234
+ h('button', {
235
+ attrs: {
236
+ 'data-testid': `main-hits-click-${hit.__position}`,
237
+ },
238
+ on: {
239
+ click: () => sendEvent('click', hit, 'Clicked'),
240
+ },
241
+ }),
242
+ ]
243
+ ),
244
+ },
245
+ }),
246
+ h(AisIndex, { props: { indexName: 'nested' } }, [
247
+ h(AisHits, {
248
+ attrs: { id: 'nested-hits' },
249
+ scopedSlots: {
250
+ item: ({ item: hit, sendEvent }) =>
251
+ h(
252
+ 'div',
253
+ {
254
+ attrs: {
255
+ 'data-testid': `nested-hits-top-level-${hit.__position}`,
256
+ },
257
+ },
258
+ [
259
+ hit.objectID,
260
+ h('button', {
261
+ attrs: {
262
+ 'data-testid': `nested-hits-click-${hit.__position}`,
263
+ },
264
+ on: {
265
+ click: () =>
266
+ sendEvent('click', hit, 'Clicked nested'),
267
+ },
268
+ }),
269
+ ]
270
+ ),
271
+ },
272
+ }),
273
+ ]),
132
274
  h(GlobalErrorSwallower),
133
275
  ])
134
276
  ),
@@ -10,12 +10,14 @@
10
10
  v-for="(item, itemIndex) in items"
11
11
  :key="item.objectID"
12
12
  :class="suit('item')"
13
+ @click="state.sendEvent('click:internal', item, 'Hit Clicked')"
13
14
  >
14
15
  <slot
15
16
  name="item"
16
17
  :item="item"
17
18
  :index="itemIndex"
18
19
  :insights="state.insights"
20
+ :send-event="state.sendEvent"
19
21
  >
20
22
  objectID: {{ item.objectID }}, index: {{ itemIndex }}
21
23
  </slot>
@@ -34,12 +34,14 @@
34
34
  v-for="(item, index) in items"
35
35
  :class="suit('item')"
36
36
  :key="item.objectID"
37
+ @click="state.sendEvent('click:internal', item, 'Hit Clicked')"
37
38
  >
38
39
  <slot
39
40
  name="item"
40
41
  :item="item"
41
42
  :index="index"
42
43
  :insights="state.insights"
44
+ :send-event="state.sendEvent"
43
45
  >
44
46
  objectID: {{ item.objectID }}, index: {{ index }}
45
47
  </slot>
@@ -38,6 +38,12 @@ export default createInstantSearchComponent({
38
38
  return true;
39
39
  },
40
40
  },
41
+ insights: {
42
+ default: undefined,
43
+ validator(value) {
44
+ return typeof value === 'boolean' || typeof value === 'object';
45
+ },
46
+ },
41
47
  stalledSearchDelay: {
42
48
  type: Number,
43
49
  default: undefined,
@@ -84,6 +90,7 @@ export default createInstantSearchComponent({
84
90
  instantSearchInstance: instantsearch({
85
91
  searchClient: this.searchClient,
86
92
  insightsClient: this.insightsClient,
93
+ insights: this.insights,
87
94
  indexName: this.indexName,
88
95
  routing: this.routing,
89
96
  stalledSearchDelay: this.stalledSearchDelay,
@@ -11,6 +11,7 @@ jest.mock('../../mixins/widget');
11
11
 
12
12
  const defaultState = {
13
13
  hits: [{ objectID: 'one' }, { objectID: 'two' }],
14
+ sendEvent: jest.fn(),
14
15
  };
15
16
 
16
17
  it('accepts an escapeHTML prop', () => {
@@ -15,6 +15,7 @@ const defaultState = {
15
15
  escapeHTML: true,
16
16
  transformItems: (items) => items,
17
17
  },
18
+ sendEvent: jest.fn(),
18
19
  hits: [
19
20
  {
20
21
  objectID: '00001',
@@ -1133,7 +1133,9 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsear
1133
1133
 
1134
1134
  const resultsState = createSerializedState();
1135
1135
  const state = new SearchParameters(resultsState.state);
1136
- const localState = new SearchParameters({ index: 'lol' });
1136
+ const localState = new SearchParameters({
1137
+ index: 'lol',
1138
+ });
1137
1139
  const results = new SearchResults(state, resultsState.results);
1138
1140
 
1139
1141
  instantSearchInstance.hydrate({
@@ -1,34 +1,11 @@
1
1
  import instantsearch from 'instantsearch.js/es';
2
+ import {
3
+ waitForResults,
4
+ getInitialResults,
5
+ } from 'instantsearch.js/es/lib/server';
2
6
  import { isVue3, isVue2, Vue2, createSSRApp } from './vue-compat';
3
7
  import { warn } from './warn';
4
8
 
5
- function walkIndex(indexWidget, visit) {
6
- visit(indexWidget);
7
-
8
- return indexWidget.getWidgets().forEach((widget) => {
9
- if (widget.$$type !== 'ais.index') return;
10
- visit(widget);
11
- walkIndex(widget, visit);
12
- });
13
- }
14
-
15
- function searchOnlyWithDerivedHelpers(helper) {
16
- return new Promise((resolve, reject) => {
17
- helper.searchOnlyWithDerivedHelpers();
18
-
19
- // we assume all derived helpers resolve at least in the same tick
20
- helper.derivedHelpers[0].on('result', () => {
21
- resolve();
22
- });
23
-
24
- helper.derivedHelpers.forEach((derivedHelper) =>
25
- derivedHelper.on('error', (e) => {
26
- reject(e);
27
- })
28
- );
29
- });
30
- }
31
-
32
9
  function defaultCloneComponent(componentInstance, { mixins = [] } = {}) {
33
10
  const options = {
34
11
  serverPrefetch: undefined,
@@ -133,24 +110,9 @@ function augmentInstantSearch(instantSearchOptions, cloneComponent) {
133
110
  });
134
111
  })
135
112
  .then(() => renderToString(app))
136
- .then(() => searchOnlyWithDerivedHelpers(instance.mainHelper))
113
+ .then(() => waitForResults(instance))
137
114
  .then(() => {
138
- initialResults = {};
139
- walkIndex(instance.mainIndex, (widget) => {
140
- initialResults[widget.getIndexId()] = {
141
- // copy just the values of SearchParameters, not the functions
142
- state: Object.entries(widget.getHelper().state).reduce(
143
- (acc, [key, value]) => {
144
- // eslint-disable-next-line no-param-reassign
145
- acc[key] = value;
146
- return acc;
147
- },
148
- {}
149
- ),
150
- results: widget.getResults()._rawResults,
151
- };
152
- });
153
-
115
+ initialResults = getInitialResults(instance.mainIndex);
154
116
  search.hydrate(initialResults);
155
117
  return search.getState();
156
118
  });
@@ -9,13 +9,34 @@ export { Vue, Vue2, isVue2, isVue3 };
9
9
 
10
10
  export function renderCompat(fn) {
11
11
  function h(tag, props, children) {
12
- if (typeof props === 'object' && (props.attrs || props.props)) {
12
+ if (
13
+ typeof props === 'object' &&
14
+ (props.attrs || props.props || props.scopedSlots || props.on)
15
+ ) {
13
16
  // In vue 3, we no longer wrap with `attrs` or `props` key.
14
- const flatProps = Object.assign({}, props, props.attrs, props.props);
17
+ const flatProps = Object.assign(
18
+ {},
19
+ props,
20
+ props.attrs,
21
+ props.props,
22
+ Object.keys(props.on || {}).reduce((acc, key) => {
23
+ // eslint-disable-next-line no-param-reassign
24
+ acc[`on${key[0].toUpperCase()}${key.slice(1)}`] = props.on[key];
25
+ return acc;
26
+ }, {})
27
+ );
15
28
  delete flatProps.attrs;
16
29
  delete flatProps.props;
30
+ delete flatProps.scopedSlots;
31
+ delete flatProps.on;
17
32
 
18
- return Vue.h(tag, flatProps, children);
33
+ return Vue.h(
34
+ tag,
35
+ flatProps,
36
+ props.scopedSlots
37
+ ? Object.assign({ default: () => children }, props.scopedSlots)
38
+ : children
39
+ );
19
40
  }
20
41
 
21
42
  return Vue.h(tag, props, children);