Skip to content

Commit

Permalink
feat(metadata): register metadata around middleware (#5492)
Browse files Browse the repository at this point in the history
  • Loading branch information
Haroenv committed Mar 2, 2023
1 parent 3fed8cd commit b6ef7b3
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 55 deletions.
1 change: 1 addition & 0 deletions packages/instantsearch.js/src/lib/routers/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const setWindowTitle = (title?: string): void => {
};

class BrowserHistory<TRouteState> implements Router<TRouteState> {
public $$type = 'ais.browser';
/**
* Transforms a UI state into a title for the page.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/instantsearch.js/src/lib/stateMappings/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export default function simpleStateMapping<
TUiState extends UiState = UiState
>(): StateMapping<TUiState, TUiState> {
return {
$$type: 'ais.simple',

stateToRoute(uiState) {
return Object.keys(uiState).reduce(
(state, indexId) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default function singleIndexStateMapping<
indexName: keyof TUiState
): StateMapping<TUiState, TUiState[typeof indexName]> {
return {
$$type: 'ais.singleIndex',
stateToRoute(uiState) {
return getIndexStateWithoutConfigure(uiState[indexName] || {});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,52 +128,120 @@ describe('createMetadataMiddleware', () => {
expect(document.head).toMatchInlineSnapshot(`
<head>
<meta
content="{\\"widgets\\":[{\\"type\\":\\"ais.searchBox\\",\\"widgetType\\":\\"ais.searchBox\\",\\"params\\":[]},{\\"type\\":\\"ais.searchBox\\",\\"widgetType\\":\\"ais.searchBox\\",\\"params\\":[]},{\\"type\\":\\"ais.hits\\",\\"widgetType\\":\\"ais.hits\\",\\"params\\":[\\"escapeHTML\\"]},{\\"type\\":\\"ais.index\\",\\"widgetType\\":\\"ais.index\\",\\"params\\":[]},{\\"type\\":\\"ais.pagination\\",\\"widgetType\\":\\"ais.pagination\\",\\"params\\":[]},{\\"type\\":\\"ais.configure\\",\\"widgetType\\":\\"ais.configure\\",\\"params\\":[\\"searchParameters\\"]}]}"
content="{\\"widgets\\":[{\\"type\\":\\"ais.searchBox\\",\\"widgetType\\":\\"ais.searchBox\\",\\"params\\":[]},{\\"type\\":\\"ais.searchBox\\",\\"widgetType\\":\\"ais.searchBox\\",\\"params\\":[]},{\\"type\\":\\"ais.hits\\",\\"widgetType\\":\\"ais.hits\\",\\"params\\":[\\"escapeHTML\\"]},{\\"type\\":\\"ais.index\\",\\"widgetType\\":\\"ais.index\\",\\"params\\":[]},{\\"type\\":\\"ais.pagination\\",\\"widgetType\\":\\"ais.pagination\\",\\"params\\":[]},{\\"type\\":\\"ais.configure\\",\\"widgetType\\":\\"ais.configure\\",\\"params\\":[\\"searchParameters\\"]},{\\"middleware\\":true,\\"type\\":\\"ais.insights\\",\\"internal\\":true},{\\"middleware\\":true,\\"type\\":\\"ais.metadata\\",\\"internal\\":true}]}"
name="instantsearch:widgets"
/>
</head>
`);

expect(JSON.parse(document.head.querySelector('meta')!.content))
expect(JSON.parse(document.head.querySelector('meta')!.content).widgets)
.toMatchInlineSnapshot(`
{
"widgets": [
{
"params": [],
"type": "ais.searchBox",
"widgetType": "ais.searchBox",
},
{
"params": [],
"type": "ais.searchBox",
"widgetType": "ais.searchBox",
},
{
"params": [
"escapeHTML",
],
"type": "ais.hits",
"widgetType": "ais.hits",
},
{
"params": [],
"type": "ais.index",
"widgetType": "ais.index",
},
{
"params": [],
"type": "ais.pagination",
"widgetType": "ais.pagination",
},
{
"params": [
"searchParameters",
],
"type": "ais.configure",
"widgetType": "ais.configure",
},
],
}
[
{
"params": [],
"type": "ais.searchBox",
"widgetType": "ais.searchBox",
},
{
"params": [],
"type": "ais.searchBox",
"widgetType": "ais.searchBox",
},
{
"params": [
"escapeHTML",
],
"type": "ais.hits",
"widgetType": "ais.hits",
},
{
"params": [],
"type": "ais.index",
"widgetType": "ais.index",
},
{
"params": [],
"type": "ais.pagination",
"widgetType": "ais.pagination",
},
{
"params": [
"searchParameters",
],
"type": "ais.configure",
"widgetType": "ais.configure",
},
{
"internal": true,
"middleware": true,
"type": "ais.insights",
},
{
"internal": true,
"middleware": true,
"type": "ais.metadata",
},
]
`);
});

it('fills it with metadata after start', async () => {
// not using createMetadataMiddleware() here,
// since metadata is built into instantsearch
const search = instantsearch({
searchClient: createSearchClient(),
indexName: 'test',
routing: true,
});

search.use(
() => ({ $$type: 'test', $$internal: false }),
// @ts-expect-error (unknown middleware, shouldn't error)
() => ({})
);

search.start();

await wait(100);

expect(document.head).toMatchInlineSnapshot(`
<head>
<meta
content="{\\"widgets\\":[{\\"middleware\\":true,\\"type\\":\\"ais.router({router:ais.browser, stateMapping:ais.simple})\\",\\"internal\\":true},{\\"middleware\\":true,\\"type\\":\\"ais.insights\\",\\"internal\\":true},{\\"middleware\\":true,\\"type\\":\\"ais.metadata\\",\\"internal\\":true},{\\"middleware\\":true,\\"type\\":\\"test\\",\\"internal\\":false},{\\"middleware\\":true,\\"type\\":\\"__unknown__\\",\\"internal\\":false}]}"
name="instantsearch:widgets"
/>
</head>
`);

expect(JSON.parse(document.head.querySelector('meta')!.content).widgets)
.toMatchInlineSnapshot(`
[
{
"internal": true,
"middleware": true,
"type": "ais.router({router:ais.browser, stateMapping:ais.simple})",
},
{
"internal": true,
"middleware": true,
"type": "ais.insights",
},
{
"internal": true,
"middleware": true,
"type": "ais.metadata",
},
{
"internal": false,
"middleware": true,
"type": "test",
},
{
"internal": false,
"middleware": true,
"type": "__unknown__",
},
]
`);
});

Expand Down Expand Up @@ -204,7 +272,7 @@ describe('createMetadataMiddleware', () => {
ua: expect.stringMatching(
/^Algolia for JavaScript \(4\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\)$/
),
widgets: [],
widgets: expect.any(Array),
});
});

Expand Down Expand Up @@ -236,7 +304,7 @@ describe('createMetadataMiddleware', () => {
ua: expect.stringMatching(
/^Algolia for JavaScript \(4\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\); test \(cool\)$/
),
widgets: [],
widgets: expect.any(Array),
});
});

Expand Down Expand Up @@ -265,7 +333,7 @@ describe('createMetadataMiddleware', () => {
ua: expect.stringMatching(
/^Algolia for JavaScript \(3\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\)$/
),
widgets: [],
widgets: expect.any(Array),
});
});

Expand Down Expand Up @@ -296,7 +364,7 @@ describe('createMetadataMiddleware', () => {
ua: expect.stringMatching(
/^Algolia for JavaScript \(3\.(\d+\.?)+\); Node\.js \((\d+\.?)+\); instantsearch\.js \((\d+\.?)+\); JS Helper \((\d+\.?)+\); test \(cool\)$/
),
widgets: [],
widgets: expect.any(Array),
});
});

Expand All @@ -320,7 +388,7 @@ describe('createMetadataMiddleware', () => {
expect(
JSON.parse(document.head.querySelector('meta')!.content)
).toEqual({
widgets: [],
widgets: expect.any(Array),
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,24 @@ import { createInitArgs, safelyRunOnBrowser } from '../lib/utils';
import type { InstantSearch, InternalMiddleware, Widget } from '../types';
import type { IndexWidget } from '../widgets/index/index';

type WidgetMetaData = {
type: string | undefined;
widgetType: string | undefined;
params: string[];
};
type WidgetMetadata =
| {
type: string | undefined;
widgetType: string | undefined;
params: string[];
}
| {
type: string;
middleware: true;
internal: boolean;
};

type Payload = {
widgets: WidgetMetaData[];
widgets: WidgetMetadata[];
ua?: string;
};

function extractPayload(
function extractWidgetPayload(
widgets: Array<Widget | IndexWidget>,
instantSearchInstance: InstantSearch,
payload: Payload
Expand Down Expand Up @@ -49,7 +55,7 @@ function extractPayload(
});

if (widget.$$type === 'ais.index') {
extractPayload(
extractWidgetPayload(
(widget as IndexWidget).getWidgets(),
instantSearchInstance,
payload
Expand Down Expand Up @@ -99,12 +105,20 @@ export function createMetadataMiddleware({
? client.transporter.userAgent.value
: client._ua;

extractPayload(
extractWidgetPayload(
instantSearchInstance.mainIndex.getWidgets(),
instantSearchInstance,
payload
);

instantSearchInstance.middleware.forEach((middleware) =>
payload.widgets.push({
middleware: true,
type: middleware.instance.$$type,
internal: middleware.instance.$$internal,
})
);

payloadContainer.content = JSON.stringify(payload);
refNode.appendChild(payloadContainer);
}, 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ export const createRouterMiddleware = <
const initialUiState = instantSearchInstance._initialUiState;

return {
$$type: 'ais.router',
$$type: `ais.router({router:${
router.$$type || '__unknown__'
}, stateMapping:${stateMapping.$$type || '__unknown__'}})`,
$$internal,
onStateChange({ uiState }) {
const routeState = stateMapping.stateToRoute(uiState);
Expand Down
10 changes: 10 additions & 0 deletions packages/instantsearch.js/src/types/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export type Router<TRouteState = UiState> = {
* Called when InstantSearch is disposed. Used to remove subscriptions.
*/
dispose(): void;

/**
* Identifier for this router. Used to differentiate between routers.
*/
$$type?: string;
};

/**
Expand All @@ -57,4 +62,9 @@ export type StateMapping<TUiState = UiState, TRouteState = TUiState> = {
* The format is the output of `stateToRoute`.
*/
routeToState(routeState: TRouteState): TUiState;

/**
* Identifier for this stateMapping. Used to differentiate between stateMappings.
*/
$$type?: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export function createInstantSearchRouterNext<TRouteState = UiState>(
...routerOptions,
});
router._isNextRouter = true;
router.$$type = 'ais.nextjs';

return router;
}

0 comments on commit b6ef7b3

Please sign in to comment.