diff --git a/packages/app/src/runner/ResizablePanels.cy.tsx b/packages/app/src/runner/ResizablePanels.cy.tsx
index a60844cce61d..840e973a9c44 100644
--- a/packages/app/src/runner/ResizablePanels.cy.tsx
+++ b/packages/app/src/runner/ResizablePanels.cy.tsx
@@ -5,9 +5,11 @@ import { runnerConstants } from './runner-constants'
// default values
const defaultPanel1Width = runnerConstants.defaultSpecListWidth
const defaultPanel2Width = runnerConstants.defaultReporterWidth
+const defaultPanel4Width = runnerConstants.defaultStudioWidth
const minPanel1Width = 100
const minPanel2Width = 100
const minPanel3Width = 500
+const minPanel4Width = runnerConstants.absoluteStudioMinimum
// helpers
const assertWidth = (panel: ResizablePanelName, width: number) => {
@@ -38,9 +40,11 @@ describe('', { viewportWidth: 1500, defaultCommandTimeout: 40
v-slots={slotContents}
initialPanel1Width={defaultPanel1Width}
initialPanel2Width={defaultPanel2Width}
+ initialPanel4Width={defaultPanel4Width}
minPanel1Width={minPanel1Width}
minPanel2Width={minPanel2Width}
minPanel3Width={minPanel3Width}
+ minPanel4Width={minPanel4Width}
/>
))
})
@@ -106,6 +110,128 @@ describe('', { viewportWidth: 1500, defaultCommandTimeout: 40
})
})
+ describe('when panel 4 is shown', () => {
+ beforeEach(() => {
+ cy.mount(() => (
+
))
+ })
+
+ it('the panels can be resized', () => {
+ assertWidth('panel1', defaultPanel1Width)
+ dragHandleToClientX('panel1', 500)
+ assertWidth('panel1', 500)
+ dragHandleToClientX('panel1', 400)
+ assertWidth('panel1', 400)
+
+ assertWidth('panel2', defaultPanel2Width)
+ dragHandleToClientX('panel2', 800)
+ assertWidth('panel2', 400)
+ dragHandleToClientX('panel2', 700)
+ assertWidth('panel2', 300)
+
+ assertWidth('panel4', defaultPanel4Width)
+ dragHandleToClientX('panel4', 1300)
+ assertWidth('panel4', 700)
+ dragHandleToClientX('panel4', 1500)
+ assertWidth('panel4', 500)
+ })
+
+ it('panel 1 can be resized between its minimum allowed width and maximum available space', () => {
+ // drag panel 1 to its minimum width and attempt to go below it
+ assertWidth('panel1', defaultPanel1Width)
+ dragHandleToClientX('panel1', 100)
+ dragHandleToClientX('panel1', 99)
+ assertWidth('panel1', minPanel1Width)
+ dragHandleToClientX('panel1', 50)
+ assertWidth('panel1', minPanel1Width)
+
+ // drag panel 1 to the maximum space available and attempt to go above it
+ dragHandleToClientX('panel1', 710)
+ dragHandleToClientX('panel1', 800)
+ assertWidth('panel1', 710)
+ dragHandleToClientX('panel1', 900)
+ assertWidth('panel1', 710)
+
+ // panel 2 was not reduced
+ assertWidth('panel2', defaultPanel2Width)
+
+ // panel 3 reached its minimum allowed size
+ assertWidth('panel3', 500)
+
+ // panel 4 was not reduced
+ assertWidth('panel4', defaultPanel4Width)
+ })
+
+ it('panel 2 can be resized between its minimum allowed width and maximum available space', () => {
+ // drag panel 2 to its minimum width and attempt to go below it
+ assertWidth('panel2', defaultPanel2Width)
+ dragHandleToClientX('panel2', 380)
+ dragHandleToClientX('panel2', 200)
+ assertWidth('panel2', minPanel2Width)
+ dragHandleToClientX('panel2', 180)
+ assertWidth('panel2', minPanel2Width)
+
+ // drag panel 2 to the maximum space available and attempt to go above it
+ dragHandleToClientX('panel2', 1160)
+ dragHandleToClientX('panel2', 1200)
+ assertWidth('panel2', 880)
+ dragHandleToClientX('panel2', 1300)
+ assertWidth('panel2', 880)
+
+ // panel 1 was not reduced
+ assertWidth('panel1', defaultPanel1Width)
+
+ // panel 3 reached its minimum allowed size
+ assertWidth('panel3', minPanel3Width)
+
+ // panel 4 was not reduced
+ assertWidth('panel4', defaultPanel4Width)
+ })
+
+ it('panel 4 can be resized between its minimum allowed width and maximum available space', () => {
+ // since its starting width is the same as its minimum width,
+ // drag panel 4 to a different width, then drag it to its minimum width and attempt to go below it
+ assertWidth('panel4', defaultPanel4Width)
+ dragHandleToClientX('panel4', 1400)
+ assertWidth('panel4', 600)
+ dragHandleToClientX('panel4', 1660)
+ dragHandleToClientX('panel4', 1800)
+ assertWidth('panel4', minPanel4Width)
+ dragHandleToClientX('panel4', 1900)
+ assertWidth('panel4', minPanel4Width)
+
+ // drag panel 4 to the maximum space available and attempt to go above it
+ dragHandleToClientX('panel4', 1230)
+ dragHandleToClientX('panel4', 1100)
+ assertWidth('panel4', 770)
+ dragHandleToClientX('panel4', 900)
+ assertWidth('panel4', 770)
+
+ // panel 1 was not reduced
+ assertWidth('panel1', defaultPanel1Width)
+
+ // panel 2 was not reduced
+ assertWidth('panel2', defaultPanel2Width)
+
+ // panel 3 reached its absolute minimum allowed size
+ assertWidth('panel3', minPanel3Width)
+ })
+ })
+
describe('when there is a side nav', () => {
it('handles being offset by some distance on the left', () => {
cy.mount(() => (
diff --git a/packages/app/src/runner/ResizablePanels.vue b/packages/app/src/runner/ResizablePanels.vue
index 2a8a8bc32f54..dbe769dff330 100644
--- a/packages/app/src/runner/ResizablePanels.vue
+++ b/packages/app/src/runner/ResizablePanels.vue
@@ -4,7 +4,7 @@
id="resizable-panels-root"
class="flex"
:class="{
- 'select-none': panel1IsDragging || panel2IsDragging,
+ 'select-none': panel1IsDragging || panel2IsDragging || panel4IsDragging,
}"
@mouseup="handleMouseup"
@mousemove="handleMousemove"
@@ -14,7 +14,7 @@
v-show="showPanel1"
data-cy="specs-list-panel"
class="h-full shrink-0 z-20 relative"
- :style="{width: `${panel1Width}px`}"
+ :style="{ width: `${panel1Width}px` }"
>
@@ -46,7 +46,7 @@
@@ -86,9 +89,11 @@ const props = withDefaults(defineProps<{
showPanel4?: boolean // studio in runner
initialPanel1Width?: number
initialPanel2Width?: number
+ initialPanel4Width?: number
minPanel1Width?: number
minPanel2Width?: number
minPanel3Width?: number
+ minPanel4Width?: number
maxTotalWidth?: number // windowWidth in runner
offsetLeft?: number
}>(), {
@@ -97,23 +102,28 @@ const props = withDefaults(defineProps<{
showPanel4: false,
initialPanel1Width: runnerConstants.defaultSpecListWidth,
initialPanel2Width: runnerConstants.defaultReporterWidth,
+ initialPanel4Width: runnerConstants.defaultStudioWidth,
minPanel1Width: 200,
minPanel2Width: 220,
minPanel3Width: 100,
+ minPanel4Width: 340,
maxTotalWidth: window.innerWidth,
offsetLeft: 0,
})
const emit = defineEmits<{
(e: 'resizeEnd', value: DraggablePanel): void
- (e: 'panelWidthUpdated', value: {panel: DraggablePanel, width: number}): void
+ (e: 'panelWidthUpdated', value: { panel: DraggablePanel, width: number }): void
}>()
const panel1HandleX = ref(props.initialPanel1Width)
const panel2HandleX = ref(props.initialPanel2Width + props.initialPanel1Width)
+const panel4HandleX = ref(props.initialPanel2Width + props.initialPanel1Width + props.initialPanel4Width)
const panel1IsDragging = ref(false)
const panel2IsDragging = ref(false)
+const panel4IsDragging = ref(false)
const cachedPanel1Width = ref(props.initialPanel1Width) // because panel 1 (the inline specs list) can be opened and closed in the UI, we cache the width
+const cachedPanel4Width = ref(props.initialPanel4Width)
const panel2Width = ref(props.initialPanel2Width)
const handleMousedown = (panel: DraggablePanel, event: MouseEvent) => {
@@ -122,10 +132,13 @@ const handleMousedown = (panel: DraggablePanel, event: MouseEvent) => {
} else if (panel === 'panel2') {
panel2IsDragging.value = true
panel2HandleX.value = event.clientX
+ } else if (panel === 'panel4') {
+ panel4IsDragging.value = true
+ panel4HandleX.value = event.clientX
}
}
const handleMousemove = (event: MouseEvent) => {
- if (!panel1IsDragging.value && !panel2IsDragging.value) {
+ if (!panel1IsDragging.value && !panel2IsDragging.value && !panel4IsDragging.value) {
// nothing is dragging, ignore mousemove
return
@@ -139,6 +152,15 @@ const handleMousemove = (event: MouseEvent) => {
panel2HandleX.value = event.clientX
panel2Width.value = event.clientX - props.offsetLeft - panel1Width.value
emit('panelWidthUpdated', { panel: 'panel2', width: panel2Width.value })
+ } else if (panel4IsDragging.value && isNewWidthAllowed(event.clientX, 'panel4')) {
+ panel4HandleX.value = event.clientX
+ // Calculate width from the right edge of the window
+ // so that when we drag the panel to the left, it grows
+ // and when we drag it to the right, it shrinks
+ const rightEdge = props.maxTotalWidth + props.offsetLeft
+
+ cachedPanel4Width.value = rightEdge - event.clientX
+ emit('panelWidthUpdated', { panel: 'panel4', width: panel4Width.value })
}
}
const handleMouseup = () => {
@@ -149,30 +171,37 @@ const handleMouseup = () => {
return
}
- handleResizeEnd('panel2')
- panel2IsDragging.value = false
+ if (panel2IsDragging.value) {
+ handleResizeEnd('panel2')
+ panel2IsDragging.value = false
+ }
+
+ if (panel4IsDragging.value) {
+ handleResizeEnd('panel4')
+ panel4IsDragging.value = false
+ }
}
const maxPanel1Width = computed(() => {
- const unavailableWidth = panel2Width.value + props.minPanel3Width
+ const unavailableWidth = panel2Width.value + props.minPanel3Width + panel4Width.value
return props.maxTotalWidth - unavailableWidth
})
-const panel4Width = computed(() => {
- if (!props.showPanel4) {
+const panel1Width = computed(() => {
+ if (!props.showPanel1) {
return 0
}
- return runnerConstants.defaultStudioWidth
+ return cachedPanel1Width.value
})
-const panel1Width = computed(() => {
- if (!props.showPanel1) {
+const panel4Width = computed(() => {
+ if (!props.showPanel4) {
return 0
}
- return cachedPanel1Width.value
+ return cachedPanel4Width.value
})
const maxPanel2Width = computed(() => {
@@ -192,6 +221,12 @@ const panel3width = computed(() => {
return panel3SpaceAvailable < props.minPanel3Width ? minimumWithBuffer : panel3SpaceAvailable
})
+const maxPanel4Width = computed(() => {
+ const unavailableWidth = panel1Width.value + panel2Width.value + props.minPanel3Width
+
+ return props.maxTotalWidth - unavailableWidth
+})
+
function handleResizeEnd (panel: DraggablePanel) {
emit('resizeEnd', panel)
}
@@ -212,15 +247,29 @@ function isNewWidthAllowed (mouseClientX: number, panel: DraggablePanel) {
return result
}
- const newWidth = mouseClientX - props.offsetLeft - panel1Width.value
+ if (panel === 'panel2') {
+ const newWidth = mouseClientX - props.offsetLeft - panel1Width.value
+
+ if (isMaxWidthSmall && newWidth > fallbackWidth) {
+ return true
+ }
+
+ return panel2IsDragging.value && newWidth >= props.minPanel2Width && newWidth <= maxPanel2Width.value
+ }
+
+ if (panel === 'panel4') {
+ const rightEdge = props.maxTotalWidth + props.offsetLeft
+ const newWidth = rightEdge - mouseClientX
+
+ if (isMaxWidthSmall && newWidth >= props.minPanel4Width) {
+ return true
+ }
- if (isMaxWidthSmall && newWidth > fallbackWidth) {
- return true
+ return panel4IsDragging.value && newWidth >= props.minPanel4Width && newWidth <= maxPanel4Width.value
}
- return panel2IsDragging.value && newWidth >= props.minPanel2Width && newWidth <= maxPanel2Width.value
+ return false
}
-
watchEffect(() => {
if (!props.showPanel1) {
emit('panelWidthUpdated', { panel: 'panel1', width: 0 })
@@ -231,7 +280,7 @@ watchEffect(() => {
if (!props.showPanel4) {
emit('panelWidthUpdated', { panel: 'panel4', width: 0 })
} else if (props.showPanel4) {
- emit('panelWidthUpdated', { panel: 'panel4', width: panel4Width.value })
+ emit('panelWidthUpdated', { panel: 'panel4', width: cachedPanel4Width.value })
}
})
diff --git a/packages/app/src/runner/SpecRunnerOpenMode.vue b/packages/app/src/runner/SpecRunnerOpenMode.vue
index cc5bd3b1de4a..b7a6a5aaf31a 100644
--- a/packages/app/src/runner/SpecRunnerOpenMode.vue
+++ b/packages/app/src/runner/SpecRunnerOpenMode.vue
@@ -26,9 +26,11 @@
:max-total-width="windowWidth - collapsedNavBarWidth"
:initial-panel1-width="specsListWidthPreferences"
:initial-panel2-width="reporterWidthPreferences"
+ :initial-panel4-width="studioWidthPreferences"
:min-panel1-width="minWidths.specsList"
:min-panel2-width="minWidths.reporter"
:min-panel3-width="minWidths.aut"
+ :min-panel4-width="minWidths.studio"
:show-panel1="runnerUiStore.isSpecsListOpen && !screenshotStore.isScreenshotting"
:show-panel2="!screenshotStore.isScreenshotting && !hideCommandLog"
:show-panel4="shouldShowStudioPanel"
@@ -149,6 +151,7 @@ const {
absoluteAutMinimum,
absoluteSpecListMinimum,
absoluteReporterMinimum,
+ absoluteStudioMinimum,
collapsedNavBarWidth,
} = runnerConstants
@@ -278,7 +281,7 @@ const shouldShowStudioButton = computed(() => {
})
const shouldShowStudioPanel = computed(() => {
- return !!cloudStudioRequested.value && (studioStore.isLoading || studioStore.isActive)
+ return !!cloudStudioRequested.value && (studioStore.isLoading || studioStore.isActive) && !screenshotStore.isScreenshotting
})
const hideCommandLog = runnerUiStore.hideCommandLog
@@ -330,6 +333,7 @@ const minWidths = computed(() => {
aut: getMinimum(absoluteAutMinimum, doesContentFit),
specsList: getMinimum(absoluteSpecListMinimum, doesContentFit),
reporter: getMinimum(absoluteReporterMinimum, doesContentFit),
+ studio: absoluteStudioMinimum,
}
})
diff --git a/packages/app/src/runner/runner-constants.ts b/packages/app/src/runner/runner-constants.ts
index 1165cfaf6d8b..e80fb2cd09cd 100644
--- a/packages/app/src/runner/runner-constants.ts
+++ b/packages/app/src/runner/runner-constants.ts
@@ -6,5 +6,6 @@ export const runnerConstants = {
absoluteAutMinimum: 100,
absoluteSpecListMinimum: 50,
absoluteReporterMinimum: 50,
+ absoluteStudioMinimum: 340,
collapsedNavBarWidth: 64,
}