diff --git a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx index e830d7ec1a..3a96bfc48d 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx @@ -36,8 +36,6 @@ import { DocId } from '../../../../../types/project-settings' import { Update } from '@/features/history/services/types/update' import { useDebugDiffTracker } from '../hooks/use-debug-diff-tracker' import { useEditorContext } from '@/shared/context/editor-context' -import useScopeValueSetterOnly from '@/shared/hooks/use-scope-value-setter-only' -import { BinaryFile } from '@/features/file-view/types/binary-file' import { convertFileRefToBinaryFile } from '@/features/ide-react/util/file-view' export interface GotoOffsetOptions { @@ -97,7 +95,7 @@ export const EditorManagerProvider: FC = ({ const { reportError, eventEmitter, projectId } = useIdeReactContext() const { setOutOfSync } = useEditorContext() const { socket, closeConnection, connectionState } = useConnectionContext() - const { view, setView } = useLayoutContext() + const { view, setView, setOpenFile } = useLayoutContext() const { showGenericMessageModal, genericModalVisible, showOutOfSyncModal } = useModalsContext() const { id: userId } = useUserContext() @@ -521,8 +519,6 @@ export const EditorManagerProvider: FC = ({ [fileTreeData, openDoc] ) - const [, setOpenFile] = useScopeValueSetterOnly('openFile') - const openFileWithId = useCallback( (fileRefId: string) => { const fileRef = findFileRefEntityById(fileTreeData, fileRefId) diff --git a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx index b46ac158c7..b4afd446da 100644 --- a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx @@ -11,8 +11,6 @@ import { import { useProjectContext } from '@/shared/context/project-context' import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' -import useScopeValueSetterOnly from '@/shared/hooks/use-scope-value-setter-only' -import { BinaryFile } from '@/features/file-view/types/binary-file' import { FileTreeDocumentFindResult, FileTreeFileRefFindResult, @@ -22,6 +20,7 @@ import { debugConsole } from '@/utils/debugging' import { convertFileRefToBinaryFile } from '@/features/ide-react/util/file-view' import { sendMB } from '@/infrastructure/event-tracking' import { FileRef } from '../../../../../types/file-ref' +import { useLayoutContext } from '@/shared/context/layout-context' const FileTreeOpenContext = createContext< | { @@ -43,7 +42,7 @@ export const FileTreeOpenProvider: FC = ({ const { eventEmitter, projectJoined } = useIdeReactContext() const { openDocWithId, currentDocumentId, openInitialDoc } = useEditorManagerContext() - const [, setOpenFile] = useScopeValueSetterOnly('openFile') + const { setOpenFile } = useLayoutContext() const [openEntity, setOpenEntity] = useState< FileTreeDocumentFindResult | FileTreeFileRefFindResult | null >(null) diff --git a/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx b/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx index 51ecbdc6c9..63734d91aa 100644 --- a/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx @@ -8,7 +8,6 @@ import React, { useCallback, } from 'react' import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store' -import populateLayoutScope from '@/features/ide-react/scope-adapters/layout-context-adapter' import { IdeProvider } from '@/shared/context/ide-context' import { createIdeEventEmitter, @@ -67,7 +66,6 @@ export function createReactScopeValueStore(projectId: string) { // necessary values in the store, but this is simpler for now populateIdeReactScope(scopeStore) populateEditorScope(scopeStore, projectId) - populateLayoutScope(scopeStore) populateProjectScope(scopeStore) populatePdfScope(scopeStore) diff --git a/services/web/frontend/js/features/ide-react/scope-adapters/layout-context-adapter.ts b/services/web/frontend/js/features/ide-react/scope-adapters/layout-context-adapter.ts deleted file mode 100644 index 88f252f70b..0000000000 --- a/services/web/frontend/js/features/ide-react/scope-adapters/layout-context-adapter.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ReactScopeValueStore } from '../scope-value-store/react-scope-value-store' -import getMeta from '@/utils/meta' - -const reviewPanelStorageKey = `ui.reviewPanelOpen.${getMeta('ol-project_id')}` - -export default function populateLayoutScope(store: ReactScopeValueStore) { - store.set('ui.view', 'editor') - store.set('openFile', null) - store.persisted('ui.chatOpen', false, 'ui.chatOpen') - store.persisted('ui.reviewPanelOpen', false, reviewPanelStorageKey) - store.set('ui.leftMenuShown', false) - store.set('ui.miniReviewPanelVisible', false) - store.set('ui.pdfLayout', 'sideBySide') -} diff --git a/services/web/frontend/js/shared/context/layout-context.tsx b/services/web/frontend/js/shared/context/layout-context.tsx index 050e679de7..c59efae270 100644 --- a/services/web/frontend/js/shared/context/layout-context.tsx +++ b/services/web/frontend/js/shared/context/layout-context.tsx @@ -9,7 +9,6 @@ import { FC, useState, } from 'react' -import useScopeValue from '../hooks/use-scope-value' import useDetachLayout from '../hooks/use-detach-layout' import localStorage from '../../infrastructure/local-storage' import getMeta from '../../utils/meta' @@ -21,6 +20,7 @@ import useEventListener from '@/shared/hooks/use-event-listener' import { isMac } from '@/shared/utils/os' import { sendSearchEvent } from '@/features/event-tracking/search-events' import { useRailContext } from '@/features/ide-redesign/contexts/rail-context' +import usePersistedState from '@/shared/hooks/use-persisted-state' export type IdeLayout = 'sideBySide' | 'flat' export type IdeView = 'editor' | 'file' | 'pdf' | 'history' @@ -55,6 +55,8 @@ export type LayoutContextValue = { pdfPreviewOpen: boolean projectSearchIsOpen: boolean setProjectSearchIsOpen: Dispatch> + openFile: BinaryFile | null + setOpenFile: Dispatch> } const debugPdfDetach = getMeta('ol-debugPdfDetach') @@ -70,10 +72,12 @@ function setLayoutInLocalStorage(pdfLayout: IdeLayout) { ) } +const reviewPanelStorageKey = `ui.reviewPanelOpen.${getMeta('ol-project_id')}` + export const LayoutProvider: FC = ({ children }) => { // what to show in the "flat" view (editor or pdf) - const [view, _setView] = useScopeValue('ui.view') - const [openFile] = useScopeValue('openFile') + const [view, _setView] = useState('editor') + const [openFile, setOpenFile] = useState(null) const historyToggleEmitter = useScopeEventEmitter('history:toggle', true) const { isOpen: railIsOpen, setIsOpen: setRailIsOpen } = useRailContext() const [prevRailIsOpen, setPrevRailIsOpen] = useState(railIsOpen) @@ -118,19 +122,23 @@ export const LayoutProvider: FC = ({ children }) => { ) // whether the chat pane is open - const [chatIsOpen, setChatIsOpen] = useScopeValue('ui.chatOpen') + const [chatIsOpen, setChatIsOpen] = usePersistedState( + 'ui.chatOpen', + false + ) // whether the review pane is open - const [reviewPanelOpen, setReviewPanelOpen] = - useScopeValue('ui.reviewPanelOpen') + const [reviewPanelOpen, setReviewPanelOpen] = usePersistedState( + reviewPanelStorageKey, + false + ) // whether the review pane is collapsed const [miniReviewPanelVisible, setMiniReviewPanelVisible] = - useScopeValue('ui.miniReviewPanelVisible') + useState(false) // whether the menu pane is open - const [leftMenuShown, setLeftMenuShown] = - useScopeValue('ui.leftMenuShown') + const [leftMenuShown, setLeftMenuShown] = useState(false) // whether the project search is open const [projectSearchIsOpen, setProjectSearchIsOpen] = useState(false) @@ -173,7 +181,7 @@ export const LayoutProvider: FC = ({ children }) => { ) // whether to display the editor and preview side-by-side or full-width ("flat") - const [pdfLayout, setPdfLayout] = useScopeValue('ui.pdfLayout') + const [pdfLayout, setPdfLayout] = useState('sideBySide') // whether stylesheet on theme is loading const [loadingStyleSheet, setLoadingStyleSheet] = useState(false) @@ -238,6 +246,7 @@ export const LayoutProvider: FC = ({ children }) => { changeLayout, chatIsOpen, leftMenuShown, + openFile, pdfLayout, pdfPreviewOpen, projectSearchIsOpen, @@ -247,6 +256,7 @@ export const LayoutProvider: FC = ({ children }) => { loadingStyleSheet, setChatIsOpen, setLeftMenuShown, + setOpenFile, setPdfLayout, setReviewPanelOpen, setMiniReviewPanelVisible, @@ -262,6 +272,7 @@ export const LayoutProvider: FC = ({ children }) => { changeLayout, chatIsOpen, leftMenuShown, + openFile, pdfLayout, pdfPreviewOpen, projectSearchIsOpen, @@ -271,6 +282,7 @@ export const LayoutProvider: FC = ({ children }) => { loadingStyleSheet, setChatIsOpen, setLeftMenuShown, + setOpenFile, setPdfLayout, setReviewPanelOpen, setMiniReviewPanelVisible, diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index 31f38c79f4..00ad7f7daa 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/services/web/frontend/stories/decorators/scope.tsx @@ -59,10 +59,6 @@ const project: Project = { const initialScope = { user, project, - ui: { - chatOpen: true, - pdfLayout: 'flat', - }, settings: { pdfViewer: 'js', syntaxValidation: true, diff --git a/services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx b/services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx index 808f97bd4b..72955f38e6 100644 --- a/services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx +++ b/services/web/test/frontend/components/editor-left-menu/editor-left-menu.spec.tsx @@ -56,14 +56,10 @@ describe('', function () { }) it('render full menu', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -110,14 +106,12 @@ describe('', function () { describe('download menu', function () { it('have a correct source & pdf download url', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) - + const scope = mockScope() cy.mount( - + ) @@ -142,14 +136,13 @@ describe('', function () { }, }) - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -184,14 +177,13 @@ describe('', function () { }, }).as('wordCount') - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -217,9 +209,6 @@ describe('', function () { }) const scope = mockScope({ - ui: { - leftMenuShown: true, - }, project: { members: [], owner: { @@ -234,7 +223,10 @@ describe('', function () { }) cy.mount( - + ) @@ -245,9 +237,6 @@ describe('', function () { it('shows git modal correctly', function () { const scope = mockScope({ - ui: { - leftMenuShown: true, - }, project: { owner: { _id: '123', @@ -259,7 +248,10 @@ describe('', function () { }) cy.mount( - + ) @@ -271,9 +263,6 @@ describe('', function () { it('shows git modal paywall correctly', function () { const scope = mockScope({ - ui: { - leftMenuShown: true, - }, project: { owner: { _id: '123', @@ -285,7 +274,10 @@ describe('', function () { }) cy.mount( - + ) @@ -304,14 +296,13 @@ describe('', function () { enabled: false, }).as('project-status') - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -335,14 +326,13 @@ describe('', function () { describe('settings menu', function () { it('shows compiler menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -369,14 +359,13 @@ describe('', function () { }) it('shows texlive version menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -410,14 +399,14 @@ describe('', function () { folders: [], } - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -451,14 +440,13 @@ describe('', function () { window.metaAttributesCache.set('ol-languages', languages) - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -475,14 +463,13 @@ describe('', function () { }) it('shows dictionary modal correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -493,14 +480,13 @@ describe('', function () { }) it('shows auto-complete menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -517,14 +503,13 @@ describe('', function () { }) it('shows auto-close brackets menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -541,14 +526,13 @@ describe('', function () { }) it('shows code check menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -579,14 +563,13 @@ describe('', function () { legacyEditorThemes ) - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -619,14 +602,13 @@ describe('', function () { }) it('shows overall theme menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -643,14 +625,13 @@ describe('', function () { }) it('shows keybindings menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -667,14 +648,13 @@ describe('', function () { }) it('shows font size menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -713,14 +693,13 @@ describe('', function () { }) it('shows font family menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -741,14 +720,13 @@ describe('', function () { }) it('shows line height menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -765,14 +743,13 @@ describe('', function () { }) it('shows pdf viewer menu correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -791,14 +768,13 @@ describe('', function () { describe('help menu', function () { it('shows hotkeys modal correctly', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -808,14 +784,13 @@ describe('', function () { }) it('shows correct url for documentation', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -828,14 +803,13 @@ describe('', function () { }) it('shows correct contact us modal', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() cy.mount( - + ) @@ -848,16 +822,13 @@ describe('', function () { describe('for anonymous users', function () { it('render minimal menu', function () { - const scope = mockScope({ - ui: { - leftMenuShown: true, - }, - }) + const scope = mockScope() + window.metaAttributesCache.set('ol-anonymous', true) Object.assign(getMeta('ol-ExposedSettings'), { ieeeBrandId: 123 }) cy.mount( - + ) diff --git a/services/web/test/frontend/components/editor-left-menu/scope.tsx b/services/web/test/frontend/components/editor-left-menu/scope.tsx index ba1764c359..4c58c01f53 100644 --- a/services/web/test/frontend/components/editor-left-menu/scope.tsx +++ b/services/web/test/frontend/components/editor-left-menu/scope.tsx @@ -47,10 +47,5 @@ export const mockScope = (scope?: Scope) => ({ }, }, hasLintingError: false, - ui: { - view: 'editor', - pdfLayout: 'sideBySide', - leftMenuShown: false, - }, ...scope, }) diff --git a/services/web/test/frontend/components/pdf-preview/scope.tsx b/services/web/test/frontend/components/pdf-preview/scope.tsx index bb4e4d9d1d..e174efbf73 100644 --- a/services/web/test/frontend/components/pdf-preview/scope.tsx +++ b/services/web/test/frontend/components/pdf-preview/scope.tsx @@ -17,8 +17,4 @@ export const mockScope = () => ({ }), }, hasLintingError: false, - ui: { - view: 'editor', - pdfLayout: 'sideBySide', - }, }) diff --git a/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx b/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx index 55a5213a09..f8ac817c36 100644 --- a/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx +++ b/services/web/test/frontend/features/editor-navigation-toolbar/components/layout-dropdown-button.test.jsx @@ -9,9 +9,11 @@ import * as eventTracking from '@/infrastructure/event-tracking' describe('', function () { let openStub let sendMBSpy - const defaultUi = { + + const defaultLayout = { pdfLayout: 'flat', view: 'pdf', + chatIsOpen: false, } beforeEach(function () { @@ -27,7 +29,9 @@ describe('', function () { it('should mark current layout option as selected', async function () { // Selected is aria-label, visually we show a checkmark - renderWithEditorContext(, { ui: defaultUi }) + renderWithEditorContext(, { + layoutContext: defaultLayout, + }) screen.getByRole('button', { name: 'Layout' }).click() @@ -69,7 +73,7 @@ describe('', function () { it('should not select any option in history view', async function () { // Selected is aria-label, visually we show a checkmark renderWithEditorContext(, { - ui: { ...defaultUi, view: 'history' }, + layoutContext: { ...defaultLayout, view: 'history' }, }) screen.getByRole('button', { name: 'Layout' }).click() @@ -112,9 +116,10 @@ describe('', function () { it('should treat file and editor views the same way', async function () { // Selected is aria-label, visually we show a checkmark renderWithEditorContext(, { - ui: { + layoutContext: { pdfLayout: 'flat', view: 'file', + chatIsOpen: false, }, }) @@ -161,7 +166,7 @@ describe('', function () { window.BroadcastChannel = originalBroadcastChannel || true // ensure that window.BroadcastChannel is truthy renderWithEditorContext(, { - ui: { ...defaultUi, view: 'editor' }, + layoutContext: { ...defaultLayout, view: 'editor' }, }) screen.getByRole('button', { name: 'Layout' }).click() @@ -192,7 +197,7 @@ describe('', function () { beforeEach(async function () { window.metaAttributesCache.set('ol-detachRole', 'detacher') renderWithEditorContext(, { - ui: { ...defaultUi, view: 'editor' }, + layoutContext: { ...defaultLayout, view: 'editor' }, }) screen.getByRole('button', { name: 'Layout' }).click() diff --git a/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx b/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx index 261d43017f..e87467ee30 100644 --- a/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx +++ b/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx @@ -93,6 +93,8 @@ const createInitialValue = () => pdfPreviewOpen: false, projectSearchIsOpen: true, setProjectSearchIsOpen: cy.stub(), + openFile: null, + setOpenFile: cy.stub(), }) satisfies LayoutContextValue const LayoutProvider: FC = ({ children }) => { diff --git a/services/web/test/frontend/features/history/components/change-list.spec.tsx b/services/web/test/frontend/features/history/components/change-list.spec.tsx index 763845db54..03c5a90592 100644 --- a/services/web/test/frontend/features/history/components/change-list.spec.tsx +++ b/services/web/test/frontend/features/history/components/change-list.spec.tsx @@ -14,11 +14,10 @@ import { withTestContainerErrorBoundary } from '../../../helpers/error-boundary' const TestContainerWithoutErrorBoundary: FC<{ component: React.ReactNode - scope: Record props: Record -}> = ({ component, scope, props }) => { +}> = ({ component, props }) => { return ( - +
{component}
@@ -34,17 +33,12 @@ const TestContainer = withTestContainerErrorBoundary( const mountWithEditorProviders = ( component: React.ReactNode, - scope: Record = {}, props: Record = {} ) => { - cy.mount() + cy.mount() } describe('change list (Bootstrap 5)', function () { - const scope = { - ui: { view: 'history', pdfLayout: 'sideBySide', chatOpen: true }, - } - const waitForData = () => { cy.wait('@updates') cy.wait('@labels') @@ -101,7 +95,8 @@ describe('change list (Bootstrap 5)', function () { describe('tags', function () { it('renders tags', function () { - mountWithEditorProviders(, scope, { + mountWithEditorProviders(, { + layoutContext: { view: 'history' }, user: { id: USER_ID, email: USER_EMAIL, @@ -173,13 +168,18 @@ describe('change list (Bootstrap 5)', function () { }) it('deletes tag', function () { - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: true, - }, - }) + mountWithEditorProviders( + , + + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: true, + }, + } + ) waitForData() cy.findByLabelText(/all history/i).click({ force: true }) @@ -235,7 +235,8 @@ describe('change list (Bootstrap 5)', function () { }) it('verifies that selecting the same list item will not trigger a new diff', function () { - mountWithEditorProviders(, scope, { + mountWithEditorProviders(, { + layoutContext: { view: 'history' }, user: { id: USER_ID, email: USER_EMAIL, @@ -257,13 +258,18 @@ describe('change list (Bootstrap 5)', function () { describe('all history', function () { beforeEach(function () { - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: true, - }, - }) + mountWithEditorProviders( + , + + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: true, + }, + } + ) waitForData() }) @@ -318,13 +324,18 @@ describe('change list (Bootstrap 5)', function () { describe('labels only', function () { beforeEach(function () { - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: true, - }, - }) + mountWithEditorProviders( + , + + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: true, + }, + } + ) waitForData() cy.findByLabelText(/labels/i).click({ force: true }) }) @@ -399,13 +410,18 @@ describe('change list (Bootstrap 5)', function () { describe('compare mode', function () { beforeEach(function () { - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: true, - }, - }) + mountWithEditorProviders( + , + + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: true, + }, + } + ) waitForData() }) @@ -435,13 +451,18 @@ describe('change list (Bootstrap 5)', function () { describe('dropdown', function () { beforeEach(function () { - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: true, - }, - }) + mountWithEditorProviders( + , + + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: true, + }, + } + ) waitForData() }) @@ -610,21 +631,18 @@ describe('change list (Bootstrap 5)', function () { }) it('shows non-owner paywall', function () { - const scope = { - ui: { - view: 'history', - pdfLayout: 'sideBySide', - chatOpen: true, - }, - } + mountWithEditorProviders( + , - mountWithEditorProviders(, scope, { - user: { - id: USER_ID, - email: USER_EMAIL, - isAdmin: false, - }, - }) + { + layoutContext: { view: 'history' }, + user: { + id: USER_ID, + email: USER_EMAIL, + isAdmin: false, + }, + } + ) waitForData() @@ -634,15 +652,8 @@ describe('change list (Bootstrap 5)', function () { }) it('shows owner paywall', function () { - const scope = { - ui: { - view: 'history', - pdfLayout: 'sideBySide', - chatOpen: true, - }, - } - - mountWithEditorProviders(, scope, { + mountWithEditorProviders(, { + layoutContext: { view: 'history' }, user: { id: USER_ID, email: USER_EMAIL, @@ -662,15 +673,8 @@ describe('change list (Bootstrap 5)', function () { }) it('shows all labels in free tier', function () { - const scope = { - ui: { - view: 'history', - pdfLayout: 'sideBySide', - chatOpen: true, - }, - } - - mountWithEditorProviders(, scope, { + mountWithEditorProviders(, { + layoutContext: { view: 'history' }, user: { id: USER_ID, email: USER_EMAIL, diff --git a/services/web/test/frontend/features/history/components/toolbar.spec.tsx b/services/web/test/frontend/features/history/components/toolbar.spec.tsx index f60beff7ce..bc2b518eae 100644 --- a/services/web/test/frontend/features/history/components/toolbar.spec.tsx +++ b/services/web/test/frontend/features/history/components/toolbar.spec.tsx @@ -5,14 +5,15 @@ import { Diff } from '../../../../../frontend/js/features/history/services/types import { EditorProviders } from '../../../helpers/editor-providers' import { FC } from 'react' import { withTestContainerErrorBoundary } from '../../../helpers/error-boundary' +import { LayoutContextValue } from '@/shared/context/layout-context' const TestContainerWithoutErrorBoundary: FC<{ - scope: Record + layoutContext: LayoutContextValue diff: Diff selection: HistoryContextValue['selection'] -}> = ({ scope, diff, selection }) => { +}> = ({ diff, selection, layoutContext }) => { return ( - +
@@ -27,10 +28,6 @@ const TestContainer = withTestContainerErrorBoundary( ) describe('history toolbar', function () { - const editorProvidersScope = { - ui: { view: 'history', pdfLayout: 'sideBySide', chatOpen: true }, - } - const diff: Diff = { binary: false, docDiff: { @@ -81,7 +78,7 @@ describe('history toolbar', function () { cy.mount( @@ -129,7 +126,7 @@ describe('history toolbar', function () { cy.mount( diff --git a/services/web/test/frontend/features/layout/components/switch-to-editor-button.spec.tsx b/services/web/test/frontend/features/layout/components/switch-to-editor-button.spec.tsx index 3cb8ea2fee..9efd500dff 100644 --- a/services/web/test/frontend/features/layout/components/switch-to-editor-button.spec.tsx +++ b/services/web/test/frontend/features/layout/components/switch-to-editor-button.spec.tsx @@ -4,7 +4,9 @@ import SwitchToEditorButton from '@/features/pdf-preview/components/switch-to-ed describe('', function () { it('shows button in full screen pdf layout', function () { cy.mount( - + ) @@ -15,7 +17,11 @@ describe('', function () { it('does not show button in split screen layout', function () { cy.mount( @@ -28,7 +34,13 @@ describe('', function () { window.metaAttributesCache.set('ol-detachRole', 'detacher') cy.mount( - + ) diff --git a/services/web/test/frontend/features/layout/components/switch-to-pdf-button.spec.tsx b/services/web/test/frontend/features/layout/components/switch-to-pdf-button.spec.tsx index 43fc1cabfc..04e20250d8 100644 --- a/services/web/test/frontend/features/layout/components/switch-to-pdf-button.spec.tsx +++ b/services/web/test/frontend/features/layout/components/switch-to-pdf-button.spec.tsx @@ -5,7 +5,7 @@ describe('', function () { it('shows button in full screen editor layout', function () { cy.mount( @@ -17,7 +17,11 @@ describe('', function () { it('does not show button in split screen layout', function () { cy.mount( @@ -31,7 +35,7 @@ describe('', function () { cy.mount( diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts index 621bdecd3c..1f78dcf93a 100644 --- a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts +++ b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts @@ -81,9 +81,6 @@ export const mockScope = ( write: true, ...permissions, }, - ui: { - reviewPanelOpen: false, - }, toggleReviewPanel: cy.stub(), toggleTrackChangesForEveryone: cy.stub(), refreshResolvedCommentsDropdown: cy.stub(() => sleep(1000)), diff --git a/services/web/test/frontend/helpers/editor-providers.jsx b/services/web/test/frontend/helpers/editor-providers.jsx index 1fe143a8e3..4f722525c7 100644 --- a/services/web/test/frontend/helpers/editor-providers.jsx +++ b/services/web/test/frontend/helpers/editor-providers.jsx @@ -3,7 +3,7 @@ import { merge } from 'lodash' import { SocketIOMock } from '@/ide/connection/SocketIoShim' import { IdeContext } from '@/shared/context/ide-context' -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { createReactScopeValueStore, IdeReactContext, @@ -12,6 +12,9 @@ import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter' import { ReactScopeEventEmitter } from '@/features/ide-react/scope-event-emitter/react-scope-event-emitter' import { ConnectionContext } from '@/features/ide-react/context/connection-context' import { ReactContextRoot } from '@/features/ide-react/context/react-context-root' +import useEventListener from '@/shared/hooks/use-event-listener' +import useDetachLayout from '@/shared/hooks/use-detach-layout' +import { LayoutContext } from '@/shared/context/layout-context' // these constants can be imported in tests instead of // using magic strings @@ -35,6 +38,22 @@ const defaultUserSettings = { mathPreview: true, } +/** + * @typedef {import('@/shared/context/layout-context').LayoutContextValue} LayoutContextValue + * @type Partial + */ +const layoutContextDefault = { + view: 'editor', + openFile: null, + chatIsOpen: true, // false in the application, true in tests + reviewPanelOpen: false, + miniReviewPanelVisible: false, + leftMenuShown: false, + projectSearchIsOpen: false, + pdfLayout: 'sideBySide', + loadingStyleSheet: false, +} + export function EditorProviders({ user = { id: USER_ID, email: USER_EMAIL }, projectId = PROJECT_ID, @@ -68,7 +87,8 @@ export function EditorProviders({ fileRefs: [], }, ], - ui = { view: 'editor', pdfLayout: 'sideBySide', chatOpen: true }, + /** @type {Partial} */ + layoutContext = layoutContextDefault, userSettings = {}, providers = {}, }) { @@ -110,7 +130,6 @@ export function EditorProviders({ imageName, compiler, }, - ui, permissionsLevel, }, defaultScope @@ -125,6 +144,7 @@ export function EditorProviders({ providers={{ ConnectionProvider: makeConnectionProvider(socket), IdeReactProvider: makeIdeReactProvider(scope, socket), + LayoutProvider: makeLayoutProvider(layoutContext), ...providers, }} > @@ -183,7 +203,6 @@ const makeIdeReactProvider = (scope, socket) => { scopeStore.set(key, value) } scopeStore.set('editor.sharejs_doc', scope.editor.sharejs_doc) - scopeStore.set('ui.chatOpen', scope.ui.chatOpen) const scopeEventEmitter = new ReactScopeEventEmitter( new IdeEventEmitter() ) @@ -215,3 +234,110 @@ const makeIdeReactProvider = (scope, socket) => { } return IdeReactProvider } + +const makeLayoutProvider = layoutContextOverrides => { + const layout = { + ...layoutContextDefault, + ...layoutContextOverrides, + } + const LayoutProvider = ({ children }) => { + const [view, setView] = useState(layout.view) + const [openFile, setOpenFile] = useState(layout.openFile) + const [chatIsOpen, setChatIsOpen] = useState(layout.chatIsOpen) + const [reviewPanelOpen, setReviewPanelOpen] = useState( + layout.reviewPanelOpen + ) + const [miniReviewPanelVisible, setMiniReviewPanelVisible] = useState( + layout.miniReviewPanelVisible + ) + const [leftMenuShown, setLeftMenuShown] = useState(layout.leftMenuShown) + const [projectSearchIsOpen, setProjectSearchIsOpen] = useState( + layout.projectSearchIsOpen + ) + const [pdfLayout, setPdfLayout] = useState(layout.pdfLayout) + const [loadingStyleSheet, setLoadingStyleSheet] = useState( + layout.loadingStyleSheet + ) + + useEventListener( + 'ui.toggle-review-panel', + useCallback(() => { + setReviewPanelOpen(open => !open) + }, [setReviewPanelOpen]) + ) + const changeLayout = useCallback( + (newLayout, newView = 'editor') => { + setPdfLayout(newLayout) + setView(newLayout === 'sideBySide' ? 'editor' : newView) + }, + [setPdfLayout, setView] + ) + const { + reattach, + detach, + isLinked: detachIsLinked, + role: detachRole, + } = useDetachLayout() + const pdfPreviewOpen = + pdfLayout === 'sideBySide' || view === 'pdf' || detachRole === 'detacher' + const value = useMemo( + () => ({ + reattach, + detach, + detachIsLinked, + detachRole, + changeLayout, + chatIsOpen, + leftMenuShown, + openFile, + pdfLayout, + pdfPreviewOpen, + projectSearchIsOpen, + setProjectSearchIsOpen, + reviewPanelOpen, + miniReviewPanelVisible, + loadingStyleSheet, + setChatIsOpen, + setLeftMenuShown, + setOpenFile, + setPdfLayout, + setReviewPanelOpen, + setMiniReviewPanelVisible, + setLoadingStyleSheet, + setView, + view, + }), + [ + reattach, + detach, + detachIsLinked, + detachRole, + changeLayout, + chatIsOpen, + leftMenuShown, + openFile, + pdfLayout, + pdfPreviewOpen, + projectSearchIsOpen, + setProjectSearchIsOpen, + reviewPanelOpen, + miniReviewPanelVisible, + loadingStyleSheet, + setChatIsOpen, + setLeftMenuShown, + setOpenFile, + setPdfLayout, + setReviewPanelOpen, + setMiniReviewPanelVisible, + setLoadingStyleSheet, + setView, + view, + ] + ) + + return ( + {children} + ) + } + return LayoutProvider +}