diff --git a/packages/autocomplete-core/src/__tests__/completion.test.ts b/packages/autocomplete-core/src/__tests__/completion.test.ts index 48b2ba0875..bbbd2683d4 100644 --- a/packages/autocomplete-core/src/__tests__/completion.test.ts +++ b/packages/autocomplete-core/src/__tests__/completion.test.ts @@ -26,30 +26,33 @@ describe('completion', () => { inputElement.focus(); userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: '1', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: '1', + }), + }) + ); userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: '2', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: '2', + }), + }) + ); userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: null, + }), + }) + ); }); test('does not set completion when panel is closed', () => { @@ -74,12 +77,13 @@ describe('completion', () => { inputElement.focus(); userEvent.type(inputElement, '{esc}{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: null, + }), + }) + ); }); test('does not set completion when no activeItemId', () => { @@ -92,12 +96,13 @@ describe('completion', () => { inputElement.focus(); userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: null, + }), + }) + ); }); test('does not set completion without itemInputValue', () => { @@ -117,11 +122,12 @@ describe('completion', () => { inputElement.focus(); userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - completion: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + completion: null, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts b/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts index 66fdd4069d..20f983f4b6 100644 --- a/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts +++ b/packages/autocomplete-core/src/__tests__/createAutocomplete.test.ts @@ -25,18 +25,19 @@ describe('createAutocomplete', () => { test('subscribes all plugins', () => { const plugin = { subscribe: jest.fn() }; - createAutocomplete({ plugins: [plugin] }); + const autocomplete = createAutocomplete({ plugins: [plugin] }); expect(plugin.subscribe).toHaveBeenCalledTimes(1); expect(plugin.subscribe).toHaveBeenLastCalledWith({ onActive: expect.any(Function), onSelect: expect.any(Function), - setCollections: expect.any(Function), - setContext: expect.any(Function), - setIsOpen: expect.any(Function), - setQuery: expect.any(Function), - setActiveItemId: expect.any(Function), - setStatus: expect.any(Function), + refresh: autocomplete.refresh, + setCollections: autocomplete.setCollections, + setContext: autocomplete.setContext, + setIsOpen: autocomplete.setIsOpen, + setQuery: autocomplete.setQuery, + setActiveItemId: autocomplete.setActiveItemId, + setStatus: autocomplete.setStatus, }); }); }); diff --git a/packages/autocomplete-core/src/__tests__/debug.test.ts b/packages/autocomplete-core/src/__tests__/debug.test.ts index 9dfbeabed2..38f2fdb1ba 100644 --- a/packages/autocomplete-core/src/__tests__/debug.test.ts +++ b/packages/autocomplete-core/src/__tests__/debug.test.ts @@ -25,9 +25,10 @@ describe('debug', () => { inputElement.focus(); inputElement.blur(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ isOpen: true }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ isOpen: true }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/defaultSelectedItemId.test.ts b/packages/autocomplete-core/src/__tests__/defaultSelectedItemId.test.ts index 4947051525..d058fcc0d2 100644 --- a/packages/autocomplete-core/src/__tests__/defaultSelectedItemId.test.ts +++ b/packages/autocomplete-core/src/__tests__/defaultSelectedItemId.test.ts @@ -17,12 +17,13 @@ describe('defaultActiveItemId', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + }), + }) + ); }); test('selects provided defaultActiveItemId on open (onInput)', () => { @@ -39,12 +40,13 @@ describe('defaultActiveItemId', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('selects defaultActiveItemId with openOnFocus on reset', () => { @@ -66,12 +68,13 @@ describe('defaultActiveItemId', () => { autocomplete.setActiveItemId(null); formElement.reset(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('selects defaultActiveItemId on focus', () => { @@ -88,12 +91,13 @@ describe('defaultActiveItemId', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('selects defaultActiveItemId when ArrowDown on the last', () => { @@ -114,12 +118,13 @@ describe('defaultActiveItemId', () => { userEvent.type(inputElement, '{arrowdown}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('selects defaultActiveItemId when ArrowUp on the first', () => { @@ -140,11 +145,12 @@ describe('defaultActiveItemId', () => { userEvent.type(inputElement, '{arrowup}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/getEnvironmentProps.test.ts b/packages/autocomplete-core/src/__tests__/getEnvironmentProps.test.ts index f21fd73255..ac23eea89d 100644 --- a/packages/autocomplete-core/src/__tests__/getEnvironmentProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getEnvironmentProps.test.ts @@ -86,13 +86,14 @@ describe('getEnvironmentProps', () => { }); window.document.dispatchEvent(customEvent); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + isOpen: false, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/getFormProps.test.ts b/packages/autocomplete-core/src/__tests__/getFormProps.test.ts index e4004be71a..a386f430b7 100644 --- a/packages/autocomplete-core/src/__tests__/getFormProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getFormProps.test.ts @@ -120,12 +120,13 @@ describe('getFormProps', () => { formProps.onSubmit(new Event('submit')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: false, + }), + }) + ); }); test('sets the activeItemId to null', () => { @@ -143,12 +144,13 @@ describe('getFormProps', () => { formProps.onSubmit(new Event('submit')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + }), + }) + ); }); test('sets the status to idle', () => { @@ -166,12 +168,13 @@ describe('getFormProps', () => { formProps.onSubmit(new Event('submit')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'idle', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'idle', + }), + }) + ); }); }); @@ -232,12 +235,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: false, + }), + }) + ); }); test('opens the panel with openOnFocus', () => { @@ -256,12 +260,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); test('sets the activeItemId to null without openOnFocus', () => { @@ -279,12 +284,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + }), + }) + ); }); test('sets the activeItemId to defaultActiveItemId with openOnFocus', () => { @@ -304,12 +310,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('sets the status to idle', () => { @@ -327,12 +334,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'idle', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'idle', + }), + }) + ); }); test('resets the query', () => { @@ -350,12 +358,13 @@ describe('getFormProps', () => { formProps.onReset(new Event('reset')); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - query: '', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + query: '', + }), + }) + ); }); }); }); diff --git a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts index 6174b299e0..157dc0b0d9 100644 --- a/packages/autocomplete-core/src/__tests__/getInputProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getInputProps.test.ts @@ -301,12 +301,13 @@ describe('getInputProps', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - query: 'a', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + query: 'a', + }), + }) + ); }); test('sets activeItemId to defaultActiveItemId', () => { @@ -318,12 +319,13 @@ describe('getInputProps', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); test('resets the state without query', () => { @@ -334,20 +336,22 @@ describe('getInputProps', () => { userEvent.type(inputElement, ''); - expect(onStateChange).not.toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'loading', - }), - }); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'idle', - collections: [], - isOpen: false, - }), - }); + expect(onStateChange).not.toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'loading', + }), + }) + ); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'idle', + collections: [], + isOpen: false, + }), + }) + ); }); test('sets the status to loading before fetching sources', () => { @@ -358,12 +362,13 @@ describe('getInputProps', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'loading', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'loading', + }), + }) + ); }); test('calls getSources', () => { @@ -434,22 +439,23 @@ describe('getInputProps', () => { await runAllMicroTasks(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'idle', - isOpen: true, - collections: [ - { - source: expect.any(Object), - items: [ - { label: '1', __autocomplete_id: 0 }, - { label: '2', __autocomplete_id: 1 }, - ], - }, - ], - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'idle', + isOpen: true, + collections: [ + { + source: expect.any(Object), + items: [ + { label: '1', __autocomplete_id: 0 }, + { label: '2', __autocomplete_id: 1 }, + ], + }, + ], + }), + }) + ); }); test('fetches sources that do not return collections closes panel', async () => { @@ -472,19 +478,20 @@ describe('getInputProps', () => { await runAllMicroTasks(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - status: 'idle', - isOpen: false, - collections: [ - { - source: expect.any(Object), - items: [], - }, - ], - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + status: 'idle', + isOpen: false, + collections: [ + { + source: expect.any(Object), + items: [], + }, + ], + }), + }) + ); }); test('calls onActive', async () => { @@ -822,13 +829,14 @@ describe('getInputProps', () => { inputElement.focus(); userEvent.type(inputElement, '{esc}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: false, - completion: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: false, + completion: null, + }), + }) + ); }); test('resets the state when panel is closed', () => { @@ -847,14 +855,15 @@ describe('getInputProps', () => { userEvent.type(inputElement, '{esc}'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - query: '', - status: 'idle', - collections: [], - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + query: '', + status: 'idle', + collections: [], + }), + }) + ); }); }); @@ -1471,12 +1480,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + }), + }) + ); }); test('to defaultActiveItemId value when set', () => { @@ -1488,12 +1498,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); }); @@ -1506,12 +1517,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: false, + }), + }) + ); }); test('to true when the query is set', () => { @@ -1525,12 +1537,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); test('to true when openOnFocus is true', () => { @@ -1542,12 +1555,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); test('to true when openOnFocus is true and the query is set', () => { @@ -1562,12 +1576,13 @@ describe('getInputProps', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); }); }); @@ -1584,13 +1599,14 @@ describe('getInputProps', () => { inputElement.focus(); inputElement.blur(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + isOpen: false, + }), + }) + ); }); test('does not reset activeItemId and isOpen when debug is true', () => { @@ -1605,13 +1621,14 @@ describe('getInputProps', () => { inputElement.focus(); inputElement.blur(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 1, - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 1, + isOpen: true, + }), + }) + ); }); test('does not reset activeItemId and isOpen on touch devices', () => { @@ -1630,13 +1647,14 @@ describe('getInputProps', () => { inputElement.focus(); inputElement.blur(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 1, - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 1, + isOpen: true, + }), + }) + ); }); }); @@ -1687,12 +1705,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: null, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: null, + }), + }) + ); }); test('to defaultActiveItemId value when set', () => { @@ -1709,12 +1728,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 1, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 1, + }), + }) + ); }); }); @@ -1732,12 +1752,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: false, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: false, + }), + }) + ); }); test('to true when the query is set', () => { @@ -1756,12 +1777,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); test('to true when openOnFocus is true', () => { @@ -1778,12 +1800,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); }); @@ -1801,12 +1824,13 @@ describe('getInputProps', () => { inputElement.click(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); }); }); diff --git a/packages/autocomplete-core/src/__tests__/getPanelProps.test.ts b/packages/autocomplete-core/src/__tests__/getPanelProps.test.ts index 316f6a3de6..a381da8641 100644 --- a/packages/autocomplete-core/src/__tests__/getPanelProps.test.ts +++ b/packages/autocomplete-core/src/__tests__/getPanelProps.test.ts @@ -32,11 +32,12 @@ describe('getPanelProps', () => { panelProps.onMouseLeave(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 0, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 0, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/getSources.test.ts b/packages/autocomplete-core/src/__tests__/getSources.test.ts index 6e7e70bbf5..b869b8009e 100644 --- a/packages/autocomplete-core/src/__tests__/getSources.test.ts +++ b/packages/autocomplete-core/src/__tests__/getSources.test.ts @@ -64,25 +64,26 @@ describe('getSources', () => { await runAllMicroTasks(); - expect(onStateChange).toHaveBeenCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - collections: expect.arrayContaining([ - expect.objectContaining({ - source: { - getItemInputValue: expect.any(Function), - getItemUrl: expect.any(Function), - getItems: expect.any(Function), - onActive: expect.any(Function), - onSelect: expect.any(Function), - templates: expect.objectContaining({ - item: expect.any(Function), - }), - }, - }), - ]), - }), - }); + expect(onStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + collections: expect.arrayContaining([ + expect.objectContaining({ + source: { + getItemInputValue: expect.any(Function), + getItemUrl: expect.any(Function), + getItems: expect.any(Function), + onActive: expect.any(Function), + onSelect: expect.any(Function), + templates: expect.objectContaining({ + item: expect.any(Function), + }), + }, + }), + ]), + }), + }) + ); }); test('concat getSources from plugins', async () => { diff --git a/packages/autocomplete-core/src/__tests__/initialState.test.ts b/packages/autocomplete-core/src/__tests__/initialState.test.ts index 97731287b0..5a8bd9a996 100644 --- a/packages/autocomplete-core/src/__tests__/initialState.test.ts +++ b/packages/autocomplete-core/src/__tests__/initialState.test.ts @@ -11,18 +11,19 @@ describe('initialState', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenCalledWith({ - prevState: { - activeItemId: null, - query: '', - completion: null, - collections: [], - isOpen: false, - status: 'idle', - context: {}, - }, - state: expect.anything(), - }); + expect(onStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + prevState: { + activeItemId: null, + query: '', + completion: null, + collections: [], + isOpen: false, + status: 'idle', + context: {}, + }, + }) + ); }); test('sets the initial state', () => { @@ -46,9 +47,10 @@ describe('initialState', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenCalledWith({ - prevState: initialState, - state: expect.anything(), - }); + expect(onStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + prevState: initialState, + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/onStateChange.test.ts b/packages/autocomplete-core/src/__tests__/onStateChange.test.ts new file mode 100644 index 0000000000..3f27b3ddfa --- /dev/null +++ b/packages/autocomplete-core/src/__tests__/onStateChange.test.ts @@ -0,0 +1,39 @@ +import { createAutocomplete } from '../createAutocomplete'; + +describe('onStateChange', () => { + test('gets called at any store change', () => { + const onStateChange = jest.fn(); + const autocomplete = createAutocomplete({ onStateChange }); + + autocomplete.setQuery('query'); + + expect(onStateChange).toHaveBeenCalledTimes(1); + expect(onStateChange).toHaveBeenCalledWith({ + prevState: { + activeItemId: null, + collections: [], + completion: null, + context: {}, + isOpen: false, + query: '', + status: 'idle', + }, + state: { + activeItemId: null, + collections: [], + completion: null, + context: {}, + isOpen: false, + query: 'query', + status: 'idle', + }, + refresh: autocomplete.refresh, + setActiveItemId: autocomplete.setActiveItemId, + setCollections: autocomplete.setCollections, + setContext: autocomplete.setContext, + setIsOpen: autocomplete.setIsOpen, + setQuery: autocomplete.setQuery, + setStatus: autocomplete.setStatus, + }); + }); +}); diff --git a/packages/autocomplete-core/src/__tests__/openOnFocus.test.ts b/packages/autocomplete-core/src/__tests__/openOnFocus.test.ts index 680b751b67..8e47d86a4e 100644 --- a/packages/autocomplete-core/src/__tests__/openOnFocus.test.ts +++ b/packages/autocomplete-core/src/__tests__/openOnFocus.test.ts @@ -28,12 +28,13 @@ describe('openOnFocus', () => { formElement.reset(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); test('sets defaultActiveItemId on reset', () => { @@ -45,12 +46,13 @@ describe('openOnFocus', () => { formElement.reset(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - activeItemId: 1, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + activeItemId: 1, + }), + }) + ); }); test('triggers a search on focus without query', () => { @@ -59,12 +61,13 @@ describe('openOnFocus', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - query: '', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + query: '', + }), + }) + ); }); test('calls getSources without query', () => { @@ -85,11 +88,12 @@ describe('openOnFocus', () => { inputElement.focus(); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/refresh.test.ts b/packages/autocomplete-core/src/__tests__/refresh.test.ts index 1db6fecbc6..ea36b4f2f9 100644 --- a/packages/autocomplete-core/src/__tests__/refresh.test.ts +++ b/packages/autocomplete-core/src/__tests__/refresh.test.ts @@ -22,12 +22,13 @@ describe('refresh', () => { refresh(); expect(getSources).toHaveBeenCalledTimes(1); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - query: 'a', - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + query: 'a', + }), + }) + ); }); test('leaves the next open state as provided', () => { @@ -45,11 +46,12 @@ describe('refresh', () => { refresh(); expect(getSources).toHaveBeenCalledTimes(1); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ - isOpen: true, - }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + isOpen: true, + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/setCollections.test.ts b/packages/autocomplete-core/src/__tests__/setCollections.test.ts index a4079c3f71..29cad3112f 100644 --- a/packages/autocomplete-core/src/__tests__/setCollections.test.ts +++ b/packages/autocomplete-core/src/__tests__/setCollections.test.ts @@ -25,22 +25,23 @@ describe('setCollections', () => { setCollections([createCollection([{ label: 'hi' }])]); expect(onStateChange).toHaveBeenCalledTimes(1); - expect(onStateChange).toHaveBeenCalledWith({ - prevState: expect.any(Object), - state: expect.objectContaining({ - collections: [ - { - items: [ - { - label: 'hi', - __autocomplete_id: 0, - }, - ], - source: expect.any(Object), - }, - ], - }), - }); + expect(onStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + collections: [ + { + items: [ + { + label: 'hi', + __autocomplete_id: 0, + }, + ], + source: expect.any(Object), + }, + ], + }), + }) + ); }); test('flattens the collections', () => { @@ -52,15 +53,16 @@ describe('setCollections', () => { setCollections([createCollection([[{ label: 'hi' }]])]); - expect(onStateChange).toHaveBeenCalledWith({ - prevState: expect.any(Object), - state: expect.objectContaining({ - collections: [ - expect.objectContaining({ - items: [{ label: 'hi', __autocomplete_id: 0 }], - }), - ], - }), - }); + expect(onStateChange).toHaveBeenCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ + collections: [ + expect.objectContaining({ + items: [{ label: 'hi', __autocomplete_id: 0 }], + }), + ], + }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts index e4dab7eb98..c2e91b470f 100644 --- a/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts +++ b/packages/autocomplete-core/src/__tests__/stallThreshold.test.ts @@ -28,24 +28,27 @@ describe('stallThreshold', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'loading' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'loading' }), + }) + ); await defer(() => {}, 300); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'stalled' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'stalled' }), + }) + ); await defer(() => {}, 200); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'idle' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'idle' }), + }) + ); }); test('allows custom stall threshold', async () => { @@ -73,30 +76,34 @@ describe('stallThreshold', () => { userEvent.type(inputElement, 'a'); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'loading' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'loading' }), + }) + ); await defer(() => {}, 300); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'loading' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'loading' }), + }) + ); await defer(() => {}, 100); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'stalled' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'stalled' }), + }) + ); await defer(() => {}, 100); - expect(onStateChange).toHaveBeenLastCalledWith({ - prevState: expect.anything(), - state: expect.objectContaining({ status: 'idle' }), - }); + expect(onStateChange).toHaveBeenLastCalledWith( + expect.objectContaining({ + state: expect.objectContaining({ status: 'idle' }), + }) + ); }); }); diff --git a/packages/autocomplete-core/src/createAutocomplete.ts b/packages/autocomplete-core/src/createAutocomplete.ts index e8f9a7af7f..187742f006 100644 --- a/packages/autocomplete-core/src/createAutocomplete.ts +++ b/packages/autocomplete-core/src/createAutocomplete.ts @@ -24,7 +24,7 @@ export function createAutocomplete< const subscribers: AutocompleteSubscribers = []; const props = getDefaultProps(options, subscribers); - const store = createStore(stateReducer, props); + const store = createStore(stateReducer, props, onStoreStateChange); const setters = getAutocompleteSetters({ store }); const propGetters = getPropGetters< @@ -34,6 +34,10 @@ export function createAutocomplete< TKeyboardEvent >({ props, refresh, store, ...setters }); + function onStoreStateChange({ prevState, state }) { + props.onStateChange({ prevState, state, refresh, ...setters }); + } + function refresh() { return onInput({ event: new Event('input'), @@ -49,6 +53,7 @@ export function createAutocomplete< props.plugins.forEach((plugin) => plugin.subscribe?.({ ...setters, + refresh, onSelect(fn) { subscribers.push({ onSelect: fn }); }, diff --git a/packages/autocomplete-core/src/createStore.ts b/packages/autocomplete-core/src/createStore.ts index cc82fa5385..c2197188b7 100644 --- a/packages/autocomplete-core/src/createStore.ts +++ b/packages/autocomplete-core/src/createStore.ts @@ -1,13 +1,23 @@ import { + AutocompleteState, AutocompleteStore, BaseItem, InternalAutocompleteOptions, Reducer, } from './types'; +type OnStoreStateChange = ({ + prevState, + state, +}: { + prevState: AutocompleteState; + state: AutocompleteState; +}) => void; + export function createStore( reducer: Reducer, - props: InternalAutocompleteOptions + props: InternalAutocompleteOptions, + onStoreStateChange: OnStoreStateChange ): AutocompleteStore { let state = props.initialState; @@ -23,7 +33,7 @@ export function createStore( payload, }); - props.onStateChange({ state, prevState }); + onStoreStateChange({ state, prevState }); }, }; } diff --git a/packages/autocomplete-core/src/types/AutocompleteOptions.ts b/packages/autocomplete-core/src/types/AutocompleteOptions.ts index 1e6085da09..9437fcdab6 100644 --- a/packages/autocomplete-core/src/types/AutocompleteOptions.ts +++ b/packages/autocomplete-core/src/types/AutocompleteOptions.ts @@ -33,6 +33,12 @@ export type InternalGetSources = ( params: GetSourcesParams ) => Promise>>; +interface OnStateChangeProps + extends AutocompleteScopeApi { + state: AutocompleteState; + prevState: AutocompleteState; +} + export interface AutocompleteOptions { /** * Whether to consider the experience in debug mode. @@ -54,10 +60,7 @@ export interface AutocompleteOptions { /** * Function called when the internal state changes. */ - onStateChange?(props: { - state: AutocompleteState; - prevState: AutocompleteState; - }): void; + onStateChange?(props: OnStateChangeProps): void; /** * The text that appears in the search box input when there is no query. */ @@ -137,10 +140,7 @@ export interface InternalAutocompleteOptions extends AutocompleteOptions { debug: boolean; id: string; - onStateChange(props: { - state: AutocompleteState; - prevState: AutocompleteState; - }): void; + onStateChange(props: OnStateChangeProps): void; placeholder: string; autoFocus: boolean; defaultActiveItemId: number | null; diff --git a/packages/autocomplete-core/src/types/AutocompletePlugin.ts b/packages/autocomplete-core/src/types/AutocompletePlugin.ts index d4dda1c46b..c1b355ed50 100644 --- a/packages/autocomplete-core/src/types/AutocompletePlugin.ts +++ b/packages/autocomplete-core/src/types/AutocompletePlugin.ts @@ -1,12 +1,11 @@ -import { BaseItem } from './AutocompleteApi'; +import { AutocompleteScopeApi, BaseItem } from './AutocompleteApi'; import { AutocompleteOptions } from './AutocompleteOptions'; -import { AutocompleteSetters } from './AutocompleteSetters'; import { OnSelectParams, OnHighlightParams } from './AutocompleteSource'; type PluginSubscriber = (params: TParams) => void; interface PluginSubscribeParams - extends AutocompleteSetters { + extends AutocompleteScopeApi { onSelect(fn: PluginSubscriber>): void; onActive(fn: PluginSubscriber>): void; } diff --git a/packages/autocomplete-js/src/types/AutocompleteOptions.ts b/packages/autocomplete-js/src/types/AutocompleteOptions.ts index 3618d5d0ef..b5c9d53c7d 100644 --- a/packages/autocomplete-js/src/types/AutocompleteOptions.ts +++ b/packages/autocomplete-js/src/types/AutocompleteOptions.ts @@ -1,4 +1,5 @@ import { + AutocompleteScopeApi, AutocompleteOptions as AutocompleteCoreOptions, BaseItem, GetSourcesParams, @@ -12,6 +13,12 @@ import { AutocompleteRenderer } from './AutocompleteRenderer'; import { AutocompleteSource } from './AutocompleteSource'; import { AutocompleteState } from './AutocompleteState'; +export interface OnStateChangeProps + extends AutocompleteScopeApi { + state: AutocompleteState; + prevState: AutocompleteState; +} + export interface AutocompleteOptions extends AutocompleteCoreOptions, Partial> { @@ -59,10 +66,7 @@ export interface AutocompleteOptions render?: AutocompleteRender; renderEmpty?: AutocompleteRender; initialState?: Partial>; - onStateChange?(props: { - state: AutocompleteState; - prevState: AutocompleteState; - }): void; + onStateChange?(props: OnStateChangeProps): void; /** * Custom renderer. */