Skip to content

Commit

Permalink
feat(sourceId): Add sourceId to provide `data-autocomplete-source-i…
Browse files Browse the repository at this point in the history
…d` on `section` source container (#429)

* Implements required source identifier `sourceId` used as value for the `data-autocomplete-source-id` attribute of the source `section` container

* Update docs

* Update tests

* add reference to `sourceId` and `data-autocomplete-source-id` in autocomplete-js

Co-authored-by: François Chalifour <[email protected]>
  • Loading branch information
shortcuts and francoischalifour authored Feb 5, 2021
1 parent 18d48c4 commit ce35fea
Show file tree
Hide file tree
Showing 21 changed files with 135 additions and 16 deletions.
1 change: 1 addition & 0 deletions examples/js/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ autocomplete({

return [
{
sourceId: 'products',
getItems() {
return getAlgoliaHits<Product>({
searchClient,
Expand Down
1 change: 1 addition & 0 deletions examples/js/shortcutsPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const shortcutsPlugin: AutocompletePlugin<DarkModeItem> = {

return [
{
sourceId: 'shortcutsPlugin',
getItems() {
return [
{
Expand Down
6 changes: 3 additions & 3 deletions packages/autocomplete-core/src/__tests__/concurrency.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import userEvent from '@testing-library/user-event';

import { defer } from '../../../../test/utils';
import { createSource, defer } from '../../../../test/utils';
import { createAutocomplete } from '../createAutocomplete';

describe.skip('concurrency', () => {
Expand All @@ -14,11 +14,11 @@ describe.skip('concurrency', () => {

return defer(() => {
return [
{
createSource({
getItems() {
return [{ label: query }];
},
},
}),
];
}, delays[deferCount]);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ describe('getInputProps', () => {
{ label: '2', url: '#2' },
],
source: {
sourceId: expect.any(String),
getItemInputValue: expect.any(Function),
getItemUrl: expect.any(Function),
getItems: expect.any(Function),
Expand Down
4 changes: 4 additions & 0 deletions packages/autocomplete-core/src/__tests__/getSources.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ describe('getSources', () => {
getSources: () => {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -78,6 +79,7 @@ describe('getSources', () => {
templates: expect.objectContaining({
item: expect.any(Function),
}),
sourceId: expect.any(String),
},
}),
]),
Expand All @@ -92,6 +94,7 @@ describe('getSources', () => {
getSources: () => {
return [
{
sourceId: 'pluginSource',
getItems() {
return [];
},
Expand All @@ -107,6 +110,7 @@ describe('getSources', () => {
getSources: () => {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down
10 changes: 5 additions & 5 deletions packages/autocomplete-core/src/__tests__/stallThreshold.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import userEvent from '@testing-library/user-event';

import { defer } from '../../../../test/utils';
import { createSource, defer } from '../../../../test/utils';
import { createAutocomplete } from '../createAutocomplete';

describe('stallThreshold', () => {
Expand All @@ -11,11 +11,11 @@ describe('stallThreshold', () => {
getSources() {
return defer(() => {
return [
{
createSource({
getItems() {
return [{ label: '1' }, { label: 2 }];
},
},
}),
];
}, 500);
},
Expand Down Expand Up @@ -59,11 +59,11 @@ describe('stallThreshold', () => {
getSources() {
return defer(() => {
return [
{
createSource({
getItems() {
return [{ label: '1' }, { label: 2 }];
},
},
}),
];
}, 500);
},
Expand Down
4 changes: 4 additions & 0 deletions packages/autocomplete-core/src/types/AutocompleteSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ export interface AutocompleteSource<TItem extends BaseItem> {
* You can trigger different behaviors based on the event `type`.
*/
onActive?(params: OnActiveParams<TItem>): void;
/**
* Identifier for the source.
*/
sourceId: string;
}

export type InternalAutocompleteSource<TItem extends BaseItem> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getNormalizedSources } from '../getNormalizedSources';

describe('getNormalizedSources', () => {
test('returns a promise of sources', async () => {
const getSources = () => [{ getItems: () => [] }];
const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }];
const params = {
query: '',
state: createState({
Expand All @@ -23,12 +23,17 @@ describe('getNormalizedSources', () => {
getItems: expect.any(Function),
onActive: expect.any(Function),
onSelect: expect.any(Function),
sourceId: 'testSource',
},
]);
});

test('filters out falsy sources', async () => {
const getSources = () => [{ getItems: () => [] }, false, undefined];
const getSources = () => [
{ sourceId: 'testSource', getItems: () => [] },
false,
undefined,
];
const params = {
query: '',
state: createState({
Expand All @@ -44,6 +49,7 @@ describe('getNormalizedSources', () => {
getItems: expect.any(Function),
onActive: expect.any(Function),
onSelect: expect.any(Function),
sourceId: 'testSource',
},
]);
});
Expand All @@ -64,8 +70,55 @@ describe('getNormalizedSources', () => {
);
});

test('with missing `sourceId` triggers invariant', async () => {
const getSources = () => [
{
getItems() {
return [];
},
templates: {
item() {},
},
},
];
const params = {
query: '',
state: createState({}),
...createScopeApi(),
};

// @ts-expect-error
await expect(getNormalizedSources(getSources, params)).rejects.toEqual(
new Error('[Autocomplete] A source must provide a `sourceId` string.')
);
});

test('with wrong `sourceId` type triggers invariant', async () => {
const getSources = () => [
{
sourceId: ['testSource'],
getItems() {
return [];
},
templates: {
item() {},
},
},
];
const params = {
query: '',
state: createState({}),
...createScopeApi(),
};

// @ts-expect-error
await expect(getNormalizedSources(getSources, params)).rejects.toEqual(
new Error('[Autocomplete] A source must provide a `sourceId` string.')
);
});

test('provides a default implementation for getItemInputValue which returns the query', async () => {
const getSources = () => [{ getItems: () => [] }];
const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }];
const params = {
query: '',
state: createState({
Expand All @@ -82,7 +135,7 @@ describe('getNormalizedSources', () => {
});

test('provides a default implementation for getItemUrl', async () => {
const getSources = () => [{ getItems: () => [] }];
const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }];
const params = {
query: '',
state: createState({}),
Expand All @@ -97,7 +150,7 @@ describe('getNormalizedSources', () => {
});

test('provides a default implementation for onSelect', async () => {
const getSources = () => [{ getItems: () => [] }];
const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }];
const params = {
query: '',
state: createState({}),
Expand All @@ -119,7 +172,7 @@ describe('getNormalizedSources', () => {
});

test('provides a default implementation for onActive', async () => {
const getSources = () => [{ getItems: () => [] }];
const getSources = () => [{ sourceId: 'testSource', getItems: () => [] }];
const params = {
query: '',
state: createState({}),
Expand Down
5 changes: 5 additions & 0 deletions packages/autocomplete-core/src/utils/getNormalizedSources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ export function getNormalizedSources<TItem extends BaseItem>(
Boolean(maybeSource)
)
.map((source) => {
invariant(
typeof source.sourceId === 'string',
'A source must provide a `sourceId` string.'
);

const normalizedSource: InternalAutocompleteSource<TItem> = {
getItemInputValue({ state }) {
return state.query;
Expand Down
11 changes: 11 additions & 0 deletions packages/autocomplete-js/src/__tests__/autocomplete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down Expand Up @@ -167,6 +168,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -206,6 +208,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -245,6 +248,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -290,6 +294,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -338,6 +343,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [];
},
Expand Down Expand Up @@ -383,6 +389,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down Expand Up @@ -418,6 +425,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down Expand Up @@ -449,6 +457,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down Expand Up @@ -482,6 +491,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down Expand Up @@ -512,6 +522,7 @@ describe('autocomplete-js', () => {
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return [
{ label: 'Item 1' },
Expand Down
1 change: 1 addition & 0 deletions packages/autocomplete-js/src/__tests__/positioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const querySuggestionsFixturePlugin: AutocompletePlugin<
getSources() {
return [
{
sourceId: 'testSource',
getItems() {
return querySuggestions;
},
Expand Down
6 changes: 5 additions & 1 deletion packages/autocomplete-js/src/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ export function renderPanel<TItem extends BaseItem>(
dom.panel.classList.toggle('aa-Panel--stalled', state.status === 'stalled');

const sections = state.collections.map(({ source, items }, sourceIndex) => (
<section key={sourceIndex} className={classNames.source}>
<section
key={sourceIndex}
className={classNames.source}
data-autocomplete-source-id={source.sourceId}
>
{source.templates.header && (
<div className={classNames.sourceHeader}>
{source.templates.header({
Expand Down
12 changes: 11 additions & 1 deletion packages/autocomplete-js/src/types/AutocompleteSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,20 @@ type WithTemplates<TType, TItem extends BaseItem> = TType & {
templates: SourceTemplates<TItem>;
};

export interface AutocompleteCoreSourceWithDocs<TItem extends BaseItem>
extends AutocompleteCoreSource<TItem> {
/**
* Identifier for the source.
* It is used as value for the `data-autocomplete-source-id` attribute of the source `section` container.
*/
sourceId: string;
}

export type AutocompleteSource<TItem extends BaseItem> = WithTemplates<
AutocompleteCoreSource<TItem>,
AutocompleteCoreSourceWithDocs<TItem>,
TItem
>;

export type InternalAutocompleteSource<TItem extends BaseItem> = WithTemplates<
InternalAutocompleteCoreSource<TItem>,
TItem
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function createQuerySuggestionsPlugin<
getSources({ query, setQuery, refresh, state }) {
return [
{
sourceId: 'querySuggestionsPlugin',
getItemInputValue({ item }) {
return item.query;
},
Expand Down
Loading

0 comments on commit ce35fea

Please sign in to comment.