[web] Move UI scope to React states (#26403)

* Move `ui.view` to setState

* Move `openFile` to setState

* Move `ui.leftMenuShown` to setState

* Move `ui.miniReviewPanelVisible` to setState

* Move `ui.pdfLayout` to setState

* Move `ui.chatOpen` to setState

* Move `ui.reviewPanelOpen` to setState

* Cleanup: remove layout-context-adapter and imports

* Replace `ui` scope by mocked `LayoutProvider` in tests

* Update test

* Remove unnecessary `scopeStore.set('ui.chatOpen' ...`

GitOrigin-RevId: 81578bfdc958239eac492905f714a6074c81d0f5
This commit is contained in:
Antoine Clausse
2025-06-25 08:05:15 +00:00
committed by Copybot
parent ab140f578d
commit 9e189e7d59
17 changed files with 406 additions and 310 deletions
@@ -56,14 +56,10 @@ describe('<EditorLeftMenu />', function () {
})
it('render full menu', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders scope={scope} layoutContext={{ leftMenuShown: true }}>
<EditorLeftMenu />
</EditorProviders>
)
@@ -110,14 +106,12 @@ describe('<EditorLeftMenu />', 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(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -142,14 +136,13 @@ describe('<EditorLeftMenu />', function () {
},
})
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -184,14 +177,13 @@ describe('<EditorLeftMenu />', function () {
},
}).as('wordCount')
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -217,9 +209,6 @@ describe('<EditorLeftMenu />', function () {
})
const scope = mockScope({
ui: {
leftMenuShown: true,
},
project: {
members: [],
owner: {
@@ -234,7 +223,10 @@ describe('<EditorLeftMenu />', function () {
})
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -245,9 +237,6 @@ describe('<EditorLeftMenu />', function () {
it('shows git modal correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
project: {
owner: {
_id: '123',
@@ -259,7 +248,10 @@ describe('<EditorLeftMenu />', function () {
})
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -271,9 +263,6 @@ describe('<EditorLeftMenu />', function () {
it('shows git modal paywall correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
project: {
owner: {
_id: '123',
@@ -285,7 +274,10 @@ describe('<EditorLeftMenu />', function () {
})
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -304,14 +296,13 @@ describe('<EditorLeftMenu />', function () {
enabled: false,
}).as('project-status')
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -335,14 +326,13 @@ describe('<EditorLeftMenu />', function () {
describe('settings menu', function () {
it('shows compiler menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -369,14 +359,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows texlive version menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -410,14 +399,14 @@ describe('<EditorLeftMenu />', function () {
folders: [],
}
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope} rootFolder={[rootFolder as any]}>
<EditorProviders
layoutContext={{ leftMenuShown: true }}
scope={scope}
rootFolder={[rootFolder as any]}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -451,14 +440,13 @@ describe('<EditorLeftMenu />', function () {
window.metaAttributesCache.set('ol-languages', languages)
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -475,14 +463,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows dictionary modal correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -493,14 +480,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows auto-complete menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -517,14 +503,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows auto-close brackets menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -541,14 +526,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows code check menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -579,14 +563,13 @@ describe('<EditorLeftMenu />', function () {
legacyEditorThemes
)
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -619,14 +602,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows overall theme menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -643,14 +625,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows keybindings menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -667,14 +648,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows font size menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -713,14 +693,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows font family menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -741,14 +720,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows line height menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -765,14 +743,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows pdf viewer menu correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -791,14 +768,13 @@ describe('<EditorLeftMenu />', function () {
describe('help menu', function () {
it('shows hotkeys modal correctly', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -808,14 +784,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows correct url for documentation', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -828,14 +803,13 @@ describe('<EditorLeftMenu />', function () {
})
it('shows correct contact us modal', function () {
const scope = mockScope({
ui: {
leftMenuShown: true,
},
})
const scope = mockScope()
cy.mount(
<EditorProviders scope={scope}>
<EditorProviders
scope={scope}
layoutContext={{ leftMenuShown: true }}
>
<EditorLeftMenu />
</EditorProviders>
)
@@ -848,16 +822,13 @@ describe('<EditorLeftMenu />', 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(
<EditorProviders scope={scope}>
<EditorProviders scope={scope} layoutContext={{ leftMenuShown: true }}>
<EditorLeftMenu />
</EditorProviders>
)
@@ -47,10 +47,5 @@ export const mockScope = (scope?: Scope) => ({
},
},
hasLintingError: false,
ui: {
view: 'editor',
pdfLayout: 'sideBySide',
leftMenuShown: false,
},
...scope,
})
@@ -17,8 +17,4 @@ export const mockScope = () => ({
}),
},
hasLintingError: false,
ui: {
view: 'editor',
pdfLayout: 'sideBySide',
},
})
@@ -9,9 +9,11 @@ import * as eventTracking from '@/infrastructure/event-tracking'
describe('<LayoutDropdownButton />', function () {
let openStub
let sendMBSpy
const defaultUi = {
const defaultLayout = {
pdfLayout: 'flat',
view: 'pdf',
chatIsOpen: false,
}
beforeEach(function () {
@@ -27,7 +29,9 @@ describe('<LayoutDropdownButton />', function () {
it('should mark current layout option as selected', async function () {
// Selected is aria-label, visually we show a checkmark
renderWithEditorContext(<LayoutDropdownButton />, { ui: defaultUi })
renderWithEditorContext(<LayoutDropdownButton />, {
layoutContext: defaultLayout,
})
screen.getByRole('button', { name: 'Layout' }).click()
@@ -69,7 +73,7 @@ describe('<LayoutDropdownButton />', function () {
it('should not select any option in history view', async function () {
// Selected is aria-label, visually we show a checkmark
renderWithEditorContext(<LayoutDropdownButton />, {
ui: { ...defaultUi, view: 'history' },
layoutContext: { ...defaultLayout, view: 'history' },
})
screen.getByRole('button', { name: 'Layout' }).click()
@@ -112,9 +116,10 @@ describe('<LayoutDropdownButton />', function () {
it('should treat file and editor views the same way', async function () {
// Selected is aria-label, visually we show a checkmark
renderWithEditorContext(<LayoutDropdownButton />, {
ui: {
layoutContext: {
pdfLayout: 'flat',
view: 'file',
chatIsOpen: false,
},
})
@@ -161,7 +166,7 @@ describe('<LayoutDropdownButton />', function () {
window.BroadcastChannel = originalBroadcastChannel || true // ensure that window.BroadcastChannel is truthy
renderWithEditorContext(<LayoutDropdownButton />, {
ui: { ...defaultUi, view: 'editor' },
layoutContext: { ...defaultLayout, view: 'editor' },
})
screen.getByRole('button', { name: 'Layout' }).click()
@@ -192,7 +197,7 @@ describe('<LayoutDropdownButton />', function () {
beforeEach(async function () {
window.metaAttributesCache.set('ol-detachRole', 'detacher')
renderWithEditorContext(<LayoutDropdownButton />, {
ui: { ...defaultUi, view: 'editor' },
layoutContext: { ...defaultLayout, view: 'editor' },
})
screen.getByRole('button', { name: 'Layout' }).click()
@@ -93,6 +93,8 @@ const createInitialValue = () =>
pdfPreviewOpen: false,
projectSearchIsOpen: true,
setProjectSearchIsOpen: cy.stub(),
openFile: null,
setOpenFile: cy.stub(),
}) satisfies LayoutContextValue
const LayoutProvider: FC<React.PropsWithChildren> = ({ children }) => {
@@ -14,11 +14,10 @@ import { withTestContainerErrorBoundary } from '../../../helpers/error-boundary'
const TestContainerWithoutErrorBoundary: FC<{
component: React.ReactNode
scope: Record<string, unknown>
props: Record<string, unknown>
}> = ({ component, scope, props }) => {
}> = ({ component, props }) => {
return (
<EditorProviders scope={scope} {...props}>
<EditorProviders {...props}>
<HistoryProvider>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<div className="history-react">{component}</div>
@@ -34,17 +33,12 @@ const TestContainer = withTestContainerErrorBoundary(
const mountWithEditorProviders = (
component: React.ReactNode,
scope: Record<string, unknown> = {},
props: Record<string, unknown> = {}
) => {
cy.mount(<TestContainer component={component} scope={scope} props={props} />)
cy.mount(<TestContainer component={component} props={props} />)
}
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(<ChangeList />, scope, {
mountWithEditorProviders(<ChangeList />, {
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(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
mountWithEditorProviders(
<ChangeList />,
{
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(<ChangeList />, scope, {
mountWithEditorProviders(<ChangeList />, {
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(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
mountWithEditorProviders(
<ChangeList />,
{
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(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
mountWithEditorProviders(
<ChangeList />,
{
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(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
mountWithEditorProviders(
<ChangeList />,
{
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(<ChangeList />, scope, {
user: {
id: USER_ID,
email: USER_EMAIL,
isAdmin: true,
},
})
mountWithEditorProviders(
<ChangeList />,
{
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(
<ChangeList />,
mountWithEditorProviders(<ChangeList />, 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(<ChangeList />, scope, {
mountWithEditorProviders(<ChangeList />, {
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(<ChangeList />, scope, {
mountWithEditorProviders(<ChangeList />, {
layoutContext: { view: 'history' },
user: {
id: USER_ID,
email: USER_EMAIL,
@@ -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<string, unknown>
layoutContext: LayoutContextValue
diff: Diff
selection: HistoryContextValue['selection']
}> = ({ scope, diff, selection }) => {
}> = ({ diff, selection, layoutContext }) => {
return (
<EditorProviders scope={scope}>
<EditorProviders layoutContext={layoutContext}>
<HistoryProvider>
<div className="history-react">
<Toolbar diff={diff} selection={selection} />
@@ -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(
<TestContainer
scope={editorProvidersScope}
layoutContext={{ view: 'history' }}
diff={diff}
selection={selection}
/>
@@ -129,7 +126,7 @@ describe('history toolbar', function () {
cy.mount(
<TestContainer
scope={editorProvidersScope}
layoutContext={{ view: 'history' }}
diff={diff}
selection={selection}
/>
@@ -4,7 +4,9 @@ import SwitchToEditorButton from '@/features/pdf-preview/components/switch-to-ed
describe('<SwitchToEditorButton />', function () {
it('shows button in full screen pdf layout', function () {
cy.mount(
<EditorProviders ui={{ view: 'pdf', pdfLayout: 'flat', chatOpen: false }}>
<EditorProviders
layoutContext={{ view: 'pdf', pdfLayout: 'flat', chatIsOpen: false }}
>
<SwitchToEditorButton />
</EditorProviders>
)
@@ -15,7 +17,11 @@ describe('<SwitchToEditorButton />', function () {
it('does not show button in split screen layout', function () {
cy.mount(
<EditorProviders
ui={{ view: 'pdf', pdfLayout: 'sideBySide', chatOpen: false }}
layoutContext={{
view: 'pdf',
pdfLayout: 'sideBySide',
chatIsOpen: false,
}}
>
<SwitchToEditorButton />
</EditorProviders>
@@ -28,7 +34,13 @@ describe('<SwitchToEditorButton />', function () {
window.metaAttributesCache.set('ol-detachRole', 'detacher')
cy.mount(
<EditorProviders ui={{ view: 'pdf', pdfLayout: 'flat', chatOpen: false }}>
<EditorProviders
layoutContext={{
view: 'pdf',
pdfLayout: 'flat',
chatIsOpen: false,
}}
>
<SwitchToEditorButton />
</EditorProviders>
)
@@ -5,7 +5,7 @@ describe('<SwitchToPDFButton />', function () {
it('shows button in full screen editor layout', function () {
cy.mount(
<EditorProviders
ui={{ view: 'editor', pdfLayout: 'flat', chatOpen: false }}
layoutContext={{ view: 'editor', pdfLayout: 'flat', chatIsOpen: false }}
>
<SwitchToPDFButton />
</EditorProviders>
@@ -17,7 +17,11 @@ describe('<SwitchToPDFButton />', function () {
it('does not show button in split screen layout', function () {
cy.mount(
<EditorProviders
ui={{ view: 'editor', pdfLayout: 'sideBySide', chatOpen: false }}
layoutContext={{
view: 'editor',
pdfLayout: 'sideBySide',
chatIsOpen: false,
}}
>
<SwitchToPDFButton />
</EditorProviders>
@@ -31,7 +35,7 @@ describe('<SwitchToPDFButton />', function () {
cy.mount(
<EditorProviders
ui={{ view: 'editor', pdfLayout: 'flat', chatOpen: false }}
layoutContext={{ view: 'editor', pdfLayout: 'flat', chatIsOpen: false }}
>
<SwitchToPDFButton />
</EditorProviders>
@@ -81,9 +81,6 @@ export const mockScope = (
write: true,
...permissions,
},
ui: {
reviewPanelOpen: false,
},
toggleReviewPanel: cy.stub(),
toggleTrackChangesForEveryone: cy.stub(),
refreshResolvedCommentsDropdown: cy.stub(() => sleep(1000)),
@@ -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<LayoutContextValue>
*/
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>} */
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 (
<LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
)
}
return LayoutProvider
}