diff --git a/packages/instantsearch.js/.storybook/playgrounds/default.ts b/packages/instantsearch.js/.storybook/playgrounds/default.ts index 6f653c4542..e1b1ce8dfd 100644 --- a/packages/instantsearch.js/.storybook/playgrounds/default.ts +++ b/packages/instantsearch.js/.storybook/playgrounds/default.ts @@ -1,23 +1,22 @@ +import { HitsTemplates } from '../../src/widgets/hits/hits'; import { Playground } from '../decorators'; -export const hitsItemTemplate = ` -
-
-
- {{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}} -
-

- {{#helpers.snippet}}{ "attribute": "description" }{{/helpers.snippet}} -

- -
+export const hitsItemTemplate: HitsTemplates['item'] = ( + hit, + { html, components } +) => html` +
+
+
+ ${components.Highlight({ hit, attribute: 'name' })} +
+

${components.Snippet({ hit, attribute: 'description' })}

+ +
`; const instantSearchPlayground: Playground = function instantSearchPlayground({ @@ -33,7 +32,7 @@ const instantSearchPlayground: Playground = function instantSearchPlayground({ typeof instantsearch.widgets.refinementList >({ templates: { - header: 'Brands', + header: () => 'Brands', }, })(instantsearch.widgets.refinementList); @@ -49,7 +48,9 @@ const instantSearchPlayground: Playground = function instantSearchPlayground({ const priceMenu = instantsearch.widgets.panel< typeof instantsearch.widgets.numericMenu - >({ templates: { header: 'Price' } })(instantsearch.widgets.numericMenu); + >({ templates: { header: () => 'Price' } })( + instantsearch.widgets.numericMenu + ); search.addWidgets([ priceMenu({ @@ -72,7 +73,7 @@ const instantSearchPlayground: Playground = function instantSearchPlayground({ typeof instantsearch.widgets.ratingMenu >({ templates: { - header: 'Rating', + header: () => 'Rating', }, })(instantsearch.widgets.ratingMenu); @@ -127,6 +128,14 @@ const instantSearchPlayground: Playground = function instantSearchPlayground({ instantsearch.widgets.pagination({ container: pagination, }), + instantsearch.widgets.hitsPerPage({ + container: rightPanel.appendChild(document.createElement('div')), + items: [ + { label: '16 per page', value: 16 }, + { label: '32 per page', value: 32 }, + { label: '64 per page', value: 64, default: true }, + ], + }), ]); const insights = instantsearch.middlewares.createInsightsMiddleware({ diff --git a/packages/instantsearch.js/.storybook/playgrounds/movies.ts b/packages/instantsearch.js/.storybook/playgrounds/movies.ts index 6bd0301c3b..2bd3f42e6b 100644 --- a/packages/instantsearch.js/.storybook/playgrounds/movies.ts +++ b/packages/instantsearch.js/.storybook/playgrounds/movies.ts @@ -13,7 +13,7 @@ const demoQueryRulesPlayground: Playground = function demoQueryRulesPlayground({ typeof instantsearch.widgets.refinementList >({ templates: { - header: 'Genres', + header: () => 'Genres', }, })(instantsearch.widgets.refinementList); @@ -53,16 +53,18 @@ const demoQueryRulesPlayground: Playground = function demoQueryRulesPlayground({ instantsearch.widgets.hits({ container: hits, templates: { - item: ` -
-
-
- {{#helpers.highlight}}{ "attribute": "title" }{{/helpers.highlight}} -
-
+ item: (hit, { html, components }) => html` +
+
+
+ ${components.Highlight({ attribute: 'title', hit })} +
+
`, }, cssClasses: { diff --git a/packages/instantsearch.js/src/components/Pagination/Pagination.tsx b/packages/instantsearch.js/src/components/Pagination/Pagination.tsx index ac11b74924..d98c9cbebd 100644 --- a/packages/instantsearch.js/src/components/Pagination/Pagination.tsx +++ b/packages/instantsearch.js/src/components/Pagination/Pagination.tsx @@ -4,6 +4,7 @@ import { cx } from '@algolia/ui-components-shared'; import { h } from 'preact'; import { isSpecialClick } from '../../lib/utils'; +import Template from '../Template/Template'; import type { ComponentCSSClasses } from '../../types'; import type { @@ -58,7 +59,8 @@ function Pagination(props: PaginationProps) { ariaLabel="First" className={props.cssClasses.firstPageItem} isDisabled={props.isFirstPage} - label={props.templates.first} + templates={props.templates} + templateKey="first" pageNumber={0} createURL={props.createURL} cssClasses={props.cssClasses} @@ -71,7 +73,8 @@ function Pagination(props: PaginationProps) { ariaLabel="Previous" className={props.cssClasses.previousPageItem} isDisabled={props.isFirstPage} - label={props.templates.previous} + templates={props.templates} + templateKey="previous" pageNumber={props.currentPage - 1} createURL={props.createURL} cssClasses={props.cssClasses} @@ -85,7 +88,8 @@ function Pagination(props: PaginationProps) { ariaLabel={`Page ${pageNumber + 1}`} className={props.cssClasses.pageItem} isSelected={pageNumber === props.currentPage} - label={`${pageNumber + 1}`} + templates={props.templates} + templateKey="page" pageNumber={pageNumber} createURL={props.createURL} cssClasses={props.cssClasses} @@ -98,7 +102,8 @@ function Pagination(props: PaginationProps) { ariaLabel="Next" className={props.cssClasses.nextPageItem} isDisabled={props.isLastPage} - label={props.templates.next} + templates={props.templates} + templateKey="next" pageNumber={props.currentPage + 1} createURL={props.createURL} cssClasses={props.cssClasses} @@ -111,7 +116,8 @@ function Pagination(props: PaginationProps) { ariaLabel="Last" className={props.cssClasses.lastPageItem} isDisabled={props.isLastPage} - label={props.templates.last} + templates={props.templates} + templateKey="last" pageNumber={props.nbPages - 1} createURL={props.createURL} cssClasses={props.cssClasses} @@ -124,7 +130,8 @@ function Pagination(props: PaginationProps) { } type PaginationLinkProps = { - label: string; + templates: PaginationTemplates; + templateKey: keyof PaginationTemplates; ariaLabel: string; pageNumber: number; isDisabled?: boolean; @@ -136,7 +143,8 @@ type PaginationLinkProps = { }; function PaginationLink({ - label, + templates, + templateKey, ariaLabel, pageNumber, className, @@ -156,17 +164,31 @@ function PaginationLink({ )} > {isDisabled ? ( - ) : ( - )} diff --git a/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx b/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx index f1949e92c8..edadd914e5 100644 --- a/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx +++ b/packages/instantsearch.js/src/components/Pagination/__tests__/Pagination-test.tsx @@ -33,7 +33,13 @@ describe('Pagination', () => { link: 'link', }, createURL: (args) => JSON.stringify(args), - templates: { first: '', last: '', next: '', previous: '' }, + templates: { + first: '', + last: '', + next: '', + page: ({ page }) => `${page}`, + previous: '', + }, currentPage: 0, pages: pager.pages(), isFirstPage: pager.isFirstPage(), diff --git a/packages/instantsearch.js/src/components/Panel/Panel.tsx b/packages/instantsearch.js/src/components/Panel/Panel.tsx index 30faa54d45..2c54bd1165 100644 --- a/packages/instantsearch.js/src/components/Panel/Panel.tsx +++ b/packages/instantsearch.js/src/components/Panel/Panel.tsx @@ -19,7 +19,7 @@ export type PanelComponentCSSClasses = ComponentCSSClasses< >; export type PanelComponentTemplates = - Required>; + PanelTemplates; export type PanelProps = { hidden: boolean; diff --git a/packages/instantsearch.js/src/components/Template/Template.tsx b/packages/instantsearch.js/src/components/Template/Template.tsx index de6adf16bf..8b7fcade02 100644 --- a/packages/instantsearch.js/src/components/Template/Template.tsx +++ b/packages/instantsearch.js/src/components/Template/Template.tsx @@ -41,16 +41,21 @@ class Template extends Component { } public render() { - warning( - Object.keys(this.props.templates).every( - (key) => typeof this.props.templates[key] === 'function' - ), - `Hogan.js and string-based templates are deprecated and will not be supported in InstantSearch.js 5.x. + if (__DEV__) { + const nonFunctionTemplates = Object.keys(this.props.templates).filter( + (key) => typeof this.props.templates[key] !== 'function' + ); + warning( + nonFunctionTemplates.length === 0, + `Hogan.js and string-based templates are deprecated and will not be supported in InstantSearch.js 5.x. You can replace them with function-form templates and use either the provided \`html\` function or JSX templates. +String-based templates: ${nonFunctionTemplates.join(', ')}. + See: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/#upgrade-templates` - ); + ); + } const RootTagName = this.props.rootTagName; diff --git a/packages/instantsearch.js/src/components/Template/__tests__/Template-test.tsx b/packages/instantsearch.js/src/components/Template/__tests__/Template-test.tsx index e127a4bb51..e8defa28ee 100644 --- a/packages/instantsearch.js/src/components/Template/__tests__/Template-test.tsx +++ b/packages/instantsearch.js/src/components/Template/__tests__/Template-test.tsx @@ -79,6 +79,8 @@ describe('Template', () => { You can replace them with function-form templates and use either the provided \`html\` function or JSX templates. +String-based templates: test. + See: https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/#upgrade-templates`); }); diff --git a/packages/instantsearch.js/src/helpers/highlight.ts b/packages/instantsearch.js/src/helpers/highlight.ts index 8c86319681..feb207e77e 100644 --- a/packages/instantsearch.js/src/helpers/highlight.ts +++ b/packages/instantsearch.js/src/helpers/highlight.ts @@ -15,12 +15,22 @@ export type HighlightOptions = { const suit = component('Highlight'); +/** + * @deprecated use html tagged templates and the Highlight component instead + */ export default function highlight({ attribute, highlightedTagName = 'mark', hit, cssClasses = {}, }: HighlightOptions): string { + warning( + false, + `\`instantsearch.highlight\` function has been deprecated. It is still supported in 4.x releases, but not further. It is replaced by the \`Highlight\` component. + +For more information, visit https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/?client=html+tagged+templates#upgrade-templates` + ); + const highlightAttributeResult = getPropertyByPath( hit._highlightResult, attribute diff --git a/packages/instantsearch.js/src/helpers/reverseHighlight.ts b/packages/instantsearch.js/src/helpers/reverseHighlight.ts index f567235368..0cfca7ec7e 100644 --- a/packages/instantsearch.js/src/helpers/reverseHighlight.ts +++ b/packages/instantsearch.js/src/helpers/reverseHighlight.ts @@ -22,12 +22,22 @@ export type ReverseHighlightOptions = { const suit = component('ReverseHighlight'); +/** + * @deprecated use html tagged templates and the ReverseHighlight component instead + */ export default function reverseHighlight({ attribute, highlightedTagName = 'mark', hit, cssClasses = {}, }: ReverseHighlightOptions): string { + warning( + false, + `\`instantsearch.reverseHighlight\` function has been deprecated. It is still supported in 4.x releases, but not further. It is replaced by the \`ReverseHighlight\` component. + +For more information, visit https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/?client=html+tagged+templates#upgrade-templates` + ); + const highlightAttributeResult = getPropertyByPath( hit._highlightResult, attribute diff --git a/packages/instantsearch.js/src/helpers/reverseSnippet.ts b/packages/instantsearch.js/src/helpers/reverseSnippet.ts index e13d6fb4a2..404e772348 100644 --- a/packages/instantsearch.js/src/helpers/reverseSnippet.ts +++ b/packages/instantsearch.js/src/helpers/reverseSnippet.ts @@ -22,12 +22,22 @@ export type ReverseSnippetOptions = { const suit = component('ReverseSnippet'); +/** + * @deprecated use html tagged templates and the ReverseSnippet component instead + */ export default function reverseSnippet({ attribute, highlightedTagName = 'mark', hit, cssClasses = {}, }: ReverseSnippetOptions): string { + warning( + false, + `\`instantsearch.reverseSnippet\` function has been deprecated. It is still supported in 4.x releases, but not further. It is replaced by the \`ReverseSnippet\` component. + +For more information, visit https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/?client=html+tagged+templates#upgrade-templates` + ); + const snippetAttributeResult = getPropertyByPath( hit._snippetResult, attribute diff --git a/packages/instantsearch.js/src/helpers/snippet.ts b/packages/instantsearch.js/src/helpers/snippet.ts index 5e5c17b928..8639cb9224 100644 --- a/packages/instantsearch.js/src/helpers/snippet.ts +++ b/packages/instantsearch.js/src/helpers/snippet.ts @@ -15,12 +15,22 @@ export type SnippetOptions = { const suit = component('Snippet'); +/** + * @deprecated use html tagged templates and the Snippet component instead + */ export default function snippet({ attribute, highlightedTagName = 'mark', hit, cssClasses = {}, }: SnippetOptions): string { + warning( + false, + `\`instantsearch.snippet\` function has been deprecated. It is still supported in 4.x releases, but not further. It is replaced by the \`Snippet\` component. + +For more information, visit https://www.algolia.com/doc/guides/building-search-ui/upgrade-guides/js/?client=html+tagged+templates#upgrade-templates` + ); + const snippetAttributeResult = getPropertyByPath( hit._snippetResult, attribute diff --git a/packages/instantsearch.js/src/index.ts b/packages/instantsearch.js/src/index.ts index efd496606c..38919692b6 100644 --- a/packages/instantsearch.js/src/index.ts +++ b/packages/instantsearch.js/src/index.ts @@ -25,9 +25,14 @@ type InstantSearchModule = { stateMappings: typeof stateMappings; createInfiniteHitsSessionStorageCache: typeof createInfiniteHitsSessionStorageCache; + + /** @deprecated use html tagged templates and the Highlight component instead */ highlight: typeof helpers.highlight; + /** @deprecated use html tagged templates and the ReverseHighlight component instead */ reverseHighlight: typeof helpers.reverseHighlight; + /** @deprecated use html tagged templates and the Snippet component instead */ snippet: typeof helpers.snippet; + /** @deprecated use html tagged templates and the ReverseSnippet component instead */ reverseSnippet: typeof helpers.reverseSnippet; /** diff --git a/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap b/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap index 5fcc4ce331..1a020a7d06 100644 --- a/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap +++ b/packages/instantsearch.js/src/widgets/pagination/__tests__/__snapshots__/pagination-test.ts.snap @@ -36,10 +36,11 @@ exports[`pagination() calls twice render(, container) 1`] = "showNext": true, "showPrevious": true, "templates": { - "first": "«", - "last": "»", - "next": "›", - "previous": "‹", + "first": [Function], + "last": [Function], + "next": [Function], + "page": [Function], + "previous": [Function], }, } `; @@ -80,10 +81,11 @@ exports[`pagination() calls twice render(, container) 2`] = "showNext": true, "showPrevious": true, "templates": { - "first": "«", - "last": "»", - "next": "›", - "previous": "‹", + "first": [Function], + "last": [Function], + "next": [Function], + "page": [Function], + "previous": [Function], }, } `; diff --git a/packages/instantsearch.js/src/widgets/pagination/pagination.tsx b/packages/instantsearch.js/src/widgets/pagination/pagination.tsx index 51735e6f92..8e99cec4fd 100644 --- a/packages/instantsearch.js/src/widgets/pagination/pagination.tsx +++ b/packages/instantsearch.js/src/widgets/pagination/pagination.tsx @@ -20,16 +20,17 @@ import type { PaginationRenderState, PaginationWidgetDescription, } from '../../connectors/pagination/connectPagination'; -import type { Renderer, WidgetFactory } from '../../types'; +import type { Renderer, Template, WidgetFactory } from '../../types'; const suit = component('Pagination'); const withUsage = createDocumentationMessageGenerator({ name: 'pagination' }); const defaultTemplates: PaginationComponentTemplates = { - previous: '‹', - next: '›', - first: '«', - last: '»', + previous: () => '‹', + next: () => '›', + page: ({ page }) => `${page}`, + first: () => '«', + last: () => '»', }; const renderer = @@ -160,22 +161,28 @@ export type PaginationTemplates = Partial<{ /** * Label for the Previous link. */ - previous: string; + previous: Template; /** * Label for the Next link. */ - next: string; + next: Template; + + /** + * Label for the link of a certain page + * Page is one-based, so `page` will be `1` for the first page. + */ + page: Template<{ page: number }>; /** * Label for the First link. */ - first: string; + first: Template; /** * Label for the Last link. */ - last: string; + last: Template; }>; export type PaginationWidgetParams = { diff --git a/packages/instantsearch.js/src/widgets/panel/__tests__/panel-test.ts b/packages/instantsearch.js/src/widgets/panel/__tests__/panel-test.ts index e4cea99666..86709e8a70 100644 --- a/packages/instantsearch.js/src/widgets/panel/__tests__/panel-test.ts +++ b/packages/instantsearch.js/src/widgets/panel/__tests__/panel-test.ts @@ -132,8 +132,6 @@ describe('Templates', () => { const { templates } = firstRender.props as PanelProps; expect(templates).toEqual({ - header: '', - footer: '', collapseButtonText: expect.any(Function), }); }); diff --git a/packages/instantsearch.js/src/widgets/panel/__tests__/panel.test.tsx b/packages/instantsearch.js/src/widgets/panel/__tests__/panel.test.tsx index 7e2813452d..65cedb6548 100644 --- a/packages/instantsearch.js/src/widgets/panel/__tests__/panel.test.tsx +++ b/packages/instantsearch.js/src/widgets/panel/__tests__/panel.test.tsx @@ -48,30 +48,28 @@ describe('panel', () => { await wait(0); expect(container).toMatchInlineSnapshot(` -
-
- -
-
-
- +
- 10 results found in 0ms - +
+
+
+ + 10 results found in 0ms + +
+
+
+
-
-
- -
-
-`); + `); }); test('renders with templates using `html`', async () => { @@ -109,58 +107,58 @@ describe('panel', () => { await wait(0); expect(container).toMatchInlineSnapshot(` -
- -
-`); + `); }); test('renders with templates using JSX', async () => { @@ -200,58 +198,58 @@ describe('panel', () => { await wait(0); expect(container).toMatchInlineSnapshot(` -
-
-
- - - Header - (10 results) - - - -
-
-
-
- +
- 10 results found in 0ms - +
+ + + Header + (10 results) + + + +
+
+
+
+ + 10 results found in 0ms + +
+
+
+ +
-
-
- -
-
-`); + `); }); function createMockedSearchClient() { diff --git a/packages/instantsearch.js/src/widgets/panel/panel.tsx b/packages/instantsearch.js/src/widgets/panel/panel.tsx index 49d8a96573..e4118c65fc 100644 --- a/packages/instantsearch.js/src/widgets/panel/panel.tsx +++ b/packages/instantsearch.js/src/widgets/panel/panel.tsx @@ -151,7 +151,7 @@ const renderer = containerNode: HTMLElement; bodyContainerNode: HTMLElement; cssClasses: PanelComponentCSSClasses; - templates: Required>; + templates: PanelTemplates; }) => ({ options, @@ -259,9 +259,7 @@ const panel: PanelWidget = (panelWidgetParams) => { const containerNode = getContainerNode(widgetParams.container); - const defaultTemplates: Required> = { - header: '', - footer: '', + const defaultTemplates: PanelTemplates = { collapseButtonText: ({ collapsed: isCollapsed }) => ` { - return hits.length === 0 ? '' : `

Answers

`; - }, - loader: `loading...`, - item: (hit) => { - return `

${hit._answer.extract}

`; + header: ({ hits }, { html }) => { + return hits.length === 0 ? '' : html`

Answers

`; }, + loader: () => `loading...`, + item: (hit, { html }) => html`

${hit._answer.extract}

`, }, }), ]); @@ -104,24 +102,22 @@ storiesOf('Results/Answers', module) root: 'my-Answers', }, templates: { - loader: ` -
-
-
-
-
-
+ loader: (_, { html }) => html` +
+
+
+
+
+
+
-
`, - item: (hit) => { - return ` -

${hit.title}

-
-

${hit._answer.extract}

- `; - }, + item: (hit, { html }) => html` +

${hit.title}

+
+

${hit._answer.extract}

+ `, }, }), ]); diff --git a/packages/instantsearch.js/stories/breadcrumb.stories.ts b/packages/instantsearch.js/stories/breadcrumb.stories.ts index 10a9e274a1..a57e5ccd14 100644 --- a/packages/instantsearch.js/stories/breadcrumb.stories.ts +++ b/packages/instantsearch.js/stories/breadcrumb.stories.ts @@ -69,7 +69,7 @@ storiesOf('Metadata/Breadcrumb', module) 'hierarchicalCategories.lvl2', ], templates: { - separator: ' + ', + separator: () => ' + ', }, }), ]); @@ -105,7 +105,7 @@ storiesOf('Metadata/Breadcrumb', module) 'hierarchicalCategories.lvl2', ], templates: { - home: 'Home Page', + home: () => 'Home Page', }, }), ]); diff --git a/packages/instantsearch.js/stories/clear-refinements.stories.ts b/packages/instantsearch.js/stories/clear-refinements.stories.ts index ce7387c8be..b0ad00a415 100644 --- a/packages/instantsearch.js/stories/clear-refinements.stories.ts +++ b/packages/instantsearch.js/stories/clear-refinements.stories.ts @@ -32,7 +32,7 @@ storiesOf('Refinements/ClearRefinements', module) container, includedAttributes: ['query'], templates: { - resetLabel: 'Clear query', + resetLabel: () => 'Clear query', }, }), ]); @@ -47,7 +47,7 @@ storiesOf('Refinements/ClearRefinements', module) container, excludedAttributes: [], templates: { - resetLabel: 'Clear refinements and query', + resetLabel: () => 'Clear refinements and query', }, }), ]); diff --git a/packages/instantsearch.js/stories/configure-related-items.stories.ts b/packages/instantsearch.js/stories/configure-related-items.stories.ts index e6b3d1e579..625e333a28 100644 --- a/packages/instantsearch.js/stories/configure-related-items.stories.ts +++ b/packages/instantsearch.js/stories/configure-related-items.stories.ts @@ -106,20 +106,18 @@ storiesOf('Basics/ConfigureRelatedItems', module).add( instantsearch.widgets.hits({ container: widgetParams.container, templates: { - item(item) { - return ` -
  • -
    - ${item.name} -
    - -
    -

    ${item.name}

    -
    -
  • - `; - }, - empty: '', + item: (item, { html }) => html` +
  • +
    + ${item.name} +
    + +
    +

    ${item.name}

    +
    +
  • + `, + empty: () => '', }, }), ]), @@ -134,26 +132,26 @@ storiesOf('Basics/ConfigureRelatedItems', module).add( instantsearch.widgets.hits({ container: productContainer, templates: { - item: ` -
    -
    -
    - {{#helpers.highlight}}{ "attribute": "name" }{{/helpers.highlight}} -
    -

    - {{#helpers.snippet}}{ "attribute": "description" }{{/helpers.snippet}} -

    -
    -

    - {{price}}$ -

    -
    -
    -`, - empty: '', + item: (hit, { html, components }) => html` +
    +
    +
    + + ${components.Highlight({ attribute: 'name', hit })})} + +
    +

    ${components.Snippet({ attribute: 'description', hit })})}

    +
    +

    + ${hit.price}$ +

    +
    +
    + `, + empty: () => '', }, cssClasses: { item: 'hits-item', diff --git a/packages/instantsearch.js/stories/current-refinements.stories.ts b/packages/instantsearch.js/stories/current-refinements.stories.ts index 66e5e7e6de..cc36552999 100644 --- a/packages/instantsearch.js/stories/current-refinements.stories.ts +++ b/packages/instantsearch.js/stories/current-refinements.stories.ts @@ -141,7 +141,7 @@ storiesOf('Refinements/CurrentRefinements', module) container: toggleRefinementContainer, attribute: 'free_shipping', templates: { - labelText: 'Free Shipping', + labelText: () => 'Free Shipping', }, }), ]); diff --git a/packages/instantsearch.js/stories/dynamic-widgets.stories.ts b/packages/instantsearch.js/stories/dynamic-widgets.stories.ts index c06d86ebf2..e5c05cd0a0 100644 --- a/packages/instantsearch.js/stories/dynamic-widgets.stories.ts +++ b/packages/instantsearch.js/stories/dynamic-widgets.stories.ts @@ -34,7 +34,7 @@ storiesOf('Basics/DynamicWidgets', module) }), (container) => instantsearch.widgets.panel({ - templates: { header: 'hierarchy' }, + templates: { header: () => 'hierarchy' }, })(instantsearch.widgets.hierarchicalMenu)({ container, attributes: [ @@ -79,7 +79,7 @@ storiesOf('Basics/DynamicWidgets', module) }), (container) => instantsearch.widgets.panel({ - templates: { header: 'hierarchy' }, + templates: { header: () => 'hierarchy' }, })(instantsearch.widgets.hierarchicalMenu)({ container, attributes: [ diff --git a/packages/instantsearch.js/stories/geo-search.stories.ts b/packages/instantsearch.js/stories/geo-search.stories.ts index 3acd702e44..95090aa221 100644 --- a/packages/instantsearch.js/stories/geo-search.stories.ts +++ b/packages/instantsearch.js/stories/geo-search.stories.ts @@ -224,9 +224,10 @@ stories instantsearch.widgets.geoSearch({ googleReference: window.google, templates: { - reset: 're-center', - toggle: 'Redo search when map moved', - redo: 'Search this area', + reset: (_, { html }) => html`re-center`, + toggle: (_, { html }) => + html`Redo search when map moved`, + redo: (_, { html }) => html`Search this area`, }, container, initialPosition, @@ -382,11 +383,10 @@ stories }, }, templates: { - HTMLMarker: ` -
    - {{price_formatted}} -
    - `, + HTMLMarker: (hit, { html }) => + html`
    + ${hit.price_formatted} +
    `, }, container, initialPosition, @@ -428,11 +428,10 @@ stories }, }, templates: { - HTMLMarker: ` -
    - {{price_formatted}} -
    - `, + HTMLMarker: (hit, { html }) => + html`
    + ${hit.price_formatted} +
    `, }, container, initialPosition, @@ -487,11 +486,10 @@ stories }, }, templates: { - HTMLMarker: ` -
    - {{price_formatted}} -
    - `, + HTMLMarker: (hit, { html }) => + html`
    + ${hit.price_formatted} +
    `, }, container, initialPosition, @@ -567,11 +565,12 @@ stories }, }, templates: { - HTMLMarker: ` -
    - {{price_formatted}} -
    - `, + HTMLMarker: (hit, { html }) => html`
    + ${hit.price_formatted} +
    `, }, container, initialPosition, diff --git a/packages/instantsearch.js/stories/hits.stories.ts b/packages/instantsearch.js/stories/hits.stories.ts index 281483a44d..d54b63d353 100644 --- a/packages/instantsearch.js/stories/hits.stories.ts +++ b/packages/instantsearch.js/stories/hits.stories.ts @@ -39,8 +39,8 @@ storiesOf('Results/Hits', module) instantsearch.widgets.hits({ container, templates: { - item(hit) { - return instantsearch.highlight({ + item(hit, { components }) { + return components.Highlight({ attribute: 'name', hit, }); @@ -70,8 +70,8 @@ storiesOf('Results/Hits', module) instantsearch.widgets.hits({ container, templates: { - item(hit) { - return instantsearch.reverseHighlight({ + item(hit, { components }) { + return components.ReverseHighlight({ attribute: 'name', hit, }); @@ -107,16 +107,20 @@ storiesOf('Results/Hits', module) instantsearch.widgets.hits({ container, templates: { - item(hit) { - return ` -

    ${instantsearch.snippet({ - attribute: 'name', - hit, - })}

    -

    ${instantsearch.snippet({ - attribute: 'description', - hit, - })}

    + item(hit, { html, components }) { + return html` +

    + ${components.Snippet({ + attribute: 'name', + hit, + })} +

    +

    + ${components.Snippet({ + attribute: 'description', + hit, + })} +

    `; }, }, @@ -214,12 +218,7 @@ storiesOf('Results/Hits', module) item: (item, { html, sendEvent }) => html`

    ${item.name}

    diff --git a/packages/instantsearch.js/stories/infinite-hits.stories.ts b/packages/instantsearch.js/stories/infinite-hits.stories.ts index 70a4d7504d..f268c72c33 100644 --- a/packages/instantsearch.js/stories/infinite-hits.stories.ts +++ b/packages/instantsearch.js/stories/infinite-hits.stories.ts @@ -19,7 +19,7 @@ storiesOf('Results/InfiniteHits', module) instantsearch.widgets.infiniteHits({ container, templates: { - item: '{{name}}', + item: ({ name }) => name, }, }), ]); @@ -32,7 +32,7 @@ storiesOf('Results/InfiniteHits', module) instantsearch.widgets.infiniteHits({ container, templates: { - item: '{{name}}', + item: ({ name }) => name, showMoreText: 'Load more', }, }), diff --git a/packages/instantsearch.js/stories/menu-select.stories.ts b/packages/instantsearch.js/stories/menu-select.stories.ts index a0000fbbd9..683c76cebf 100644 --- a/packages/instantsearch.js/stories/menu-select.stories.ts +++ b/packages/instantsearch.js/stories/menu-select.stories.ts @@ -23,7 +23,7 @@ storiesOf('Refinements/MenuSelect', module) attribute: 'categories', limit: 10, templates: { - item: '{{label}}', + item: ({ label }) => label, }, }), ]); @@ -38,7 +38,7 @@ storiesOf('Refinements/MenuSelect', module) attribute: 'categories', limit: 10, templates: { - defaultOption: 'Default choice', + defaultOption: () => 'Default choice', }, }), ]); diff --git a/packages/instantsearch.js/stories/menu.stories.ts b/packages/instantsearch.js/stories/menu.stories.ts index bfd45f5392..af6761ec5b 100644 --- a/packages/instantsearch.js/stories/menu.stories.ts +++ b/packages/instantsearch.js/stories/menu.stories.ts @@ -68,13 +68,7 @@ storiesOf('Refinements/Menu', module) showMore: true, showMoreLimit: 10, templates: { - showMoreText: ` - {{#isShowingMore}} - ⬆️ - {{/isShowingMore}} - {{^isShowingMore}} - ⬇️ - {{/isShowingMore}}`, + showMoreText: ({ isShowingMore }) => (isShowingMore ? '⬆️' : '⬇️'), }, }), ]); diff --git a/packages/instantsearch.js/stories/pagination.stories.ts b/packages/instantsearch.js/stories/pagination.stories.ts index f3980d0abc..6fdfd78435 100644 --- a/packages/instantsearch.js/stories/pagination.stories.ts +++ b/packages/instantsearch.js/stories/pagination.stories.ts @@ -76,10 +76,10 @@ storiesOf('Pagination/Pagination', module) instantsearch.widgets.pagination({ container, templates: { - previous: 'Previous', - next: 'Next', - first: 'First', - last: 'Last', + previous: () => 'Previous', + next: () => 'Next', + first: () => 'First', + last: () => 'Last', }, }), ]); diff --git a/packages/instantsearch.js/stories/panel.stories.ts b/packages/instantsearch.js/stories/panel.stories.ts index ad317a9b92..84d3d8e4c3 100644 --- a/packages/instantsearch.js/stories/panel.stories.ts +++ b/packages/instantsearch.js/stories/panel.stories.ts @@ -23,7 +23,7 @@ storiesOf('Basics/Panel', module) withHits(({ search, container, instantsearch }) => { const brandList = instantsearch.widgets.panel({ templates: { - header: 'Brand', + header: () => 'Brand', footer: ({ results }) => { return results ? `${results.nbHits} results` : ''; }, @@ -43,8 +43,8 @@ storiesOf('Basics/Panel', module) withHits(({ search, container, instantsearch }) => { const priceList = instantsearch.widgets.panel({ templates: { - header: 'Price', - footer: 'Footer', + header: () => 'Price', + footer: () => 'Footer', }, })(instantsearch.widgets.rangeInput); @@ -63,8 +63,8 @@ storiesOf('Basics/Panel', module) typeof instantsearch.widgets.rangeSlider >({ templates: { - header: 'Price', - footer: 'Footer', + header: () => 'Price', + footer: () => 'Footer', }, })(instantsearch.widgets.rangeSlider); @@ -87,8 +87,8 @@ storiesOf('Basics/Panel', module) withHits(({ search, container, instantsearch }) => { const priceList = instantsearch.widgets.panel({ templates: { - header: 'Price', - footer: 'The panel is hidden when there are no results.', + header: () => 'Price', + footer: () => 'The panel is hidden when there are no results.', }, hidden: ({ results }) => results.nbHits === 0, })(instantsearch.widgets.rangeInput); @@ -109,8 +109,8 @@ storiesOf('Basics/Panel', module) return options && options.state && !options.state.query; }, templates: { - header: 'Brand (collapsible)', - footer: 'The panel collapses on empty query until controlled', + header: () => 'Brand (collapsible)', + footer: () => 'The panel collapses on empty query until controlled', }, })(instantsearch.widgets.refinementList); @@ -130,8 +130,8 @@ storiesOf('Basics/Panel', module) return options && options.state && !options.state.query; }, templates: { - header: 'Collapsible panel', - footer: 'The panel collapses on empty query until controlled', + header: () => 'Collapsible panel', + footer: () => 'The panel collapses on empty query until controlled', collapseButtonText: ({ collapsed }) => (collapsed ? 'More' : 'Less'), }, })(instantsearch.widgets.refinementList); @@ -158,7 +158,7 @@ storiesOf('Basics/Panel', module) header({ canRefine }) { return `Breadcrumb that can${canRefine ? '' : "'t "} refine`; }, - footer: + footer: () => 'The panel collapses if it cannot refine. Click "Home". This panel will collapse and you will not see this footer anymore.', }, })(instantsearch.widgets.breadcrumb); diff --git a/packages/instantsearch.js/stories/query-rule-context.stories.ts b/packages/instantsearch.js/stories/query-rule-context.stories.ts index 7bc07873b7..326006abcb 100644 --- a/packages/instantsearch.js/stories/query-rule-context.stories.ts +++ b/packages/instantsearch.js/stories/query-rule-context.stories.ts @@ -46,22 +46,20 @@ storiesOf('Metadata/QueryRuleContext', module) return items.filter((item) => typeof item.banner !== 'undefined'); }, templates: { - default: ({ items }: { items: CustomDataItem[] }) => - items - .map((item) => { - const { title, banner, link } = item; + default: ({ items }: { items: CustomDataItem[] }, { html }) => + items.map((item) => { + const { title, banner, link } = item; - return ` + return html`

    ${title}

    - ${title} + ${title}
    `; - }) - .join(''), + }), }, }), ]); @@ -97,22 +95,20 @@ storiesOf('Metadata/QueryRuleContext', module) return items.filter((item) => typeof item.banner !== 'undefined'); }, templates: { - default: ({ items }: { items: CustomDataItem[] }) => - items - .map((item) => { - const { title, banner, link } = item; + default: ({ items }: { items: CustomDataItem[] }, { html }) => + items.map((item) => { + const { title, banner, link } = item; - return ` -
    -

    ${title}

    + return html` +
    +

    ${title}

    - - ${title} - -
    - `; - }) - .join(''), + + ${title} + +
    + `; + }), }, }), ]); diff --git a/packages/instantsearch.js/stories/query-rule-custom-data.stories.ts b/packages/instantsearch.js/stories/query-rule-custom-data.stories.ts index 0e4ab81fdd..e6ab22cfb4 100644 --- a/packages/instantsearch.js/stories/query-rule-custom-data.stories.ts +++ b/packages/instantsearch.js/stories/query-rule-custom-data.stories.ts @@ -31,26 +31,24 @@ storiesOf('Metadata/QueryRuleCustomData', module) instantsearch.widgets.queryRuleCustomData({ container: widgetContainer, templates: { - default: ({ items }: { items: CustomDataItem[] }) => - items - .map((item) => { - const { title, banner, link } = item; + default: ({ items }: { items: CustomDataItem[] }, { html }) => + items.map((item) => { + const { title, banner, link } = item; - if (!banner) { - return ''; - } + if (!banner) { + return html``; + } - return ` + return html`

    ${title}

    - ${title} + ${title}
    `; - }) - .join(''), + }), }, }), ]); @@ -116,20 +114,20 @@ storiesOf('Metadata/QueryRuleCustomData', module) ]; }, templates: { - default({ items }: { items: CustomDataItem[] }) { + default({ items }: { items: CustomDataItem[] }, { html }) { if (items.length === 0) { - return ''; + return html``; } const { title, banner, link } = items[0]; - return ` -

    ${title}

    + return html` +

    ${title}

    - - ${title} - - `; + + ${title} + + `; }, }, }), diff --git a/packages/instantsearch.js/stories/range-input.stories.ts b/packages/instantsearch.js/stories/range-input.stories.ts index 2082579511..7959d27dd6 100644 --- a/packages/instantsearch.js/stories/range-input.stories.ts +++ b/packages/instantsearch.js/stories/range-input.stories.ts @@ -73,8 +73,8 @@ storiesOf('Refinements/RangeInput', module) min: 10, max: 500, templates: { - separatorText: '→', - submitText: 'Refine', + separatorText: () => '→', + submitText: () => 'Refine', }, }), ]); diff --git a/packages/instantsearch.js/stories/refinement-list.stories.ts b/packages/instantsearch.js/stories/refinement-list.stories.ts index c23670d5f9..fbf8fd1652 100644 --- a/packages/instantsearch.js/stories/refinement-list.stories.ts +++ b/packages/instantsearch.js/stories/refinement-list.stories.ts @@ -39,13 +39,7 @@ storiesOf('Refinements/RefinementList', module) showMore: true, showMoreLimit: 10, templates: { - showMoreText: ` - {{#isShowingMore}} - ⬆️ - {{/isShowingMore}} - {{^isShowingMore}} - ⬇️ - {{/isShowingMore}}`, + showMoreText: ({ isShowingMore }) => (isShowingMore ? '⬆️' : '⬇️'), }, }), ]); @@ -73,10 +67,10 @@ storiesOf('Refinements/RefinementList', module) searchable: true, searchablePlaceholder: 'Find other brands...', templates: { - searchableNoResults: 'No results found', - searchableSubmit: 'Go', - searchableReset: 'x', - searchableLoadingIndicator: '•', + searchableNoResults: () => 'No results found', + searchableSubmit: () => 'Go', + searchableReset: () => 'x', + searchableLoadingIndicator: () => '•', }, }), ]); diff --git a/packages/instantsearch.js/stories/search-box.stories.ts b/packages/instantsearch.js/stories/search-box.stories.ts index 72c5ca0295..35bd800bbf 100644 --- a/packages/instantsearch.js/stories/search-box.stories.ts +++ b/packages/instantsearch.js/stories/search-box.stories.ts @@ -53,7 +53,7 @@ storiesOf('Basics/SearchBox', module) instantsearch.widgets.searchBox({ container, templates: { - loadingIndicator: '⚡️', + loadingIndicator: () => '⚡️', }, }), ]); @@ -66,8 +66,10 @@ storiesOf('Basics/SearchBox', module) instantsearch.widgets.searchBox({ container, templates: { - submit: '
    🔍
    ', - reset: '
    ✖️
    ', + submit: (_, { html }) => + html`
    🔍
    `, + reset: (_, { html }) => + html`
    ✖️
    `, }, }), ]); diff --git a/packages/instantsearch.js/stories/toggle-refinement.stories.ts b/packages/instantsearch.js/stories/toggle-refinement.stories.ts index 46c03db7d0..ae9e95ac3e 100644 --- a/packages/instantsearch.js/stories/toggle-refinement.stories.ts +++ b/packages/instantsearch.js/stories/toggle-refinement.stories.ts @@ -22,7 +22,7 @@ storiesOf('Refinements/ToggleRefinement', module) container, attribute: 'free_shipping', templates: { - labelText: 'Free Shipping (toggle single value)', + labelText: () => 'Free Shipping (toggle single value)', }, }), ]); @@ -38,7 +38,7 @@ storiesOf('Refinements/ToggleRefinement', module) on: 'Sony', off: 'Canon', templates: { - labelText: 'Canon (not checked) or sony (checked)', + labelText: () => 'Canon (not checked) or sony (checked)', }, }), ]); @@ -52,7 +52,7 @@ storiesOf('Refinements/ToggleRefinement', module) container: node, attribute: 'free_shipping', templates: { - labelText: 'Free Shipping (toggle single value)', + labelText: () => 'Free Shipping (toggle single value)', }, }) ); diff --git a/packages/instantsearch.js/stories/voice-search.stories.ts b/packages/instantsearch.js/stories/voice-search.stories.ts index 2b5ba4fb36..2b197a43d8 100644 --- a/packages/instantsearch.js/stories/voice-search.stories.ts +++ b/packages/instantsearch.js/stories/voice-search.stories.ts @@ -28,7 +28,7 @@ storiesOf('Basics/VoiceSearch', module) instantsearch.widgets.voiceSearch({ container, templates: { - status: ``, + status: () => ``, }, }), ]); @@ -88,13 +88,13 @@ storiesOf('Basics/VoiceSearch', module) instantsearch.widgets.voiceSearch({ container, templates: { - status: ` -

    status: {{status}}

    -

    errorCode: {{errorCode}}

    -

    isListening: {{isListening}}

    -

    transcript: {{transcript}}

    -

    isSpeechFinal: {{isSpeechFinal}}

    -

    isBrowserSupported: {{isBrowserSupported}}

    + status: (data, { html }) => html` +

    status: ${data.status}

    +

    errorCode: ${data.errorCode}

    +

    isListening: ${data.isListening}

    +

    transcript: ${data.transcript}

    +

    isSpeechFinal: ${data.isSpeechFinal}

    +

    isBrowserSupported: ${data.isBrowserSupported}

    `, }, }), @@ -109,13 +109,13 @@ storiesOf('Basics/VoiceSearch', module) container, searchAsYouSpeak: true, templates: { - status: ` -

    status: {{status}}

    -

    errorCode: {{errorCode}}

    -

    isListening: {{isListening}}

    -

    transcript: {{transcript}}

    -

    isSpeechFinal: {{isSpeechFinal}}

    -

    isBrowserSupported: {{isBrowserSupported}}

    + status: (data, { html }) => html` +

    status: ${data.status}

    +

    errorCode: ${data.errorCode}

    +

    isListening: ${data.isListening}

    +

    transcript: ${data.transcript}

    +

    isSpeechFinal: ${data.isSpeechFinal}

    +

    isBrowserSupported: ${data.isBrowserSupported}

    `, }, }), @@ -168,12 +168,12 @@ storiesOf('Basics/VoiceSearch', module) status: 'voice-search-status', }, templates: { - status({ isListening, transcript }) { - return ` -
    - ${transcript} -
    - `; + status({ isListening, transcript }, { html }) { + return html` +
    + ${transcript} +
    + `; }, }, }),