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

Commit 60b595e

Browse files
authored
fix(ssr): forward slots of the AisInstantSearchSsr component (#898)
* fix(ssr): forward slots of the AisInstantSearchSsr component The clone created in findResultsState also manually needs to forward slots, or the data won't be available. * Update src/util/createServerRootMixin.js * chore: also forward root * scoped slots doesn't work, add tests
1 parent 98d7e46 commit 60b595e

File tree

2 files changed

+168
-0
lines changed

2 files changed

+168
-0
lines changed

src/util/__tests__/createServerRootMixin.test.js

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,163 @@ Array [
362362

363363
await renderToString(wrapper);
364364
});
365+
366+
it('forwards slots', async done => {
367+
const searchClient = createFakeClient();
368+
369+
expect.assertions(2);
370+
371+
const App = Vue.component('App', {
372+
mixins: [
373+
forceIsServerMixin,
374+
createServerRootMixin({
375+
searchClient,
376+
indexName: 'hello',
377+
}),
378+
],
379+
render(h) {
380+
return h(InstantSearchSsr, {}, this.$slots.default);
381+
},
382+
serverPrefetch() {
383+
return (
384+
this.instantsearch
385+
.findResultsState(this)
386+
.then(res => {
387+
expect(
388+
this.instantsearch.mainIndex.getWidgets().map(w => w.$$type)
389+
).toEqual(['ais.configure']);
390+
391+
expect(res.hello._state.hitsPerPage).toBe(100);
392+
})
393+
// jest throws an error we need to catch, since stuck in the flow
394+
.catch(e => {
395+
done.fail(e);
396+
})
397+
);
398+
},
399+
});
400+
401+
const wrapper = new Vue({
402+
mixins: [forceIsServerMixin],
403+
render(h) {
404+
return h(App, [
405+
h('template', { slot: 'default' }, [
406+
h(Configure, {
407+
attrs: {
408+
hitsPerPage: 100,
409+
},
410+
}),
411+
]),
412+
]);
413+
},
414+
});
415+
416+
await renderToString(wrapper);
417+
done();
418+
});
419+
420+
// TODO: forwarding of scoped slots doesn't yet work.
421+
it.skip('forwards scoped slots', async done => {
422+
const searchClient = createFakeClient();
423+
424+
expect.assertions(2);
425+
426+
const App = Vue.component('App', {
427+
mixins: [
428+
forceIsServerMixin,
429+
createServerRootMixin({
430+
searchClient,
431+
indexName: 'hello',
432+
}),
433+
],
434+
render(h) {
435+
return h(InstantSearchSsr, {}, [
436+
this.$scopedSlots.default({ test: true }),
437+
]);
438+
},
439+
serverPrefetch() {
440+
return (
441+
this.instantsearch
442+
.findResultsState(this)
443+
.then(res => {
444+
expect(
445+
this.instantsearch.mainIndex.getWidgets().map(w => w.$$type)
446+
).toEqual(['ais.configure']);
447+
448+
expect(res.hello._state.hitsPerPage).toBe(100);
449+
})
450+
// jest throws an error we need to catch, since stuck in the flow
451+
.catch(e => {
452+
done.fail(e);
453+
})
454+
);
455+
},
456+
});
457+
458+
const wrapper = new Vue({
459+
mixins: [forceIsServerMixin],
460+
render(h) {
461+
return h(App, {
462+
scopedSlots: {
463+
default({ test }) {
464+
if (test) {
465+
return h(Configure, {
466+
attrs: {
467+
hitsPerPage: 100,
468+
},
469+
});
470+
}
471+
return null;
472+
},
473+
},
474+
});
475+
},
476+
});
477+
478+
await renderToString(wrapper);
479+
done();
480+
});
481+
482+
it('forwards root', async () => {
483+
const searchClient = createFakeClient();
484+
485+
// there are two renders of App, each with an assertion
486+
expect.assertions(2);
487+
488+
const App = Vue.component('App', {
489+
mixins: [
490+
forceIsServerMixin,
491+
createServerRootMixin({
492+
searchClient,
493+
indexName: 'hello',
494+
}),
495+
],
496+
render(h) {
497+
expect(this.$root).toBe(wrapper);
498+
499+
return h(InstantSearchSsr, {}, [
500+
h(Configure, {
501+
attrs: {
502+
hitsPerPage: 100,
503+
},
504+
}),
505+
h(SearchBox),
506+
]);
507+
},
508+
serverPrefetch() {
509+
return this.instantsearch.findResultsState(this);
510+
},
511+
});
512+
513+
const wrapper = new Vue({
514+
mixins: [forceIsServerMixin],
515+
render(h) {
516+
return h(App);
517+
},
518+
});
519+
520+
await renderToString(wrapper);
521+
});
365522
});
366523

367524
describe('hydrate', () => {

src/util/createServerRootMixin.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ function augmentInstantSearch(instantSearchOptions, searchClient, indexName) {
8888
propsData: componentInstance.$options.propsData,
8989
});
9090

91+
// https://stackoverflow.com/a/48195006/3185307
92+
app.$slots = componentInstance.$slots;
93+
94+
app.$root = componentInstance.$root;
95+
9196
app.$options.serverPrefetch = [];
9297

9398
app.instantsearch.helper = helper;
@@ -160,6 +165,12 @@ function augmentInstantSearch(instantSearchOptions, searchClient, indexName) {
160165

161166
const results = search.__initialSearchResults[parent.getIndexId()];
162167

168+
// this happens when a different InstantSearch gets rendered initially,
169+
// after the hydrate finished. There's thus no initial results available.
170+
if (!results) {
171+
return;
172+
}
173+
163174
const state = results._state;
164175

165176
// helper gets created in init, but that means it doesn't get the injected

0 commit comments

Comments
 (0)