diff --git a/services/web/.eslintrc b/services/web/.eslintrc index fa9613ce20..2455feba86 100644 --- a/services/web/.eslintrc +++ b/services/web/.eslintrc @@ -111,6 +111,32 @@ "plugin:cypress/recommended" ] }, + { + // React component specific rules + // + "files": ["**/frontend/js/**/components/**/*.{js,ts,tsx}", "**/frontend/js/**/hooks/**/*.{js,ts,tsx}"], + "rules": { + // https://astexplorer.net/ + "no-restricted-syntax": [ + "error", + // prohibit direct calls to methods of window.location + { + "selector": "CallExpression[callee.object.object.name='window'][callee.object.property.name='location']", + "message": "Modify location via useLocation instead of calling window.location methods directly" + }, + // prohibit assignment to window.location + { + "selector": "AssignmentExpression[left.object.name='window'][left.property.name='location']", + "message": "Modify location via useLocation instead of calling window.location methods directly" + }, + // prohibit assignment to window.location.href + { + "selector": "AssignmentExpression[left.object.object.name='window'][left.object.property.name='location'][left.property.name='href']", + "message": "Modify location via useLocation instead of calling window.location methods directly" + } + ] + } + }, { // Frontend specific rules "files": ["**/frontend/js/**/*.{js,ts,tsx}", "**/frontend/stories/**/*.{js,ts,tsx}", "**/*.stories.{js,ts,tsx}", "**/test/frontend/**/*.{js,ts,tsx}", "**/test/frontend/components/**/*.spec.{js,ts,tsx}"], diff --git a/services/web/frontend/js/features/clone-project-modal/controllers/left-menu-clone-project-modal-controller.js b/services/web/frontend/js/features/clone-project-modal/controllers/left-menu-clone-project-modal-controller.js index 042fa2c07b..beaca13cd1 100644 --- a/services/web/frontend/js/features/clone-project-modal/controllers/left-menu-clone-project-modal-controller.js +++ b/services/web/frontend/js/features/clone-project-modal/controllers/left-menu-clone-project-modal-controller.js @@ -2,6 +2,7 @@ import App from '../../../base' import { react2angular } from 'react2angular' import EditorCloneProjectModalWrapper from '../components/editor-clone-project-modal-wrapper' import { rootContext } from '../../../shared/context/root-context' +import { assign } from '../../../shared/components/location' export default App.controller( 'LeftMenuCloneProjectModalController', @@ -21,7 +22,7 @@ export default App.controller( } $scope.openProject = project => { - window.location.assign(`/project/${project.project_id}`) + assign(`/project/${project.project_id}`) } } ) diff --git a/services/web/frontend/js/features/editor-left-menu/components/actions-copy-project.tsx b/services/web/frontend/js/features/editor-left-menu/components/actions-copy-project.tsx index 0bea62be06..6030b0ad1b 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/actions-copy-project.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/actions-copy-project.tsx @@ -1,8 +1,8 @@ import { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' -import { assign } from '../../../shared/components/location' import EditorCloneProjectModalWrapper from '../../clone-project-modal/components/editor-clone-project-modal-wrapper' import LeftMenuButton from './left-menu-button' +import { useLocation } from '../../../shared/hooks/use-location' type ProjectCopyResponse = { project_id: string @@ -11,12 +11,13 @@ type ProjectCopyResponse = { export default function ActionsCopyProject() { const [showModal, setShowModal] = useState(false) const { t } = useTranslation() + const location = useLocation() const openProject = useCallback( ({ project_id: projectId }: ProjectCopyResponse) => { - assign(`/project/${projectId}`) + location.assign(`/project/${projectId}`) }, - [] + [location] ) return ( diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.js b/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.js index 1cf6f66b55..0c20abfcc2 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.js +++ b/services/web/frontend/js/features/file-tree/components/file-tree-create/redirect-to-login.js @@ -2,12 +2,13 @@ import { useState, useEffect } from 'react' import PropTypes from 'prop-types' import { Trans } from 'react-i18next' import { useProjectContext } from '../../../../shared/context/project-context' +import { useLocation } from '../../../../shared/hooks/use-location' // handle "not-logged-in" errors by redirecting to the login page export default function RedirectToLogin() { const { _id: projectId } = useProjectContext(projectContextPropTypes) - const [secondsToRedirect, setSecondsToRedirect] = useState(10) + const location = useLocation() useEffect(() => { setSecondsToRedirect(10) @@ -16,7 +17,7 @@ export default function RedirectToLogin() { setSecondsToRedirect(value => { if (value === 0) { window.clearInterval(timer) - window.location.assign(`/login?redir=/project/${projectId}`) + location.assign(`/login?redir=/project/${projectId}`) return 0 } @@ -27,7 +28,7 @@ export default function RedirectToLogin() { return () => { window.clearInterval(timer) } - }, [projectId]) + }, [projectId, location]) return (

{t('generic_something_went_wrong')}

{t('please_refresh')}

- diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.js b/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.js index 4bfafad1fc..12dd467e1a 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.js +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-orphan-refresh-button.js @@ -1,14 +1,16 @@ import { Button } from 'react-bootstrap' import { useTranslation } from 'react-i18next' -import { memo } from 'react' +import { memo, useCallback } from 'react' import { buildUrlWithDetachRole } from '../../../shared/utils/url-helper' - -const redirect = function () { - window.location = buildUrlWithDetachRole(null).toString() -} +import { useLocation } from '../../../shared/hooks/use-location' function PdfOrphanRefreshButton() { const { t } = useTranslation() + const location = useLocation() + + const redirect = useCallback(() => { + location.assign(buildUrlWithDetachRole(null).toString()) + }, [location]) return (