vue-instantsearch 4.18.1 → 4.19.1

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 (35) hide show
  1. package/package.json +6 -6
  2. package/src/__tests__/common-widgets.test.js +2 -6
  3. package/src/components/Hits.js +13 -1
  4. package/src/components/InfiniteHits.vue +32 -0
  5. package/src/components/__tests__/Hits.js +45 -0
  6. package/src/components/__tests__/InfiniteHits.js +74 -0
  7. package/src/components/__tests__/__snapshots__/InfiniteHits.js.snap +94 -0
  8. package/src/util/__tests__/createServerRootMixin.test.js +12 -8
  9. package/src/util/__tests__/renderCompat.test.js +52 -0
  10. package/src/util/vue-compat/index-vue2.js +17 -0
  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.js +1 -1
  15. package/vue2/es/src/components/Hits.js.map +1 -1
  16. package/vue2/es/src/components/InfiniteHits.vue.js +1 -1
  17. package/vue2/es/src/components/InfiniteHits.vue_rollup-plugin-vue=script.js +1 -1
  18. package/vue2/es/src/util/vue-compat/index-vue2.js +1 -1
  19. package/vue2/es/src/util/vue-compat/index-vue2.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.js +1 -1
  26. package/vue3/es/src/components/Hits.js.map +1 -1
  27. package/vue3/es/src/components/InfiniteHits.vue.js +1 -1
  28. package/vue3/es/src/components/InfiniteHits.vue_vue&type=script&lang.js +1 -1
  29. package/vue3/es/src/components/InfiniteHits.vue_vue&type=script&lang.js.map +1 -1
  30. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=31c7998c&lang.js +2 -0
  31. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=31c7998c&lang.js.map +1 -0
  32. package/vue3/umd/index.js +1 -1
  33. package/vue3/umd/index.js.map +1 -1
  34. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=1b2a109e&lang.js +0 -2
  35. package/vue3/es/src/components/InfiniteHits.vue_vue&type=template&id=1b2a109e&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.18.1",
19
+ "version": "4.19.1",
20
20
  "files": [
21
21
  "vue2",
22
22
  "vue3",
@@ -36,8 +36,8 @@
36
36
  "watch:es": "yarn --silent build --watch"
37
37
  },
38
38
  "dependencies": {
39
- "instantsearch-ui-components": "0.7.0",
40
- "instantsearch.js": "4.73.1",
39
+ "instantsearch-ui-components": "0.8.0",
40
+ "instantsearch.js": "4.73.3",
41
41
  "mitt": "^2.1.0"
42
42
  },
43
43
  "peerDependencies": {
@@ -62,8 +62,8 @@
62
62
  "@vue/test-utils": "1.3.0",
63
63
  "@vue/test-utils2": "npm:@vue/test-utils@2.0.0-rc.11",
64
64
  "algoliasearch": "4.23.2",
65
- "algoliasearch-helper": "3.22.2",
66
- "instantsearch.css": "8.3.0",
65
+ "algoliasearch-helper": "3.22.3",
66
+ "instantsearch.css": "8.4.0",
67
67
  "rollup": "1.32.1",
68
68
  "rollup-plugin-babel": "4.4.0",
69
69
  "rollup-plugin-buble": "0.19.6",
@@ -89,5 +89,5 @@
89
89
  "vuex": "3.5.1",
90
90
  "vuex4": "npm:vuex@4.0.0"
91
91
  },
92
- "gitHead": "45dae35640d80a10e371990d653be2ec65e35ed8"
92
+ "gitHead": "86b33c675f38136d4ef3479891edc21338614b40"
93
93
  }
