Skip to content

Commit 0a33b44

Browse files
fix(js): rely on environment instead of global object (#572)
* fix(js): rely on `environment` instead of global object (#572) * chore(eslint): disallow `window` and `document` usage Co-authored-by: François Chalifour <[email protected]>
1 parent f2154c8 commit 0a33b44

File tree

14 files changed

+135
-49
lines changed

14 files changed

+135
-49
lines changed

.eslintrc.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ module.exports = {
7373
'eslint-comments/no-unlimited-disable': OFF,
7474
},
7575
},
76+
{
77+
files: [
78+
'packages/autocomplete-core/**/*',
79+
'packages/autocomplete-js/**/*',
80+
],
81+
rules: {
82+
'no-restricted-globals': [
83+
'error',
84+
{
85+
name: 'window',
86+
message: 'Use the `environment` param to access this property.',
87+
},
88+
{
89+
name: 'document',
90+
message: 'Use the `environment` param to access this property.',
91+
},
92+
],
93+
},
94+
},
95+
{
96+
files: ['**/__tests__/**'],
97+
rules: {
98+
'no-restricted-globals': OFF,
99+
},
100+
},
76101
{
77102
files: ['**/rollup.config.js', 'stories/**/*', '**/__tests__/**'],
78103
rules: {

bundlesize.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
},
77
{
88
"path": "packages/autocomplete-js/dist/umd/index.production.js",
9-
"maxSize": "15.25 kB"
9+
"maxSize": "15.5 kB"
1010
},
1111
{
1212
"path": "packages/autocomplete-preset-algolia/dist/umd/index.production.js",

packages/autocomplete-core/src/getDefaultProps.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@ export function getDefaultProps<TItem extends BaseItem>(
1616
props: AutocompleteOptions<TItem>,
1717
pluginSubscribers: AutocompleteSubscribers<TItem>
1818
): InternalAutocompleteOptions<TItem> {
19+
/* eslint-disable no-restricted-globals */
1920
const environment: AutocompleteEnvironment = (typeof window !== 'undefined'
2021
? window
2122
: {}) as typeof window;
23+
/* eslint-enable no-restricted-globals */
2224
const plugins = props.plugins || [];
2325

2426
return {

packages/autocomplete-js/src/autocomplete.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ export function autocomplete<TItem extends BaseItem>(
112112
autocomplete: autocomplete.value,
113113
autocompleteScopeApi,
114114
classNames: props.value.renderer.classNames,
115+
environment: props.value.core.environment,
115116
isDetached: isDetached.value,
116117
placeholder: props.value.core.placeholder,
117118
propGetters,
@@ -237,7 +238,7 @@ export function autocomplete<TItem extends BaseItem>(
237238
// We scroll to the top of the panel whenever the query changes (i.e. new
238239
// results come in) so that users don't have to.
239240
if (state.query !== prevState.query) {
240-
const scrollablePanels = document.querySelectorAll(
241+
const scrollablePanels = props.value.core.environment.document.querySelectorAll(
241242
'.aa-Panel--scrollable'
242243
);
243244
scrollablePanels.forEach((scrollablePanel) => {
@@ -336,19 +337,27 @@ export function autocomplete<TItem extends BaseItem>(
336337

337338
function setIsModalOpen(value: boolean) {
338339
requestAnimationFrame(() => {
339-
const prevValue = document.body.contains(dom.value.detachedOverlay);
340+
const prevValue = props.value.core.environment.document.body.contains(
341+
dom.value.detachedOverlay
342+
);
340343

341344
if (value === prevValue) {
342345
return;
343346
}
344347

345348
if (value) {
346-
document.body.appendChild(dom.value.detachedOverlay);
347-
document.body.classList.add('aa-Detached');
349+
props.value.core.environment.document.body.appendChild(
350+
dom.value.detachedOverlay
351+
);
352+
props.value.core.environment.document.body.classList.add('aa-Detached');
348353
dom.value.input.focus();
349354
} else {
350-
document.body.removeChild(dom.value.detachedOverlay);
351-
document.body.classList.remove('aa-Detached');
355+
props.value.core.environment.document.body.removeChild(
356+
dom.value.detachedOverlay
357+
);
358+
props.value.core.environment.document.body.classList.remove(
359+
'aa-Detached'
360+
);
352361
autocomplete.value.setQuery('');
353362
autocomplete.value.refresh();
354363
}

packages/autocomplete-js/src/createAutocompleteDom.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import {
22
AutocompleteApi as AutocompleteCoreApi,
3+
AutocompleteEnvironment,
34
AutocompleteScopeApi,
45
BaseItem,
56
} from '@algolia/autocomplete-core';
67

7-
import { createDomElement } from './createDomElement';
88
import { ClearIcon, Input, LoadingIcon, SearchIcon } from './elements';
9+
import { getCreateDomElement } from './getCreateDomElement';
910
import {
1011
AutocompleteClassNames,
1112
AutocompleteDom,
@@ -18,6 +19,7 @@ type CreateDomProps<TItem extends BaseItem> = {
1819
autocomplete: AutocompleteCoreApi<TItem>;
1920
autocompleteScopeApi: AutocompleteScopeApi<TItem>;
2021
classNames: AutocompleteClassNames;
22+
environment: AutocompleteEnvironment;
2123
isDetached: boolean;
2224
placeholder?: string;
2325
propGetters: AutocompletePropGetters<TItem>;
@@ -29,12 +31,15 @@ export function createAutocompleteDom<TItem extends BaseItem>({
2931
autocomplete,
3032
autocompleteScopeApi,
3133
classNames,
34+
environment,
3235
isDetached,
3336
placeholder = 'Search',
3437
propGetters,
3538
setIsModalOpen,
3639
state,
3740
}: CreateDomProps<TItem>): AutocompleteDom {
41+
const createDomElement = getCreateDomElement(environment);
42+
3843
const rootProps = propGetters.getRootProps({
3944
state,
4045
props: autocomplete.getRootProps({}),
@@ -68,7 +73,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
6873
class: classNames.submitButton,
6974
type: 'submit',
7075
title: 'Submit',
71-
children: [SearchIcon({})],
76+
children: [SearchIcon({ environment })],
7277
});
7378
const label = createDomElement('label', {
7479
class: classNames.label,
@@ -79,15 +84,16 @@ export function createAutocompleteDom<TItem extends BaseItem>({
7984
class: classNames.clearButton,
8085
type: 'reset',
8186
title: 'Clear',
82-
children: [ClearIcon({})],
87+
children: [ClearIcon({ environment })],
8388
});
8489
const loadingIndicator = createDomElement('div', {
8590
class: classNames.loadingIndicator,
86-
children: [LoadingIcon({})],
91+
children: [LoadingIcon({ environment })],
8792
});
8893

8994
const input = Input({
9095
class: classNames.input,
96+
environment,
9197
state,
9298
getInputProps: propGetters.getInputProps,
9399
getInputPropsCore: autocomplete.getInputProps,
@@ -142,7 +148,7 @@ export function createAutocompleteDom<TItem extends BaseItem>({
142148
if (isDetached) {
143149
const detachedSearchButtonIcon = createDomElement('div', {
144150
class: classNames.detachedSearchButtonIcon,
145-
children: [SearchIcon({})],
151+
children: [SearchIcon({ environment })],
146152
});
147153
const detachedSearchButtonPlaceholder = createDomElement('div', {
148154
class: classNames.detachedSearchButtonPlaceholder,

packages/autocomplete-js/src/createDomElement.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.

packages/autocomplete-js/src/elements/ClearIcon.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
1+
import { AutocompleteEnvironment } from '@algolia/autocomplete-core';
2+
13
import { AutocompleteElement } from '../types/AutocompleteElement';
24

3-
export const ClearIcon: AutocompleteElement<{}, SVGSVGElement> = () => {
4-
const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
5+
export const ClearIcon: AutocompleteElement<
6+
{ environment: AutocompleteEnvironment },
7+
SVGSVGElement
8+
> = ({ environment }) => {
9+
const element = environment.document.createElementNS(
10+
'http://www.w3.org/2000/svg',
11+
'svg'
12+
);
513
element.setAttribute('class', 'aa-ClearIcon');
614
element.setAttribute('viewBox', '0 0 24 24');
715
element.setAttribute('width', '18');
816
element.setAttribute('height', '18');
917
element.setAttribute('fill', 'currentColor');
1018

11-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
19+
const path = environment.document.createElementNS(
20+
'http://www.w3.org/2000/svg',
21+
'path'
22+
);
1223
path.setAttribute(
1324
'd',
1425
'M5.293 6.707l5.293 5.293-5.293 5.293c-0.391 0.391-0.391 1.024 0 1.414s1.024 0.391 1.414 0l5.293-5.293 5.293 5.293c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414l-5.293-5.293 5.293-5.293c0.391-0.391 0.391-1.024 0-1.414s-1.024-0.391-1.414 0l-5.293 5.293-5.293-5.293c-0.391-0.391-1.024-0.391-1.414 0s-0.391 1.024 0 1.414z'

packages/autocomplete-js/src/elements/Input.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import {
22
AutocompleteApi as AutocompleteCoreApi,
3+
AutocompleteEnvironment,
34
AutocompleteScopeApi,
45
} from '@algolia/autocomplete-core';
56

6-
import { createDomElement } from '../createDomElement';
7+
import { getCreateDomElement } from '../getCreateDomElement';
78
import { AutocompletePropGetters, AutocompleteState } from '../types';
89
import { AutocompleteElement } from '../types/AutocompleteElement';
910
import { setProperties } from '../utils';
1011

1112
type InputProps = {
1213
autocompleteScopeApi: AutocompleteScopeApi<any>;
14+
environment: AutocompleteEnvironment;
1315
getInputProps: AutocompletePropGetters<any>['getInputProps'];
1416
getInputPropsCore: AutocompleteCoreApi<any>['getInputProps'];
1517
onDetachedEscape?(): void;
@@ -18,13 +20,15 @@ type InputProps = {
1820

1921
export const Input: AutocompleteElement<InputProps, HTMLInputElement> = ({
2022
autocompleteScopeApi,
23+
environment,
2124
classNames,
2225
getInputProps,
2326
getInputPropsCore,
2427
onDetachedEscape,
2528
state,
2629
...props
2730
}) => {
31+
const createDomElement = getCreateDomElement(environment);
2832
const element = createDomElement('input', props);
2933
const inputProps = getInputProps({
3034
state,

packages/autocomplete-js/src/elements/LoadingIcon.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1+
import { AutocompleteEnvironment } from '@algolia/autocomplete-core';
2+
13
import { AutocompleteElement } from '../types/AutocompleteElement';
24

3-
export const LoadingIcon: AutocompleteElement<{}, SVGSVGElement> = () => {
4-
const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
5+
export const LoadingIcon: AutocompleteElement<
6+
{ environment: AutocompleteEnvironment },
7+
SVGSVGElement
8+
> = ({ environment }) => {
9+
const element = environment.document.createElementNS(
10+
'http://www.w3.org/2000/svg',
11+
'svg'
12+
);
513
element.setAttribute('class', 'aa-LoadingIcon');
614
element.setAttribute('viewBox', '0 0 100 100');
715
element.setAttribute('width', '20');

packages/autocomplete-js/src/elements/SearchIcon.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
1+
import { AutocompleteEnvironment } from '@algolia/autocomplete-core';
2+
13
import { AutocompleteElement } from '../types/AutocompleteElement';
24

3-
export const SearchIcon: AutocompleteElement<{}, SVGSVGElement> = () => {
4-
const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
5+
export const SearchIcon: AutocompleteElement<
6+
{ environment: AutocompleteEnvironment },
7+
SVGSVGElement
8+
> = ({ environment }) => {
9+
const element = environment.document.createElementNS(
10+
'http://www.w3.org/2000/svg',
11+
'svg'
12+
);
513
element.setAttribute('class', 'aa-SubmitIcon');
614
element.setAttribute('viewBox', '0 0 24 24');
715
element.setAttribute('width', '20');
816
element.setAttribute('height', '20');
917
element.setAttribute('fill', 'currentColor');
1018

11-
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
19+
const path = environment.document.createElementNS(
20+
'http://www.w3.org/2000/svg',
21+
'path'
22+
);
1223
path.setAttribute(
1324
'd',
1425
'M16.041 15.856c-0.034 0.026-0.067 0.055-0.099 0.087s-0.060 0.064-0.087 0.099c-1.258 1.213-2.969 1.958-4.855 1.958-1.933 0-3.682-0.782-4.95-2.050s-2.050-3.017-2.050-4.95 0.782-3.682 2.050-4.95 3.017-2.050 4.95-2.050 3.682 0.782 4.95 2.050 2.050 3.017 2.050 4.95c0 1.886-0.745 3.597-1.959 4.856zM21.707 20.293l-3.675-3.675c1.231-1.54 1.968-3.493 1.968-5.618 0-2.485-1.008-4.736-2.636-6.364s-3.879-2.636-6.364-2.636-4.736 1.008-6.364 2.636-2.636 3.879-2.636 6.364 1.008 4.736 2.636 6.364 3.879 2.636 6.364 2.636c2.125 0 4.078-0.737 5.618-1.968l3.675 3.675c0.391 0.391 1.024 0.391 1.414 0s0.391-1.024 0-1.414z'

0 commit comments

Comments
 (0)