From b8a360669d6d755a614384dbff8869bceef6c98c Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Tue, 16 Mar 2021 02:06:27 +1000 Subject: [PATCH] feat: persist pane sizes and fix resizing bug (#15480) --- .../cypress/component/RunnerCt.spec.tsx | 49 ++++++++++++---- .../runner-ct/src/app/ReporterContainer.tsx | 2 +- packages/runner-ct/src/app/RunnerCt.tsx | 56 ++++++++++++------- packages/runner-ct/src/lib/state.ts | 3 +- packages/runner-ct/src/main.jsx | 7 ++- packages/server/lib/saved_state.js | 3 + 6 files changed, 86 insertions(+), 34 deletions(-) diff --git a/packages/runner-ct/cypress/component/RunnerCt.spec.tsx b/packages/runner-ct/cypress/component/RunnerCt.spec.tsx index 411ed6795ba9..669dcb9b6e82 100644 --- a/packages/runner-ct/cypress/component/RunnerCt.spec.tsx +++ b/packages/runner-ct/cypress/component/RunnerCt.spec.tsx @@ -7,15 +7,27 @@ import '@packages/runner/src/main.scss' const selectors = { reporter: '[data-cy=reporter]', + noSpecSelectedReporter: '[data-cy=no-spec-selected-reporter]', specsList: '[data-cy=specs-list]', searchInput: 'input[placeholder="Find spec..."]', } +interface Overrides { + saveState?: Function +} + +const noop = () => {} + class FakeEventManager { - start = () => { } - on = () => { } - stop = () => {} - notifyRunningSpec = () => { } + constructor (overrides: Overrides = {}) { + this.saveState = overrides.saveState || noop + } + + start = noop + on = noop + stop = noop + notifyRunningSpec = noop + saveState: Function = () => { } } const fakeConfig = { projectName: 'Project', env: {}, isTextTerminal: false } as any as Cypress.RuntimeConfigOptions @@ -58,30 +70,43 @@ describe('RunnerCt', () => { }) context('keyboard shortcuts', () => { - beforeEach(() => { + it('toggles specs list drawer using shortcut', () => { + const saveState = cy.stub() + mount( , ) cy.window().then((win) => win.focus()) - }) - - it('toggles specs list drawer using shortcut', () => { cy.get(selectors.specsList).should('be.visible') cy.realPress(['Meta', 'B']) - cy.get(selectors.specsList).should('not.be.visible') + cy.get(selectors.specsList).should('not.be.visible').then(() => { + expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: false }) + }) cy.realPress(['Meta', 'B']) - cy.get(selectors.specsList).should('be.visible') + cy.get(selectors.specsList).should('be.visible').then(() => { + expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: false }), + expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: true }) + }) }) it('focuses the search field on "/"', () => { + mount( + , + ) + cy.realPress('/') cy.get(selectors.searchInput).should('be.focused') }) @@ -95,7 +120,7 @@ describe('RunnerCt', () => { eventManager={new FakeEventManager()} config={fakeConfig} />) - cy.get(selectors.reporter).should('not.be.visible') + cy.get(selectors.noSpecSelectedReporter).should('exist') cy.percySnapshot() }) }) diff --git a/packages/runner-ct/src/app/ReporterContainer.tsx b/packages/runner-ct/src/app/ReporterContainer.tsx index 89d8907cd177..be784b9035db 100644 --- a/packages/runner-ct/src/app/ReporterContainer.tsx +++ b/packages/runner-ct/src/app/ReporterContainer.tsx @@ -20,7 +20,7 @@ export const ReporterContainer = observer( function ReporterContainer (props: ReporterContainerProps) { if (!props.state.spec) { return ( -
+
) diff --git a/packages/runner-ct/src/app/RunnerCt.tsx b/packages/runner-ct/src/app/RunnerCt.tsx index 03830f37f302..6ca5bf67c138 100644 --- a/packages/runner-ct/src/app/RunnerCt.tsx +++ b/packages/runner-ct/src/app/RunnerCt.tsx @@ -95,9 +95,9 @@ const App: React.FC = observer( }, []) React.useEffect(() => { - const isOpenMode = !config.isTextTerminal - - state.setIsSpecsListOpen(isOpenMode) + if (config.isTextTerminal) { + state.setIsSpecsListOpen(false) + } }, []) useScreenshotHandler({ @@ -124,7 +124,10 @@ const App: React.FC = observer( type: 'js', onClick: ({ index }) => { onNavItemClick(index) - state.setIsSpecsListOpen(!props.state.isSpecsListOpen) + const isOpen = !props.state.isSpecsListOpen + + state.setIsSpecsListOpen(isOpen) + props.eventManager.saveState({ ctIsSpecsListOpen: isOpen }) }, }, }, @@ -150,7 +153,10 @@ const App: React.FC = observer( function toggleSpecsList () { setActiveIndex((val) => val === 0 ? undefined : 0) - state.setIsSpecsListOpen(!props.state.isSpecsListOpen) + const newVal = !props.state.isSpecsListOpen + + state.setIsSpecsListOpen(newVal) + props.eventManager.saveState({ ctIsSpecsListOpen: newVal }) } function focusSpecsList () { @@ -179,6 +185,12 @@ const App: React.FC = observer( state.updateSpecListWidth(newWidth) } + function persistWidth (prop: 'ctReporterWidth' | 'ctSpecListWidth') { + return (newWidth: number) => { + props.eventManager.saveState({ [prop]: newWidth }) + } + } + function hideIfScreenshotting (callback: () => number) { if (state.screenshotting) { return 0 @@ -218,6 +230,21 @@ const App: React.FC = observer( ) + const MainAreaComponent: React.FC | typeof SplitPane = props.state.spec + ? SplitPane + : (props) =>
{props.children}
+ + const mainAreaProps = props.state.spec + ? { + split: 'vertical', + minSize: hideReporterIfNecessary(() => 100), + maxSize: hideReporterIfNecessary(() => 600), + defaultSize: hideReporterIfNecessary(() => state.reporterWidth), + className: 'primary', + onChange: debounce(onReporterSplitPaneChange), + } + : {} + return ( = observer( {leftNav} state.isSpecsListOpen ? 30 : 0)} maxSize={hideIfScreenshotting(() => state.isSpecsListOpen ? 600 : 0)} - defaultSize={hideIfScreenshotting(() => state.isSpecsListOpen ? DEFAULT_LIST_WIDTH : 0)} + defaultSize={hideIfScreenshotting(() => state.isSpecsListOpen ? state.specListWidth : 0)} + onDragFinished={persistWidth('ctSpecListWidth')} className="primary" // @ts-expect-error split-pane ref types are weak so we are using our custom type for ref ref={splitPaneRef} @@ -250,15 +277,7 @@ const App: React.FC = observer( } onSelectSpec={runSpec} /> - - 100)} - maxSize={hideReporterIfNecessary(() => 600)} - defaultSize={hideReporterIfNecessary(() => DEFAULT_REPORTER_WIDTH)} - className="primary" - onChange={debounce(onReporterSplitPaneChange)} - > + = observer( allowResize={props.state.isAnyDevtoolsPluginOpen} size={hideIfScreenshotting(() => state.isAnyDevtoolsPluginOpen - ? DEFAULT_PLUGINS_HEIGHT + ? state.pluginsHeight // show the small not resize-able panel with buttons or nothing : state.isAnyPluginToShow ? PLUGIN_BAR_HEIGHT : 0)} onChange={debounce(onPluginsSplitPaneChange)} @@ -296,9 +315,8 @@ const App: React.FC = observer( pluginRootContainer={pluginRootContainer} /> - + - ) }, diff --git a/packages/runner-ct/src/lib/state.ts b/packages/runner-ct/src/lib/state.ts index 2a07438fe348..b7dfccea0549 100644 --- a/packages/runner-ct/src/lib/state.ts +++ b/packages/runner-ct/src/lib/state.ts @@ -136,9 +136,10 @@ export default class State { multiSpecs = [], reporterWidth = DEFAULT_REPORTER_WIDTH, specListWidth = DEFAULT_LIST_WIDTH, + isSpecsListOpen = true, }) { this.reporterWidth = reporterWidth - this.pluginsHeight = PLUGIN_BAR_HEIGHT + this.isSpecsListOpen = isSpecsListOpen this.spec = spec this.specs = specs this.specListWidth = specListWidth diff --git a/packages/runner-ct/src/main.jsx b/packages/runner-ct/src/main.jsx index aef3ec3c2430..74b3b7ed55fa 100644 --- a/packages/runner-ct/src/main.jsx +++ b/packages/runner-ct/src/main.jsx @@ -31,7 +31,12 @@ const Runner = { configState.specs = config.specs - const state = new State(configState) + const ctRunnerSpecificDefaults = { + reporterWidth: config.state.ctReporterWidth, + isSpecsListOpen: config.state.ctIsSpecsListOpen, + specListWidth: config.state.ctSpecListWidth, + } + const state = new State({ ...configState, ...ctRunnerSpecificDefaults }) const setSpecByUrlHash = () => { const specPath = util.specPath() diff --git a/packages/server/lib/saved_state.js b/packages/server/lib/saved_state.js index 1068deb0ba3b..36126ebbfa31 100644 --- a/packages/server/lib/saved_state.js +++ b/packages/server/lib/saved_state.js @@ -25,6 +25,9 @@ reporterWidth showedOnBoardingModal showedStudioModal preferredOpener +ctReporterWidth +ctIsSpecsListOpen +ctSpecListWidth `.trim().split(/\s+/) const formStatePath = (projectRoot) => {