From daca1681c0cb83de82a3c3263f2cf24226f0873a Mon Sep 17 00:00:00 2001 From: Simon Knott Date: Wed, 31 Jul 2024 12:48:46 +0200 Subject: [PATCH] refactor(ui): in splitview component, move sidebar and main from children into named properties (#31925) Pulled out from https://github.com/microsoft/playwright/pull/31900 I stumbled over `React.Children`, because it's the first time I saw that used. https://react.dev/reference/react/Children lists `React.Children` it as "Legacy" and mentions it's uncommon. Also, the fact that SplitView only displays its first two children, and all others are silently discarded, can be a surprise to some. By separating things out into `sidebar` and `main`, not only do we give the two elements names (otherwise one needs to remember that sidebar is always the first child), but we also prevent any "third children" from being dropped. --- packages/recorder/src/recorder.tsx | 15 ++--- packages/trace-viewer/src/ui/networkTab.tsx | 13 +++-- packages/trace-viewer/src/ui/sourceTab.tsx | 13 +++-- packages/trace-viewer/src/ui/uiModeView.tsx | 16 ++++-- packages/trace-viewer/src/ui/workbench.tsx | 34 +++++++----- .../web/src/components/splitView.spec.tsx | 55 ++++++++++++------- packages/web/src/components/splitView.tsx | 15 +++-- 7 files changed, 101 insertions(+), 60 deletions(-) diff --git a/packages/recorder/src/recorder.tsx b/packages/recorder/src/recorder.tsx index 3b47c7d085..b3966fd01a 100644 --- a/packages/recorder/src/recorder.tsx +++ b/packages/recorder/src/recorder.tsx @@ -167,26 +167,27 @@ export const Recorder: React.FC = ({ }}> toggleTheme()}> - - - } + sidebar={ copy(locator)} />] : []} tabs={[ { id: 'locator', title: 'Locator', - render: () => + render: () => }, { id: 'log', title: 'Log', - render: () => + render: () => }, ]} selectedTab={selectedTab} setSelectedTab={setSelectedTab} - /> - + />} + /> ; }; diff --git a/packages/trace-viewer/src/ui/networkTab.tsx b/packages/trace-viewer/src/ui/networkTab.tsx index 311af733a9..3158ad0e67 100644 --- a/packages/trace-viewer/src/ui/networkTab.tsx +++ b/packages/trace-viewer/src/ui/networkTab.tsx @@ -101,10 +101,15 @@ export const NetworkTab: React.FunctionComponent<{ />; return <> {!selectedEntry && grid} - {selectedEntry && - setSelectedEntry(undefined)} /> - {grid} - } + {selectedEntry && + setSelectedEntry(undefined)} />} + sidebar={grid} + />} ; }; diff --git a/packages/trace-viewer/src/ui/sourceTab.tsx b/packages/trace-viewer/src/ui/sourceTab.tsx index 27d64146ec..121b226216 100644 --- a/packages/trace-viewer/src/ui/sourceTab.tsx +++ b/packages/trace-viewer/src/ui/sourceTab.tsx @@ -96,17 +96,20 @@ export const SourceTab: React.FunctionComponent<{ const showStackFrames = (stack?.length ?? 0) > 1; - return -
+ return { fileName && {fileName} {location && } } -
- -
; + } + sidebar={} + />; }; export async function calculateSha1(text: string): Promise { diff --git a/packages/trace-viewer/src/ui/uiModeView.tsx b/packages/trace-viewer/src/ui/uiModeView.tsx index 98d19649eb..3484f3c749 100644 --- a/packages/trace-viewer/src/ui/uiModeView.tsx +++ b/packages/trace-viewer/src/ui/uiModeView.tsx @@ -433,8 +433,13 @@ export const UIModeView: React.FC<{}> = ({
UI Mode disconnected
} - -
+
Output
@@ -452,8 +457,8 @@ export const UIModeView: React.FC<{}> = ({ onOpenExternally={location => testServerConnection?.openNoReply({ location: { file: location.file, line: location.line, column: location.column } })} />
-
-
+
} + sidebar={
Playwright logo
Playwright
@@ -530,6 +535,7 @@ export const UIModeView: React.FC<{}> = ({ showRouteActionsSetting, ]} />}
-
+ } + /> ; }; diff --git a/packages/trace-viewer/src/ui/workbench.tsx b/packages/trace-viewer/src/ui/workbench.tsx index 189e9265a9..a213d15c59 100644 --- a/packages/trace-viewer/src/ui/workbench.tsx +++ b/packages/trace-viewer/src/ui/workbench.tsx @@ -293,9 +293,15 @@ export const Workbench: React.FunctionComponent<{ selectedTime={selectedTime} setSelectedTime={setSelectedTime} /> - - - - - - } + sidebar={ + + } + />} + sidebar={ ]} mode={sidebarLocation === 'bottom' ? 'default' : 'select'} - /> - + />} + /> ; }; diff --git a/packages/web/src/components/splitView.spec.tsx b/packages/web/src/components/splitView.spec.tsx index fc376bd2ef..a9260a2d48 100644 --- a/packages/web/src/components/splitView.spec.tsx +++ b/packages/web/src/components/splitView.spec.tsx @@ -20,10 +20,12 @@ import { SplitView } from './splitView'; test.use({ viewport: { width: 500, height: 500 } }); test('should render', async ({ mount }) => { - const component = await mount( -
main
- -
); + const component = await mount( + main} + sidebar={} + />); const mainBox = await component.locator('#main').boundingBox(); const sidebarBox = await component.locator('#sidebar').boundingBox(); expect.soft(mainBox).toEqual({ x: 0, y: 0, width: 500, height: 400 }); @@ -31,10 +33,13 @@ test('should render', async ({ mount }) => { }); test('should render sidebar first', async ({ mount }) => { - const component = await mount( -
main
- -
); + const component = await mount( + main} + sidebar={} + />); const mainBox = await component.locator('#main').boundingBox(); const sidebarBox = await component.locator('#sidebar').boundingBox(); expect.soft(mainBox).toEqual({ x: 0, y: 100, width: 500, height: 400 }); @@ -42,10 +47,14 @@ test('should render sidebar first', async ({ mount }) => { }); test('should render horizontal split', async ({ mount }) => { - const component = await mount( -
main
- -
); + const component = await mount( + main} + sidebar={} + />); const mainBox = await component.locator('#main').boundingBox(); const sidebarBox = await component.locator('#sidebar').boundingBox(); expect.soft(mainBox).toEqual({ x: 100, y: 0, width: 400, height: 500 }); @@ -53,19 +62,25 @@ test('should render horizontal split', async ({ mount }) => { }); test('should hide sidebar', async ({ mount }) => { - const component = await mount( -
main
- -
); + const component = await mount( + main} + sidebar={} + />); const mainBox = await component.locator('#main').boundingBox(); expect.soft(mainBox).toEqual({ x: 0, y: 0, width: 500, height: 500 }); }); test('drag resize', async ({ page, mount }) => { - const component = await mount( -
main
- -
); + const component = await mount( + main} + sidebar={} + />); await page.mouse.move(25, 400); await page.mouse.down(); await page.mouse.move(25, 100); diff --git a/packages/web/src/components/splitView.tsx b/packages/web/src/components/splitView.tsx index 4c4513f235..776b8d842f 100644 --- a/packages/web/src/components/splitView.tsx +++ b/packages/web/src/components/splitView.tsx @@ -25,18 +25,22 @@ export type SplitViewProps = { orientation?: 'vertical' | 'horizontal'; minSidebarSize?: number; settingName?: string; + + sidebar: React.ReactNode; + main: React.ReactNode; }; const kMinSize = 50; -export const SplitView: React.FC> = ({ +export const SplitView: React.FC = ({ sidebarSize, sidebarHidden = false, sidebarIsFirst = false, orientation = 'vertical', minSidebarSize = kMinSize, settingName, - children + sidebar, + main, }) => { const defaultSize = Math.max(minSidebarSize, sidebarSize) * window.devicePixelRatio; const hSetting = useSetting((settingName ?? 'unused') + '.' + orientation + ':size', defaultSize); @@ -60,7 +64,6 @@ export const SplitView: React.FC> = ({ size = measure.width - 10; } - const childrenArray = React.Children.toArray(children); document.body.style.userSelect = resizing ? 'none' : 'inherit'; let resizerStyle: any = {}; if (orientation === 'vertical') { @@ -75,9 +78,9 @@ export const SplitView: React.FC> = ({ resizerStyle = { right: resizing ? 0 : size - 4, left: resizing ? 0 : undefined, width: resizing ? 'initial' : 8 }; } - return
-
{childrenArray[0]}
- { !sidebarHidden &&
{childrenArray[1]}
} + return
+
{main}
+ { !sidebarHidden &&
{sidebar}
} { !sidebarHidden &&