ElasticPress
ElasticPress copied to clipboard
Support customizing the template for search results in Instant Results
Is your enhancement related to a problem? Please describe. Currently the output of Instant Results search results is not customizable beyond CSS. Ideally it would be possible for a developer to customize or replace the template to arrange things as desired, or add additional data.
Describe the solution you'd like Some way to replace the template. Possibilities include applying a JavaScript filter to the search results component, or some way to replace the component with a file in the theme. I have not tested how well either would work with React components.
So I've done some discovery on this using the @wordpress/hooks
package and it looks promising.
The hooks package itself is only a 5kb dependency, loaded in the footer, and its only dependency is wp-polyfill
which is already a dependency of several packages used in Instant Results, so I don't think we have a problem with using this package on the front end.
I've also tested and confirmed that it will work well for allowing the replacement of components. For example, we currently display results in the React app like so:
{searchResults.map((hit) => (
<Result key={hit._id} hit={hit} />
))}
We can allow plugin/theme authors to replace the component used to display results by first create a hooks instance that's exposed on the window object:
window.epInstantResults.hooks = createHooks();
And then applying a filter like this:
{searchResults.map((hit, index) =>
window.epInstantResults.hooks.applyFilters(
'elasticpress.InstantResults.Result',
<Result key={hit._id} hit={hit} />,
hit,
index,
),
)}
Although in production we'd probably pass the hooks instance around internally by importing it from config.js
.
Then a user can create their own bog-standard React component and use it for results by adding a filter like so:
const CustomResult = ({ hit }) => {
return (
<article className="ep-search-result">
<h2 className="ep-search-result__title">
<a
href={hit._source.permalink}
dangerouslySetInnerHTML={{ __html: hit._source.post_title }}
/>
</h2>
<p
className="ep-search-result__description"
dangerouslySetInnerHTML={{ __html: hit._source.post_excerpt }}
/>
</article>
);
};
window.epInstantResults.hooks.addFilter('elasticpress.InstantResults.Result', 'namespace', (result, hit) => {
return <CustomResult key={hit._id} hit={hit} />;
});
If they don't have a build process set up they could even use it without JSX, although it could be a bit of a headache:
window.epInstantResults.hooks.addFilter('elasticpress.InstantResults.Result', 'namespace', (result, hit) => {
return wp.element.createElement(
'article',
{
className: 'ep-search-result',
},
[
wp.element.createElement(
'h2',
{
className: 'ep-search-result__title',
},
[
wp.element.createElement('a', {
href: hit._source.permalink,
dangerouslySetInnerHTML: { __html: hit._source.post_title },
}),
],
),
wp.element.createElement('p', {
className: 'ep-search-result__description',
dangerouslySetInnerHTML: { __html: hit._source.post_excerpt },
}),
],
);
});
Before implementing this I'd want to review the structure of Instant Results just to make sure we can apply filters in the right places with the right scope, and think about how attributes will be passed into filterable components. I'm not sure if passing the result as a single hit={hit}
attribute is preferable to {...hit}
for example.
Also, we'd probably want to check how well, if at all, wp-hookdoc
will work for documenting these hooks.
I love this idea. We want to show the post modified date instead of published date, and it's quite rigid.
Is it not possible to have PHP filters for markup/elements in the results? I'm guessing not, since it's pulling from ES, but if there was any way to tweak the template dynamically with PHP, that would be great.
Hi @JiveDig, the reason Instant Results can be so Instant is that it bypasses WordPress and PHP altogether, so unfortunately the components used by Instant Results will only be modifiable via JavaScript.