@@ -589,12 +589,8 @@ const testOptions = {
589
589
  createBreadcrumbWidgetTests: undefined,
590
590
  createMenuWidgetTests: undefined,
591
591
  createPaginationWidgetTests: undefined,
592
- createInfiniteHitsWidgetTests: {
593
- skippedTests: { 'banner option': true },
594
- },
595
- createHitsWidgetTests: {
596
- skippedTests: { 'banner option': true },
597
- },
592
+ createInfiniteHitsWidgetTests: undefined,
593
+ createHitsWidgetTests: undefined,
598
594
  createRangeInputWidgetTests: undefined,
599
595
  createRatingMenuWidgetTests: undefined,
600
596
  createInstantSearchWidgetTests: undefined,
@@ -19,6 +19,10 @@ export default {
19
19
  createSuitMixin({ name: 'Hits' }),
20
20
  ],
21
21
  props: {
22
+ showBanner: {
23
+ type: Boolean,
24
+ default: true,
25
+ },
22
26
  escapeHTML: {
23
27
  type: Boolean,
24
28
  default: true,
@@ -31,6 +35,7 @@ export default {
31
35
  computed: {
32
36
  widgetParams() {
33
37
  return {
38
+ showBanner: this.showBanner,
34
39
  escapeHTML: this.escapeHTML,
35
40
  transformItems: this.transformItems,
36
41
  };
@@ -43,6 +48,7 @@ export default {
43
48
 
44
49
  const defaultSlot = getScopedSlot(this, 'default');
45
50
  const itemSlot = getScopedSlot(this, 'item');
51
+ const bannerSlot = getScopedSlot(this, 'banner');
46
52
 
47
53
  const itemComponent = ({
48
54
  hit,
@@ -78,7 +84,7 @@ export default {
78
84
 
79
85
  // We only want to render the default slot
80
86
  // if no other slots are defined
81
- if (!itemSlot && defaultSlot) {
87
+ if (!itemSlot && !bannerSlot && defaultSlot) {
82
88
  return h(
83
89
  'div',
84
90
  {
@@ -88,6 +94,7 @@ export default {
88
94
  },
89
95
  [
90
96
  defaultSlot({
97
+ banner: this.state.banner,
91
98
  items: this.state.items,
92
99
  insights: this.state.insights,
93
100
  sendEvent: this.state.sendEvent,
@@ -99,11 +106,16 @@ export default {
99
106
  return h(createHitsComponent({ createElement: h }), {
100
107
  hits: this.state.items,
101
108
  itemComponent,
109
+ banner: this.showBanner ? this.state.banner : undefined,
110
+ bannerComponent: bannerSlot,
102
111
  sendEvent: this.state.sendEvent,
103
112
  classNames: this.classNames && {
104
113
  root: this.classNames['ais-Hits'],
105
114
  list: this.classNames['ais-Hits-list'],
106
115
  item: this.classNames['ais-Hits-item'],
116
+ bannerRoot: this.classNames['ais-Hits-banner'],
117
+ bannerImage: this.classNames['ais-Hits-banner-image'],
118
+ bannerLink: this.classNames['ais-Hits-banner-link'],
107
119
  },
108
120
  });
109
121
  }),
@@ -22,6 +22,7 @@
22
22
  <slot
23
23
  :items="state.items"
24
24
  :results="state.results"
25
+ :banner="state.banner"
25
26
  :is-last-page="state.isLastPage"
26
27
  :refine-previous="refinePrevious"
27
28
  :refine-next="refineNext"
@@ -29,6 +30,32 @@
29
30
  :insights="state.insights"
30
31
  :send-event="state.sendEvent"
31
32
  >
33
+ <template
34
+ v-if="showBanner && state.banner && state.banner.image.urls[0].url"
35
+ >
36
+ <slot name="banner" :banner="state.banner">
37
+ <aside :class="suit('banner')">
38
+ <a
39
+ v-if="state.banner.link"
40
+ :href="state.banner.link.url"
41
+ :target="state.banner.link.target"
42
+ :class="suit('banner-link')"
43
+ >
44
+ <img
45
+ :src="state.banner.image.urls[0].url"
46
+ :alt="state.banner.image.title"
47
+ :class="suit('banner-image')"
48
+ />
49
+ </a>
50
+ <img
51
+ v-else
52
+ :src="state.banner.image.urls[0].url"
53
+ :alt="state.banner.image.title"
54
+ :class="suit('banner-image')"
55
+ />
56
+ </aside>
57
+ </slot>
58
+ </template>
32
59
  <ol :class="suit('list')">
33
60
  <li
34
61
  v-for="(item, index) in state.items"
@@ -91,6 +118,10 @@ export default {
91
118
  createSuitMixin({ name: 'InfiniteHits' }),
92
119
  ],
93
120
  props: {
121
+ showBanner: {
122
+ type: Boolean,
123
+ default: true,
124
+ },
94
125
  showPrevious: {
95
126
  type: Boolean,
96
127
  default: false,
@@ -111,6 +142,7 @@ export default {
111
142
  computed: {
112
143
  widgetParams() {
113
144
  return {
145
+ showBanner: this.showBanner,
114
146
  showPrevious: this.showPrevious,
115
147
  escapeHTML: this.escapeHTML,
116
148
  transformItems: this.transformItems,
@@ -14,6 +14,20 @@ const defaultState = {
14
14
  sendEvent: jest.fn(),
15
15
  };
16
16
 
17
+ it('accepts a showBanner prop', () => {
18
+ __setState({
19
+ ...defaultState,
20
+ });
21
+
22
+ const wrapper = mount(Hits, {
23
+ propsData: {
24
+ showBanner: true,
25
+ },
26
+ });
27
+
28
+ expect(wrapper.vm.widgetParams.showBanner).toBe(true);
29
+ });
30
+
17
31
  it('accepts an escapeHTML prop', () => {
18
32
  __setState({
19
33
  ...defaultState,
@@ -28,6 +42,37 @@ it('accepts an escapeHTML prop', () => {
28
42
  expect(wrapper.vm.widgetParams.escapeHTML).toBe(true);
29
43
  });
30
44
 
45
+ it('exposes banner prop to the banner slot', () => {
46
+ __setState({
47
+ ...defaultState,
48
+ banner: {
49
+ image: {
50
+ urls: [{ url: 'https://via.placeholder.com/550x250' }],
51
+ },
52
+ },
53
+ });
54
+
55
+ const wrapper = mount(
56
+ {
57
+ components: { Hits },
58
+ template: `
59
+ <Hits>
60
+ <template v-slot:banner="{ banner }">
61
+ <img :src=banner.image.urls[0].url />
62
+ </template>
63
+ </Hits>
64
+ `,
65
+ },
66
+ {
67
+ propsData: {
68
+ showBanner: true,
69
+ },
70
+ }
71
+ );
72
+ const img = wrapper.find('img');
73
+ expect(img.attributes('src')).toBe('https://via.placeholder.com/550x250');
74
+ });
75
+
31
76
  it('exposes insights prop to the default slot', async () => {
32
77
  const insights = jest.fn();
33
78
  __setState({
@@ -11,6 +11,7 @@ jest.mock('../../mixins/widget');
11
11
 
12
12
  const defaultState = {
13
13
  widgetParams: {
14
+ showBanner: true,
14
15
  showPrevious: false,
15
16
  escapeHTML: true,
16
17
  transformItems: (items) => items,
@@ -86,6 +87,79 @@ it('renders correctly with a custom item rendering', () => {
86
87
  expect(wrapper.html()).toMatchSnapshot();
87
88
  });
88
89
 
90
+ it('renders correctly with banner data', () => {
91
+ __setState({
92
+ ...defaultState,
93
+ banner: {
94
+ image: {
95
+ urls: [{ url: 'https://via.placeholder.com/550x250' }],
96
+ },
97
+ link: {
98
+ url: 'https://www.algolia.com',
99
+ },
100
+ },
101
+ });
102
+
103
+ const wrapper = mount({
104
+ components: { InfiniteHits },
105
+ template: `<InfiniteHits />`,
106
+ });
107
+
108
+ expect(wrapper.html()).toMatchSnapshot();
109
+ });
110
+
111
+ it('renders correctly with banner data and custom banner rendering', () => {
112
+ __setState({
113
+ ...defaultState,
114
+ banner: {
115
+ image: {
116
+ urls: [{ url: 'https://via.placeholder.com/550x250' }],
117
+ },
118
+ link: {
119
+ url: 'https://www.algolia.com',
120
+ },
121
+ },
122
+ });
123
+
124
+ const wrapper = mount({
125
+ components: { InfiniteHits },
126
+ template: `
127
+ <InfiniteHits>
128
+ <template v-slot:banner="{ banner }">
129
+ <img :src="banner.image.urls[0].url" />
130
+ </template>
131
+ </InfiniteHits>
132
+ `,
133
+ });
134
+
135
+ expect(wrapper.html()).toMatchSnapshot();
136
+ });
137
+
138
+ it('does not render a banner when showBanner is false', () => {
139
+ __setState({
140
+ ...defaultState,
141
+ banner: {
142
+ image: {
143
+ urls: [{ url: 'https://via.placeholder.com/550x250' }],
144
+ },
145
+ link: {
146
+ url: 'https://www.algolia.com',
147
+ },
148
+ },
149
+ widgetParams: {
150
+ ...defaultState.widgetParams,
151
+ showBanner: false,
152
+ },
153
+ });
154
+
155
+ const wrapper = mount({
156
+ components: { InfiniteHits },
157
+ template: `<InfiniteHits />`,
158
+ });
159
+
160
+ expect(wrapper.html()).toMatchSnapshot();
161
+ });
162
+
89
163
  it('exposes insights prop to the default slot', async () => {
90
164
  const insights = jest.fn();
91
165
 
@@ -1,5 +1,39 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`does not render a banner when showBanner is false 1`] = `
4
+ <div class="ais-InfiniteHits">
5
+ <aside class="ais-InfiniteHits-banner">
6
+ <a class="ais-InfiniteHits-banner-link"
7
+ href="https://www.algolia.com"
8
+ >
9
+ <img class="ais-InfiniteHits-banner-image"
10
+ src="https://via.placeholder.com/550x250"
11
+ >
12
+ </a>
13
+ </aside>
14
+ <ol class="ais-InfiniteHits-list">
15
+ <li class="ais-InfiniteHits-item">
16
+ objectID: 00001, index: 0
17
+ </li>
18
+ <li class="ais-InfiniteHits-item">
19
+ objectID: 00002, index: 1
20
+ </li>
21
+ <li class="ais-InfiniteHits-item">
22
+ objectID: 00003, index: 2
23
+ </li>
24
+ <li class="ais-InfiniteHits-item">
25
+ objectID: 00004, index: 3
26
+ </li>
27
+ <li class="ais-InfiniteHits-item">
28
+ objectID: 00005, index: 4
29
+ </li>
30
+ </ol>
31
+ <button class="ais-InfiniteHits-loadMore">
32
+ Show more results
33
+ </button>
34
+ </div>
35
+ `;
36
+
3
37
  exports[`renders correctly with a custom item rendering 1`] = `
4
38
  <div class="ais-InfiniteHits">
5
39
  <ol class="ais-InfiniteHits-list">
@@ -56,3 +90,63 @@ exports[`renders correctly with a custom rendering 1`] = `
56
90
  </div>
57
91
  </div>
58
92
  `;
93
+
94
+ exports[`renders correctly with banner data 1`] = `
95
+ <div class="ais-InfiniteHits">
96
+ <aside class="ais-InfiniteHits-banner">
97
+ <a class="ais-InfiniteHits-banner-link"
98
+ href="https://www.algolia.com"
99
+ >
100
+ <img class="ais-InfiniteHits-banner-image"
101
+ src="https://via.placeholder.com/550x250"
102
+ >
103
+ </a>
104
+ </aside>
105
+ <ol class="ais-InfiniteHits-list">
106
+ <li class="ais-InfiniteHits-item">
107
+ objectID: 00001, index: 0
108
+ </li>
109
+ <li class="ais-InfiniteHits-item">
110
+ objectID: 00002, index: 1
111
+ </li>
112
+ <li class="ais-InfiniteHits-item">
113
+ objectID: 00003, index: 2
114
+ </li>
115
+ <li class="ais-InfiniteHits-item">
116
+ objectID: 00004, index: 3
117
+ </li>
118
+ <li class="ais-InfiniteHits-item">
119
+ objectID: 00005, index: 4
120
+ </li>
121
+ </ol>
122
+ <button class="ais-InfiniteHits-loadMore">
123
+ Show more results
124
+ </button>
125
+ </div>
126
+ `;
127
+
128
+ exports[`renders correctly with banner data and custom banner rendering 1`] = `
129
+ <div class="ais-InfiniteHits">
130
+ <img src="https://via.placeholder.com/550x250">
131
+ <ol class="ais-InfiniteHits-list">
132
+ <li class="ais-InfiniteHits-item">
133
+ objectID: 00001, index: 0
134
+ </li>
135
+ <li class="ais-InfiniteHits-item">
136
+ objectID: 00002, index: 1
137
+ </li>
138
+ <li class="ais-InfiniteHits-item">
139
+ objectID: 00003, index: 2
140
+ </li>
141
+ <li class="ais-InfiniteHits-item">
142
+ objectID: 00004, index: 3
143
+ </li>
144
+ <li class="ais-InfiniteHits-item">
145
+ objectID: 00005, index: 4
146
+ </li>
147
+ </ol>
148
+ <button class="ais-InfiniteHits-loadMore">
149
+ Show more results
150
+ </button>
151
+ </div>
152
+ `;
@@ -315,10 +315,12 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsear
315
315
  });
316
316
 
317
317
  expect(state.hello).toEqual({
318
- requestParams: {
319
- hitsPerPage: 100,
320
- query: '',
321
- },
318
+ requestParams: [
319
+ {
320
+ hitsPerPage: 100,
321
+ query: '',
322
+ },
323
+ ],
322
324
  results: [
323
325
  {
324
326
  query: '',
@@ -342,10 +344,12 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/instantsear
342
344
 
343
345
  // Parent's widgets state should not be merged into nested index state
344
346
  expect(state.nestedIndex).toEqual({
345
- requestParams: {
346
- hitsPerPage: 100,
347
- query: '',
348
- },
347
+ requestParams: [
348
+ {
349
+ hitsPerPage: 100,
350
+ query: '',
351
+ },
352
+ ],
349
353
  results: [
350
354
  {
351
355
  query: '',
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+
5
+ import { mount } from '../../../test/utils';
6
+ import { renderCompat } from '../vue-compat';
7
+
8
+ describe('renderCompat', () => {
9
+ it('should handle function components that return multiple children', () => {
10
+ const wrapper = mount({
11
+ render: renderCompat((h) => {
12
+ function Component({ text }) {
13
+ return h(
14
+ 'div',
15
+ { attrs: { 'data-text': text } },
16
+ h('span', {}, ['1']),
17
+ h('span', {}, '2')
18
+ );
19
+ }
20
+ return h(Component, { text: 'Hello world' });
21
+ }),
22
+ });
23
+
24
+ expect(wrapper.html()).toMatchInlineSnapshot(`
25
+ <div data-text="Hello world">
26
+ <span>
27
+ 1
28
+ </span>
29
+ <span>
30
+ 2
31
+ </span>
32
+ </div>
33
+ `);
34
+ });
35
+
36
+ it('should map props to attrs when using a HTML tag', () => {
37
+ const wrapper = mount({
38
+ render: renderCompat((h) => {
39
+ return h('img', {
40
+ src: 'http://algolia.com/image.png',
41
+ alt: 'Some image',
42
+ });
43
+ }),
44
+ });
45
+
46
+ expect(wrapper.html()).toMatchInlineSnapshot(`
47
+ <img src="http://algolia.com/image.png"
48
+ alt="Some image"
49
+ >
50
+ `);
51
+ });
52
+ });
@@ -21,6 +21,23 @@ const augmentCreateElement =
21
21
  );
22
22
  }
23
23
 
24
+ if (typeof tag === 'string') {
25
+ const { on, style, attrs, domProps, nativeOn, key, ...rest } = props;
26
+ return createElement(
27
+ tag,
28
+ {
29
+ class: className || props.class,
30
+ attrs: attrs || rest,
31
+ on,
32
+ nativeOn,
33
+ style,
34
+ domProps,
35
+ key,
36
+ },
37
+ children
38
+ );
39
+ }
40
+
24
41
  return createElement(
25
42
  tag,
26
43
  Object.assign(props, { class: className || props.class }),