Hide workspace side panels

This commit is contained in:
2026-07-20 11:49:01 +08:00
parent 22128f0171
commit 5abc9b634b
2 changed files with 80 additions and 65 deletions

View File

@@ -21,28 +21,18 @@ page.on('console', (message) => {
if (message.type() === 'error') errors.push(message.text()) if (message.type() === 'error') errors.push(message.text())
}) })
await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' }) const collectMetrics = async () => page.evaluate(() => {
await page.screenshot({ path: 'test-results/login-react-acro.png', fullPage: true })
await page.locator('.login-form-panel .arco-btn-primary').click()
await page.waitForSelector('.workbench-shell')
await page.waitForTimeout(700)
await page.screenshot({ path: 'test-results/workbench-react-acro-light.png', fullPage: true })
await page.locator('.topbar-actions .arco-btn').first().click()
await page.screenshot({ path: 'test-results/workbench-react-acro-dark.png', fullPage: true })
const metrics = await page.evaluate(() => {
const statusbar = document.querySelector('.statusbar') const statusbar = document.querySelector('.statusbar')
const statusName = document.querySelector('.status-name') const statusName = document.querySelector('.status-name')
const statusOnlineDot = document.querySelector('.status-online-dot') const statusOnlineDot = document.querySelector('.status-online-dot')
const search = document.querySelector('.global-search') const search = document.querySelector('.global-search')
const main = document.querySelector('.workbench-main') const main = document.querySelector('.workbench-main')
const workspacePage = document.querySelector('.workspace-page') const workspacePage = document.querySelector('.workspace-page')
const projectOverview = document.querySelector('.overview-page:not(.workspace-page)')
const rail = document.querySelector('.project-rail') const rail = document.querySelector('.project-rail')
const railChildren = rail?.querySelector('.arco-layout-sider-children') const railChildren = rail?.querySelector('.arco-layout-sider-children')
const sidebar = document.querySelector('.channel-sidebar') const sidebar = document.querySelector('.channel-sidebar')
const sidebarTitle = document.querySelector('.project-title h5')
const stage = document.querySelector('.stage') const stage = document.querySelector('.stage')
const inspectorTitle = document.querySelector('.inspector-title h5')
const inspector = document.querySelector('.inspector') const inspector = document.querySelector('.inspector')
const dashboard = document.querySelector('.dashboard-button') const dashboard = document.querySelector('.dashboard-button')
const project = document.querySelector('.project-button') const project = document.querySelector('.project-button')
@@ -80,13 +70,12 @@ const metrics = await page.evaluate(() => {
searchHeight: search?.getBoundingClientRect().height, searchHeight: search?.getBoundingClientRect().height,
workbenchMain: rect(main), workbenchMain: rect(main),
hasWorkspacePage: Boolean(workspacePage), hasWorkspacePage: Boolean(workspacePage),
hasProjectOverview: Boolean(projectOverview),
projectRailWidth: rail?.getBoundingClientRect().width, projectRailWidth: rail?.getBoundingClientRect().width,
projectRail: rect(rail), projectRail: rect(rail),
channelSidebar: rect(sidebar), channelSidebar: rect(sidebar),
sidebarTitleText: sidebarTitle?.textContent ?? '',
stage: rect(stage), stage: rect(stage),
inspector: rect(inspector), inspector: rect(inspector),
inspectorTitleText: inspectorTitle?.textContent ?? '',
dashboardButton: rect(dashboard), dashboardButton: rect(dashboard),
dashboardButtonActive: dashboard?.classList.contains('active') ?? false, dashboardButtonActive: dashboard?.classList.contains('active') ?? false,
projectButton: rect(project), projectButton: rect(project),
@@ -102,12 +91,29 @@ const metrics = await page.evaluate(() => {
} }
}) })
await page.goto(`http://127.0.0.1:${port}/`, { waitUntil: 'networkidle' })
await page.screenshot({ path: 'test-results/login-react-acro.png', fullPage: true })
await page.locator('.login-form-panel .arco-btn-primary').click()
await page.waitForSelector('.workbench-shell')
await page.waitForTimeout(700)
await page.screenshot({ path: 'test-results/workbench-react-acro-light.png', fullPage: true })
const workspaceMetrics = await collectMetrics()
await page.locator('.project-button').first().click()
await page.waitForTimeout(500)
await page.screenshot({ path: 'test-results/project-react-acro-light.png', fullPage: true })
const projectMetrics = await collectMetrics()
await page.locator('.topbar-actions .arco-btn').first().click()
await page.screenshot({ path: 'test-results/project-react-acro-dark.png', fullPage: true })
await browser.close() await browser.close()
await server.close() await server.close()
console.log(JSON.stringify({ metrics, errors }, null, 2)) console.log(JSON.stringify({ workspaceMetrics, projectMetrics, errors }, null, 2))
const failures = [] const failures = []
const metrics = workspaceMetrics
if (errors.length) failures.push(`console errors: ${errors.join('; ')}`) if (errors.length) failures.push(`console errors: ${errors.join('; ')}`)
if (metrics.statusbarHeight !== 32) failures.push(`expected statusbar height 32, got ${metrics.statusbarHeight}`) if (metrics.statusbarHeight !== 32) failures.push(`expected statusbar height 32, got ${metrics.statusbarHeight}`)
if (!metrics.statusOnlineDot) failures.push('expected online status to render as an icon dot') if (!metrics.statusOnlineDot) failures.push('expected online status to render as an icon dot')
@@ -120,35 +126,16 @@ if (!metrics.workbenchMain) failures.push('missing workbench main')
if (!metrics.hasWorkspacePage) failures.push('expected login to land on workspace page') if (!metrics.hasWorkspacePage) failures.push('expected login to land on workspace page')
if (!metrics.dashboardButtonActive) failures.push('expected dashboard button to be active after login') if (!metrics.dashboardButtonActive) failures.push('expected dashboard button to be active after login')
if (metrics.projectButtonActive) failures.push('expected first project button not to be active on workspace landing page') if (metrics.projectButtonActive) failures.push('expected first project button not to be active on workspace landing page')
if (!metrics.sidebarTitleText.includes('工作台')) { if (metrics.channelSidebar) failures.push(`expected workspace to hide channel sidebar, got ${JSON.stringify(metrics.channelSidebar)}`)
failures.push(`expected sidebar title to describe workspace landing page, got ${JSON.stringify(metrics.sidebarTitleText)}`) if (metrics.inspector) failures.push(`expected workspace to hide inspector, got ${JSON.stringify(metrics.inspector)}`)
} if (!metrics.projectRail || !metrics.stage) {
if (!metrics.inspectorTitleText.includes('工作台')) { failures.push(`missing workspace layout regions: rail=${JSON.stringify(metrics.projectRail)}, stage=${JSON.stringify(metrics.stage)}`)
failures.push(`expected inspector title to describe workspace landing page, got ${JSON.stringify(metrics.inspectorTitleText)}`)
}
if (!metrics.projectRail || !metrics.channelSidebar || !metrics.stage || !metrics.inspector) {
failures.push(
`missing main layout regions: rail=${JSON.stringify(metrics.projectRail)}, sidebar=${JSON.stringify(metrics.channelSidebar)}, stage=${JSON.stringify(metrics.stage)}, inspector=${JSON.stringify(metrics.inspector)}`,
)
} else { } else {
const sameTop = [metrics.projectRail, metrics.channelSidebar, metrics.stage, metrics.inspector] if (Math.abs(metrics.stage.left - metrics.projectRail.right) > 1) {
.every((region) => Math.abs(region.top - metrics.projectRail.top) <= 1) failures.push(`expected workspace stage to start after project rail, got stage.left=${metrics.stage.left}, rail.right=${metrics.projectRail.right}`)
if (!sameTop) {
failures.push(
`expected project rail, channel sidebar, stage, and inspector to share the same top edge, got rail=${metrics.projectRail.top}, sidebar=${metrics.channelSidebar.top}, stage=${metrics.stage.top}, inspector=${metrics.inspector.top}`,
)
} }
if (Math.abs(metrics.channelSidebar.left - metrics.projectRail.right) > 1) { if (Math.abs(metrics.stage.right - metrics.viewportWidth) > 1) {
failures.push(`expected channel sidebar to sit after project rail, got sidebar.left=${metrics.channelSidebar.left}, rail.right=${metrics.projectRail.right}`) failures.push(`expected workspace stage to end at viewport right edge, got stage.right=${metrics.stage.right}, viewport=${metrics.viewportWidth}`)
}
if (Math.abs(metrics.stage.left - metrics.channelSidebar.right) > 1) {
failures.push(`expected stage to sit after channel sidebar, got stage.left=${metrics.stage.left}, sidebar.right=${metrics.channelSidebar.right}`)
}
if (Math.abs(metrics.inspector.left - metrics.stage.right) > 1) {
failures.push(`expected inspector to sit after stage, got inspector.left=${metrics.inspector.left}, stage.right=${metrics.stage.right}`)
}
if (Math.abs(metrics.inspector.right - metrics.viewportWidth) > 1) {
failures.push(`expected inspector to end at viewport right edge, got inspector.right=${metrics.inspector.right}, viewport=${metrics.viewportWidth}`)
} }
} }
if (!metrics.projectRailWidth || metrics.projectRailWidth < 88) { if (!metrics.projectRailWidth || metrics.projectRailWidth < 88) {
@@ -165,11 +152,7 @@ for (const [name, overflow] of [
['project rail', metrics.projectRailOverflow], ['project rail', metrics.projectRailOverflow],
['project rail children', metrics.projectRailChildrenOverflow], ['project rail children', metrics.projectRailChildrenOverflow],
]) { ]) {
if ( if (!overflow || overflow.overflowX !== 'hidden' || overflow.overflowY !== 'hidden') {
!overflow ||
overflow.overflowX !== 'hidden' ||
overflow.overflowY !== 'hidden'
) {
failures.push(`expected ${name} scrollbars to be hidden with no overflow, got ${JSON.stringify(overflow)}`) failures.push(`expected ${name} scrollbars to be hidden with no overflow, got ${JSON.stringify(overflow)}`)
} }
} }
@@ -185,18 +168,44 @@ if (
metrics.dashboardButton.width !== metrics.projectButton?.width || metrics.dashboardButton.width !== metrics.projectButton?.width ||
metrics.dashboardButton.height !== metrics.projectButton?.height metrics.dashboardButton.height !== metrics.projectButton?.height
) { ) {
failures.push( failures.push(`dashboard button size ${JSON.stringify(metrics.dashboardButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`)
`dashboard button size ${JSON.stringify(metrics.dashboardButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`,
)
} }
if ( if (
!metrics.createProjectButton || !metrics.createProjectButton ||
metrics.createProjectButton.width !== metrics.projectButton?.width || metrics.createProjectButton.width !== metrics.projectButton?.width ||
metrics.createProjectButton.height !== metrics.projectButton?.height metrics.createProjectButton.height !== metrics.projectButton?.height
) { ) {
failures.push(`create project button size ${JSON.stringify(metrics.createProjectButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`)
}
if (!projectMetrics.hasProjectOverview) failures.push('expected first project click to show project overview page')
if (projectMetrics.hasWorkspacePage) failures.push('expected project overview mode to leave workspace page')
if (!projectMetrics.projectButtonActive) failures.push('expected first project button to be active in project mode')
if (projectMetrics.dashboardButtonActive) failures.push('expected dashboard button not to be active in project mode')
if (!projectMetrics.projectRail || !projectMetrics.channelSidebar || !projectMetrics.stage || !projectMetrics.inspector) {
failures.push( failures.push(
`create project button size ${JSON.stringify(metrics.createProjectButton)} does not match project button ${JSON.stringify(metrics.projectButton)}`, `missing project layout regions: rail=${JSON.stringify(projectMetrics.projectRail)}, sidebar=${JSON.stringify(projectMetrics.channelSidebar)}, stage=${JSON.stringify(projectMetrics.stage)}, inspector=${JSON.stringify(projectMetrics.inspector)}`,
) )
} else {
const sameTop = [projectMetrics.projectRail, projectMetrics.channelSidebar, projectMetrics.stage, projectMetrics.inspector]
.every((region) => Math.abs(region.top - projectMetrics.projectRail.top) <= 1)
if (!sameTop) {
failures.push(
`expected project rail, channel sidebar, stage, and inspector to share the same top edge, got rail=${projectMetrics.projectRail.top}, sidebar=${projectMetrics.channelSidebar.top}, stage=${projectMetrics.stage.top}, inspector=${projectMetrics.inspector.top}`,
)
}
if (Math.abs(projectMetrics.channelSidebar.left - projectMetrics.projectRail.right) > 1) {
failures.push(`expected channel sidebar to sit after project rail, got sidebar.left=${projectMetrics.channelSidebar.left}, rail.right=${projectMetrics.projectRail.right}`)
}
if (Math.abs(projectMetrics.stage.left - projectMetrics.channelSidebar.right) > 1) {
failures.push(`expected stage to sit after channel sidebar, got stage.left=${projectMetrics.stage.left}, sidebar.right=${projectMetrics.channelSidebar.right}`)
}
if (Math.abs(projectMetrics.inspector.left - projectMetrics.stage.right) > 1) {
failures.push(`expected inspector to sit after stage, got inspector.left=${projectMetrics.inspector.left}, stage.right=${projectMetrics.stage.right}`)
}
if (Math.abs(projectMetrics.inspector.right - projectMetrics.viewportWidth) > 1) {
failures.push(`expected inspector to end at viewport right edge, got inspector.right=${projectMetrics.inspector.right}, viewport=${projectMetrics.viewportWidth}`)
}
} }
if (failures.length) { if (failures.length) {

View File

@@ -33,6 +33,8 @@ export function ProjectPage({
onSelectItem: (title: string) => void onSelectItem: (title: string) => void
onToggleTheme: () => void onToggleTheme: () => void
}) { }) {
const isWorkspace = activeView === 'workspace'
return ( return (
<Layout className="workbench-shell"> <Layout className="workbench-shell">
<ProjectTopbar theme={theme} onToggleTheme={onToggleTheme} /> <ProjectTopbar theme={theme} onToggleTheme={onToggleTheme} />
@@ -45,28 +47,32 @@ export function ProjectPage({
onSelectProject={onSelectProject} onSelectProject={onSelectProject}
/> />
<ProjectSidebar {!isWorkspace && (
activeView={activeView} <ProjectSidebar
activeProject={activeProject} activeView={activeView}
activeChannel={activeChannel} activeProject={activeProject}
onSelectChannel={onSelectChannel} activeChannel={activeChannel}
onSelectItem={onSelectItem} onSelectChannel={onSelectChannel}
/> onSelectItem={onSelectItem}
/>
)}
<Content className="stage"> <Content className="stage">
{activeView === 'workspace' ? ( {isWorkspace ? (
<WorkspacePage onSelectProject={onSelectProject} /> <WorkspacePage onSelectProject={onSelectProject} />
) : ( ) : (
<ProjectOverview onSelectItem={onSelectItem} /> <ProjectOverview onSelectItem={onSelectItem} />
)} )}
</Content> </Content>
<ProjectInspector {!isWorkspace && (
activeView={activeView} <ProjectInspector
activeProject={activeProject} activeView={activeView}
activeChannel={activeChannel} activeProject={activeProject}
selectedItem={activeView === 'workspace' ? '工作台动态' : selectedItem} activeChannel={activeChannel}
/> selectedItem={selectedItem}
/>
)}
</Layout> </Layout>
<ProjectStatusbar /> <ProjectStatusbar />