Skip to content

Commit

Permalink
feat(js): introduce Translations API (#581)
Browse files Browse the repository at this point in the history
Co-authored-by: François Chalifour <francoischalifour@users.noreply.github.com>
  • Loading branch information
shortcuts and francoischalifour committed May 11, 2021
1 parent 2c10ede commit 970ee6a
Show file tree
Hide file tree
Showing 8 changed files with 207 additions and 3 deletions.
27 changes: 27 additions & 0 deletions packages/autocomplete-js/src/__tests__/api.test.ts
Expand Up @@ -5,6 +5,10 @@ import { createCollection } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';

describe('api', () => {
afterEach(() => {
document.body.innerHTML = '';
});

describe('setActiveItemId', () => {
test('sets `activeItemId` value in the state', () => {
const onStateChange = jest.fn();
Expand Down Expand Up @@ -271,6 +275,29 @@ describe('api', () => {
).toBeInTheDocument();
});
});

test('overrides the default translations', () => {
const container = document.createElement('div');
document.body.appendChild(container);

const { update } = autocomplete<{ label: string }>({
container,
});

expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Submit');

update({
translations: {
submitButtonTitle: 'Envoyer',
},
});

expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Envoyer');
});
});

describe('destroy', () => {
Expand Down
147 changes: 147 additions & 0 deletions packages/autocomplete-js/src/__tests__/translations.test.ts
@@ -0,0 +1,147 @@
import { waitFor } from '@testing-library/dom';

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

describe('translations', () => {
afterEach(() => {
document.body.innerHTML = '';
});

describe('regular DOM', () => {
test('provides default translations', () => {
const container = document.createElement('div');
document.body.appendChild(container);

autocomplete<{ label: string }>({
container,
});

expect(
document.querySelector<HTMLButtonElement>('.aa-ClearButton')
).toHaveAttribute('title', 'Clear');
expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Submit');
});

test('allows custom translations', () => {
const container = document.createElement('div');
document.body.appendChild(container);

autocomplete<{ label: string }>({
container,
translations: {
clearButtonTitle: 'Effacer',
submitButtonTitle: 'Envoyer',
},
});

expect(
document.querySelector<HTMLButtonElement>('.aa-ClearButton')
).toHaveAttribute('title', 'Effacer');
expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Envoyer');
});
});

describe('detached DOM', () => {
const originalMatchMedia = window.matchMedia;

beforeAll(() => {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn((query) => ({
matches: true,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
});
});

afterAll(() => {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: originalMatchMedia,
});
});

test('provides default translations', async () => {
const container = document.createElement('div');
document.body.appendChild(container);

const { destroy } = autocomplete<{ label: string }>({
container,
detachedMediaQuery: '',
});

const searchButton = container.querySelector<HTMLButtonElement>(
'.aa-DetachedSearchButton'
);

searchButton.click();

await waitFor(() => {
expect(
document.querySelector('.aa-DetachedOverlay')
).toBeInTheDocument();
});

expect(
document.querySelector<HTMLButtonElement>('.aa-ClearButton')
).toHaveAttribute('title', 'Clear');
expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Submit');
expect(
document.querySelector<HTMLButtonElement>('.aa-DetachedCancelButton')
).toHaveTextContent('Cancel');

destroy();
});

test('allows custom translations', async () => {
const container = document.createElement('div');
document.body.appendChild(container);

const { destroy } = autocomplete({
container,
detachedMediaQuery: '',
translations: {
clearButtonTitle: 'Effacer',
detachedCancelButtonText: 'Annuler',
submitButtonTitle: 'Envoyer',
},
});

const searchButton = container.querySelector<HTMLButtonElement>(
'.aa-DetachedSearchButton'
);

searchButton.click();

await waitFor(() => {
expect(
document.querySelector('.aa-DetachedOverlay')
).toBeInTheDocument();
});

expect(
document.querySelector<HTMLButtonElement>('.aa-ClearButton')
).toHaveAttribute('title', 'Effacer');
expect(
document.querySelector<HTMLButtonElement>('.aa-SubmitButton')
).toHaveAttribute('title', 'Envoyer');
expect(
document.querySelector<HTMLButtonElement>('.aa-DetachedCancelButton')
).toHaveTextContent('Annuler');

destroy();
});
});
});
1 change: 1 addition & 0 deletions packages/autocomplete-js/src/autocomplete.ts
Expand Up @@ -118,6 +118,7 @@ export function autocomplete<TItem extends BaseItem>(
propGetters,
setIsModalOpen,
state: lastStateRef.current,
translations: props.value.renderer.translations,
})
);

Expand Down
9 changes: 6 additions & 3 deletions packages/autocomplete-js/src/createAutocompleteDom.ts
Expand Up @@ -12,6 +12,7 @@ import {
AutocompleteDom,
AutocompletePropGetters,
AutocompleteState,
AutocompleteTranslations,
} from './types';
import { setProperties } from './utils';

Expand All @@ -25,6 +26,7 @@ type CreateDomProps<TItem extends BaseItem> = {
propGetters: AutocompletePropGetters<TItem>;
setIsModalOpen(value: boolean): void;
state: AutocompleteState<TItem>;
translations: AutocompleteTranslations;
};

export function createAutocompleteDom<TItem extends BaseItem>({
Expand All @@ -37,6 +39,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
propGetters,
setIsModalOpen,
state,
translations,
}: CreateDomProps<TItem>): AutocompleteDom {
const createDomElement = getCreateDomElement(environment);

Expand Down Expand Up @@ -72,7 +75,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
const submitButton = createDomElement('button', {
class: classNames.submitButton,
type: 'submit',
title: 'Submit',
title: translations.submitButtonTitle,
children: [SearchIcon({ environment })],
});
const label = createDomElement('label', {
Expand All @@ -83,7 +86,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
const clearButton = createDomElement('button', {
class: classNames.clearButton,
type: 'reset',
title: 'Clear',
title: translations.clearButtonTitle,
children: [ClearIcon({ environment })],
});
const loadingIndicator = createDomElement('div', {
Expand Down Expand Up @@ -164,7 +167,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
});
const detachedCancelButton = createDomElement('button', {
class: classNames.detachedCancelButton,
textContent: 'Cancel',
textContent: translations.detachedCancelButtonText,
onClick() {
autocomplete.setIsOpen(false);
setIsModalOpen(false);
Expand Down
11 changes: 11 additions & 0 deletions packages/autocomplete-js/src/getDefaultOptions.ts
Expand Up @@ -21,6 +21,7 @@ import {
AutocompleteOptions,
AutocompleteRender,
AutocompleteRenderer,
AutocompleteTranslations,
} from './types';
import { getHTMLElement, mergeClassNames } from './utils';

Expand Down Expand Up @@ -82,6 +83,7 @@ export function getDefaultOptions<TItem extends BaseItem>(
renderer,
detachedMediaQuery,
components,
translations,
...core
} = options;

Expand All @@ -104,6 +106,11 @@ export function getDefaultOptions<TItem extends BaseItem>(
ReverseSnippet: createReverseSnippetComponent(defaultedRenderer),
Snippet: createSnippetComponent(defaultedRenderer),
};
const defaultTranslations: AutocompleteTranslations = {
clearButtonTitle: 'Clear',
detachedCancelButtonText: 'Cancel',
submitButtonTitle: 'Submit',
};

return {
renderer: {
Expand Down Expand Up @@ -136,6 +143,10 @@ export function getDefaultOptions<TItem extends BaseItem>(
...defaultComponents,
...components,
},
translations: {
...defaultTranslations,
...translations,
},
},
core: {
...core,
Expand Down
9 changes: 9 additions & 0 deletions packages/autocomplete-js/src/types/AutocompleteOptions.ts
Expand Up @@ -14,6 +14,7 @@ import { AutocompleteRender } from './AutocompleteRender';
import { AutocompleteRenderer } from './AutocompleteRenderer';
import { AutocompleteSource } from './AutocompleteSource';
import { AutocompleteState } from './AutocompleteState';
import { AutocompleteTranslations } from './AutocompleteTranslations';

export interface OnStateChangeProps<TItem extends BaseItem>
extends AutocompleteScopeApi<TItem> {
Expand Down Expand Up @@ -108,4 +109,12 @@ export interface AutocompleteOptions<TItem extends BaseItem>
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-components
*/
components?: PublicAutocompleteComponents;
/**
* A mapping of translation strings.
*
* Defaults to English values.
*
* @link https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-translations
*/
translations?: Partial<AutocompleteTranslations>;
}
@@ -0,0 +1,5 @@
export type AutocompleteTranslations = {
detachedCancelButtonText: string;
clearButtonTitle: string;
submitButtonTitle: string;
};
1 change: 1 addition & 0 deletions packages/autocomplete-js/src/types/index.ts
Expand Up @@ -10,4 +10,5 @@ export * from './AutocompleteRender';
export * from './AutocompleteRenderer';
export * from './AutocompleteSource';
export * from './AutocompleteState';
export * from './AutocompleteTranslations';
export * from './HighlightHitParams';

0 comments on commit 970ee6a

Please sign in to comment.