Skip to content

Commit

Permalink
feat(connectors): rely entirely on gWSP and gUS for parameters in dis…
Browse files Browse the repository at this point in the history
…pose

In `dispose`, we no longer have any logic other than clearing listeners and removing rendering. This causes a significant simplification in all connectors.

One side-effect of this is that `configure` no longer can be collaborating in ui state. If we'd want that, there's no way to distinguish between the ui state set for one configure or the one set for another. Therefore the configure widget is completely in charge of its parameters. If you want to synchronise configure with the URL, do that separately of the UI State.

Small side-effect of this PR is the deduplication of the test/mock createWidget files

BREAKING CHANGE: widgets no longer explicitly clean in dispose (handled in index widget automatically)
BREAKING CHANGE: configure no longer shows in ui state or routing.

[FX-3209]
  • Loading branch information
Haroenv committed Dec 30, 2024
1 parent 7ff6737 commit de97b0c
Show file tree
Hide file tree
Showing 83 changed files with 812 additions and 2,556 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { createSearchClient } from '@instantsearch/mocks';
import { wait } from '@instantsearch/testutils/wait';
import { createWidget } from 'instantsearch-core/test/createWidget';
import qs from 'qs';

import {
Expand All @@ -12,7 +13,6 @@ import {
connectHitsPerPage,
connectSearchBox,
} from '..';
import { createWidget } from '../../test/createWidget';

import type { Router, UiState, StateMapping, IndexUiState } from '../types';
import type { JSDOM } from 'jsdom';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,60 +6,15 @@ import { createSearchClient } from '@instantsearch/mocks';
import { createRecommendSearchClient } from '@instantsearch/mocks/fixtures';
import { castToJestMock } from '@instantsearch/testutils';
import { wait } from '@instantsearch/testutils/wait';
import { getByText, fireEvent } from '@testing-library/dom';

import {
instantsearch,
connectConfigure,
connectSearchBox,
connectFrequentlyBoughtTogether,
} from '..';

import type { MiddlewareDefinition } from '../types';

describe('configure', () => {
it('provides up-to-date uiState to onStateChange', () => {
const container = document.createElement('div');
const onStateChange = jest.fn();
const search = instantsearch({
indexName: 'instant_search',
searchClient: createSearchClient(),
onStateChange({ uiState, setUiState }) {
onStateChange(uiState);
setUiState(uiState);
},
});
const customComp = connectConfigure(({ refine }, isFirstRendering) => {
if (isFirstRendering) {
const button = document.createElement('button');
button.setAttribute('type', 'button');
button.textContent = 'click me';
container.appendChild(button);
container.querySelector('button')!.addEventListener('click', () => {
refine({ hitsPerPage: 4 });
});
}
});
search.addWidgets([
connectConfigure(() => {})({
searchParameters: {
hitsPerPage: 10,
},
}),
customComp({ searchParameters: {} }),
]);

search.start();
expect(onStateChange).not.toHaveBeenCalled();

fireEvent.click(getByText(container, 'click me'));
expect(onStateChange).toHaveBeenCalledTimes(1);
expect(onStateChange).toHaveBeenCalledWith({
instant_search: { configure: { hitsPerPage: 4 } },
});
});
});

describe('middleware', () => {
it("runs middlewares' onStateChange when uiState changes", async () => {
const search = instantsearch({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
import { castToJestMock } from '@instantsearch/testutils/castToJestMock';
import { wait } from '@instantsearch/testutils/wait';
import originalHelper from 'algoliasearch-helper';
import {
createRenderOptions,
createWidget,
} from 'instantsearch-core/test/createWidget';
import { h, render, createRef } from 'preact';

import {
Expand All @@ -21,7 +25,6 @@ import {
connectSearchBox,
connectPagination,
} from '..';
import { createRenderOptions, createWidget } from '../../test/createWidget';
import version from '../version';

import type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import algoliasearchHelper, {
SearchResults,
SearchParameters,
} from 'algoliasearch-helper';

import { connectAutocomplete, instantsearch, TAG_PLACEHOLDER } from '../..';
import {
createInitOptions,
createRenderOptions,
createDisposeOptions,
} from '../../../test/createWidget';
} from 'instantsearch-core/test/createWidget';

import { connectAutocomplete, instantsearch, TAG_PLACEHOLDER } from '../..';

import type { AutocompleteRenderState } from '../..';
import type { SearchClient, SearchResponse } from '../../types';
Expand Down Expand Up @@ -336,112 +336,21 @@ search.addWidgets([
});

describe('dispose', () => {
it('calls the unmount function', () => {
const searchClient = createSearchClient();
const helper = algoliasearchHelper(searchClient, '');

it('calls unmount function', () => {
const render = jest.fn();
const unmount = jest.fn();
const makeWidget = connectAutocomplete(render, unmount);
const widget = makeWidget({});

widget.init!(createInitOptions({ helper }));
const widget = connectAutocomplete(render, unmount)({});

expect(unmount).toHaveBeenCalledTimes(0);
widget.dispose!(createDisposeOptions());

widget.dispose!(createDisposeOptions({ helper, state: helper.state }));

expect(unmount).toHaveBeenCalledTimes(1);
expect(unmount).toHaveBeenCalled();
});

it('does not throw without the unmount function', () => {
const searchClient = createSearchClient();
const helper = algoliasearchHelper(searchClient, '');

const render = jest.fn();
const makeWidget = connectAutocomplete(render);
const widget = makeWidget({});

widget.init!(createInitOptions({ helper }));

expect(() =>
widget.dispose!(createDisposeOptions({ helper, state: helper.state }))
).not.toThrow();
});

it('removes the `query` from the `SearchParameters`', () => {
const searchClient = createSearchClient();
const helper = algoliasearchHelper(searchClient, '', {
query: 'Apple',
});

const render = jest.fn();
const makeWidget = connectAutocomplete(render);
const widget = makeWidget({});

widget.init!(createInitOptions({ helper }));

expect(helper.state.query).toBe('Apple');

const nextState = widget.dispose!(
createDisposeOptions({ helper, state: helper.state })
) as SearchParameters;

expect(nextState.query).toBeUndefined();
});

it('removes the TAG_PLACEHOLDER from the `SearchParameters`', () => {
const searchClient = createSearchClient();
const helper = algoliasearchHelper(searchClient, '', {
...TAG_PLACEHOLDER,
});

const render = jest.fn();
const makeWidget = connectAutocomplete(render);
const widget = makeWidget({});

expect(helper.state.highlightPreTag).toBe(
TAG_PLACEHOLDER.highlightPreTag
);

expect(helper.state.highlightPostTag).toBe(
TAG_PLACEHOLDER.highlightPostTag
);

widget.init!(createInitOptions({ helper }));

const nextState = widget.dispose!(
createDisposeOptions({ helper, state: helper.state })
) as SearchParameters;

expect(nextState.highlightPreTag).toBeUndefined();
expect(nextState.highlightPostTag).toBeUndefined();
});

it('does not remove the TAG_PLACEHOLDER from the `SearchParameters` with `escapeHTML` disabled', () => {
const searchClient = createSearchClient();
const helper = algoliasearchHelper(searchClient, '', {
highlightPreTag: '<mark>',
highlightPostTag: '</mark>',
});

const render = jest.fn();
const makeWidget = connectAutocomplete(render);
const widget = makeWidget({
escapeHTML: false,
});

expect(helper.state.highlightPreTag).toBe('<mark>');
expect(helper.state.highlightPostTag).toBe('</mark>');

widget.init!(createInitOptions({ helper }));

const nextState = widget.dispose!(
createDisposeOptions({ helper, state: helper.state })
) as SearchParameters;

expect(nextState.highlightPreTag).toBe('<mark>');
expect(nextState.highlightPostTag).toBe('</mark>');
const render = () => {};
const widget = connectAutocomplete(render)({});
expect(() => widget.dispose!(createDisposeOptions())).not.toThrow();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import algoliasearchHelper, {
SearchResults,
SearchParameters,
} from 'algoliasearch-helper';

import { connectBreadcrumb, warnCache } from '../..';
import {
createDisposeOptions,
createInitOptions,
createRenderOptions,
} from '../../../test/createWidget';
} from 'instantsearch-core/test/createWidget';

import { connectBreadcrumb, warnCache } from '../..';

describe('connectBreadcrumb', () => {
describe('Usage', () => {
Expand Down Expand Up @@ -1243,42 +1243,24 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/breadcrumb/
});

describe('dispose', () => {
it('does not throw without the unmount function', () => {
const helper = algoliasearchHelper(createSearchClient(), '');
it('calls unmount function', () => {
const render = jest.fn();
const unmount = jest.fn();

const renderFn = () => {};
const makeWidget = connectBreadcrumb(renderFn);
const widget = makeWidget({ attributes: ['category'] });
const widget = connectBreadcrumb(
render,
unmount
)({ attributes: ['cat'] });

expect(() =>
widget.dispose!(createDisposeOptions({ helper, state: helper.state }))
).not.toThrow();
});

it('does not remove refinement', () => {
const renderFn = () => {};
const makeWidget = connectBreadcrumb(renderFn);
const widget = makeWidget({ attributes: ['category'] });
widget.dispose!(createDisposeOptions());

const helper = algoliasearchHelper(createSearchClient(), '', {
hierarchicalFacetsRefinements: {
category: ['boxes'],
},
});
helper.search = jest.fn();

widget.init!(
createInitOptions({
helper,
state: helper.state,
})
);

const newState = widget.dispose!(
createDisposeOptions({ helper, state: helper.state })
);
expect(unmount).toHaveBeenCalled();
});

expect(newState).toBeUndefined();
it('does not throw without the unmount function', () => {
const render = () => {};
const widget = connectBreadcrumb(render)({ attributes: ['cat'] });
expect(() => widget.dispose!(createDisposeOptions())).not.toThrow();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import {
createSearchClient,
} from '@instantsearch/mocks';
import algoliasearchHelper, { SearchResults } from 'algoliasearch-helper';

import { connectClearRefinements } from '../..';
import {
createDisposeOptions,
createInitOptions,
createRenderOptions,
} from '../../../test/createWidget';
} from 'instantsearch-core/test/createWidget';

import { connectClearRefinements } from '../..';

describe('connectClearRefinements', () => {
describe('Usage', () => {
Expand Down Expand Up @@ -125,15 +125,23 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/clear-refin
expect(secondRenderingOptions.canRefine).toBe(false);
});

it('does not throw without the unmount function', () => {
const helper = algoliasearchHelper(createSearchClient(), 'indexName');
const rendering = () => {};
const makeWidget = connectClearRefinements(rendering);
const widget = makeWidget({});
describe('dispose', () => {
it('calls unmount function', () => {
const render = jest.fn();
const unmount = jest.fn();

const widget = connectClearRefinements(render, unmount)({});

widget.dispose!(createDisposeOptions());

expect(() =>
widget.dispose!(createDisposeOptions({ helper, state: helper.state }))
).not.toThrow();
expect(unmount).toHaveBeenCalled();
});

it('does not throw without the unmount function', () => {
const render = () => {};
const widget = connectClearRefinements(render)({});
expect(() => widget.dispose!(createDisposeOptions())).not.toThrow();
});
});

describe('getRenderState', () => {
Expand Down
Loading

0 comments on commit de97b0c

Please sign in to comment.