feat: persist pane sizes and fix resizing bug (#15480)

This commit is contained in:
Lachlan Miller
2021-03-16 02:06:27 +10:00
committed by GitHub
parent b880e8c890
commit b8a360669d
6 changed files with 94 additions and 42 deletions

View File

@@ -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,7 +70,34 @@ describe('RunnerCt', () => {
})
context('keyboard shortcuts', () => {
beforeEach(() => {
it('toggles specs list drawer using shortcut', () => {
const saveState = cy.stub()
mount(
<RunnerCt
state={makeState()}
// @ts-ignore - this is difficult to stub. Real one breaks things.
eventManager={new FakeEventManager({ saveState })}
config={fakeConfig}
/>,
)
cy.window().then((win) => win.focus())
cy.get(selectors.specsList).should('be.visible')
cy.realPress(['Meta', 'B'])
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').then(() => {
expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: false }),
expect(saveState).to.have.been.calledWith({ ctIsSpecsListOpen: true })
})
})
it('focuses the search field on "/"', () => {
mount(
<RunnerCt
state={makeState()}
@@ -68,20 +107,6 @@ describe('RunnerCt', () => {
/>,
)
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.realPress(['Meta', 'B'])
cy.get(selectors.specsList).should('be.visible')
})
it('focuses the search field on "/"', () => {
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()
})
})

View File

@@ -20,7 +20,7 @@ export const ReporterContainer = observer(
function ReporterContainer (props: ReporterContainerProps) {
if (!props.state.spec) {
return (
<div className='no-spec' data-cy="reporter">
<div className='no-spec' data-cy="no-spec-selected-reporter">
<NoSpecSelected />
</div>
)

View File

@@ -95,9 +95,9 @@ const App: React.FC<AppProps> = 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<AppProps> = 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<AppProps> = 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<AppProps> = 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<AppProps> = observer(
<KeyboardHelper />
)
const MainAreaComponent: React.FC | typeof SplitPane = props.state.spec
? SplitPane
: (props) => <div>{props.children}</div>
const mainAreaProps = props.state.spec
? {
split: 'vertical',
minSize: hideReporterIfNecessary(() => 100),
maxSize: hideReporterIfNecessary(() => 600),
defaultSize: hideReporterIfNecessary(() => state.reporterWidth),
className: 'primary',
onChange: debounce(onReporterSplitPaneChange),
}
: {}
return (
<SplitPane
split="vertical"
@@ -229,10 +256,10 @@ const App: React.FC<AppProps> = observer(
{leftNav}
<SplitPane
split="vertical"
// do not allow resizing of this for now, simplifes calculation for scale of AUT.
minSize={hideIfScreenshotting(() => 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<AppProps> = observer(
}
onSelectSpec={runSpec}
/>
<SplitPane
split="vertical"
minSize={hideReporterIfNecessary(() => 100)}
maxSize={hideReporterIfNecessary(() => 600)}
defaultSize={hideReporterIfNecessary(() => DEFAULT_REPORTER_WIDTH)}
className="primary"
onChange={debounce(onReporterSplitPaneChange)}
>
<MainAreaComponent {...mainAreaProps}>
<ReporterContainer
state={props.state}
config={props.config}
@@ -271,7 +290,7 @@ const App: React.FC<AppProps> = 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<AppProps> = observer(
pluginRootContainer={pluginRootContainer}
/>
</SplitPane>
</SplitPane>
</MainAreaComponent>
</SplitPane>
</SplitPane>
)
},

View File

@@ -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

View File

@@ -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()

View File

@@ -25,6 +25,9 @@ reporterWidth
showedOnBoardingModal
showedStudioModal
preferredOpener
ctReporterWidth
ctIsSpecsListOpen
ctSpecListWidth
`.trim().split(/\s+/)
const formStatePath = (projectRoot) => {