Skip to content

Commit

Permalink
fix(js): provide fallback method for addEventListener on media quer…
Browse files Browse the repository at this point in the history
…ies (#600)
  • Loading branch information
shortcuts authored Jun 11, 2021
1 parent 3e78566 commit 760f8e7
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 53 deletions.
17 changes: 3 additions & 14 deletions packages/autocomplete-js/src/__tests__/autocomplete.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as autocompleteShared from '@algolia/autocomplete-shared';
import { fireEvent, waitFor } from '@testing-library/dom';

import { castToJestMock } from '../../../../test/utils';
import { castToJestMock, createMatchMedia } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';

jest.mock('@algolia/autocomplete-shared', () => {
Expand Down Expand Up @@ -210,27 +210,16 @@ describe('autocomplete-js', () => {
initialNbCalls
);

const originalMatchMedia = window.matchMedia;

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(),
})),
value: createMatchMedia({ matches: true }),
});

update({ detachedMediaQuery: '' });

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

expect(autocompleteShared.generateAutocompleteId).toHaveBeenCalledTimes(
Expand Down
16 changes: 3 additions & 13 deletions packages/autocomplete-js/src/__tests__/detached.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import { fireEvent, waitFor } from '@testing-library/dom';

import { createMatchMedia } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';

describe('detached', () => {
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(),
})),
value: createMatchMedia({ matches: true }),
});
});

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
import { createMatchMedia, createSource } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';

describe('detachedMediaQuery', () => {
test.todo('tests');
afterAll(() => {
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: createMatchMedia({}),
});
});

test('falls back to the deprecated `addListener` if `addEventListener` is undefined', () => {
const addListener = jest.fn();

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: createMatchMedia({
matches: true,
addListener,
addEventListener: undefined,
}),
});

const container = document.createElement('div');
document.body.appendChild(container);
autocomplete<{ label: string }>({
id: 'autocomplete',
detachedMediaQuery: '',
container,
getSources() {
return [
{
...createSource({}),
templates: {
item({ item }) {
return item.label;
},
},
},
];
},
});

expect(addListener).toHaveBeenCalledTimes(1);
});
});
16 changes: 3 additions & 13 deletions packages/autocomplete-js/src/__tests__/translations.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { waitFor } from '@testing-library/dom';

import { createMatchMedia } from '../../../../test/utils';
import { autocomplete } from '../autocomplete';

describe('translations', () => {
Expand Down Expand Up @@ -46,28 +47,17 @@ describe('translations', () => {
});

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(),
})),
value: createMatchMedia({ matches: true }),
});
});

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

Expand Down
13 changes: 11 additions & 2 deletions packages/autocomplete-js/src/autocomplete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,10 +301,19 @@ export function autocomplete<TItem extends BaseItem>(

toggleModalClassname(isModalDetachedMql.matches);

isModalDetachedMql.addEventListener('change', onChange);
// Prior to Safari 14, `MediaQueryList` isn't based on `EventTarget`,
// so we must use `addListener` and `removeListener` to observe media query lists.
// See https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList/addListener
const hasModernEventListener = Boolean(isModalDetachedMql.addEventListener);

hasModernEventListener
? isModalDetachedMql.addEventListener('change', onChange)
: isModalDetachedMql.addListener(onChange);

return () => {
isModalDetachedMql.removeEventListener('change', onChange);
hasModernEventListener
? isModalDetachedMql.removeEventListener('change', onChange)
: isModalDetachedMql.removeListener(onChange);
};
});

Expand Down
13 changes: 3 additions & 10 deletions scripts/jest/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { createMatchMedia } from '../../test/utils';

import { toWarnDev } from './matchers';

expect.extend({ toWarnDev });
Expand All @@ -6,14 +8,5 @@ global.console.warn = jest.fn();

Object.defineProperty(window, 'matchMedia', {
writable: true,
value: jest.fn((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
})),
value: createMatchMedia({}),
});
24 changes: 24 additions & 0 deletions test/utils/createMatchMedia.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
type MatchMediaProps = Partial<{
matches: boolean;
media: string;
onchange: null;
addListener: jest.Mock;
removeListener: jest.Mock;
addEventListener: jest.Mock;
removeEventListener: jest.Mock;
dispatchEvent: jest.Mock;
}>;

export const createMatchMedia = (props: MatchMediaProps) => {
return jest.fn((query) => ({
matches: false,
media: query,
onchange: null,
addListener: jest.fn(),
removeListener: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
...props,
}));
};
1 change: 1 addition & 0 deletions test/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './castToJestMock';
export * from './createApiResponse';
export * from './createCollection';
export * from './createMatchMedia';
export * from './createNavigator';
export * from './createPlayground';
export * from './createScopeApi';
Expand Down

0 comments on commit 760f8e7

Please sign in to comment.