Skip to content

Commit f29b9d4

Browse files
authored
feat(browser): add browser iframe mouse interaction (#5815)
1 parent f9d9b3b commit f29b9d4

File tree

6 files changed

+70
-22
lines changed

6 files changed

+70
-22
lines changed

packages/browser/src/client/orchestrator.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ function createIframe(container: HTMLDivElement, file: string) {
4444

4545
iframe.style.display = 'block'
4646
iframe.style.border = 'none'
47-
iframe.style.pointerEvents = 'none'
4847
iframe.setAttribute('allowfullscreen', 'true')
4948
iframe.setAttribute('allow', 'clipboard-write;')
5049

@@ -187,7 +186,7 @@ async function createTesters(testFiles: string[]) {
187186
const container = await getContainer(config)
188187

189188
if (config.browser.ui) {
190-
container.className = ''
189+
container.className = 'scrolls'
191190
container.textContent = ''
192191
}
193192

packages/ui/client/auto-imports.d.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -96,20 +96,22 @@ declare global {
9696
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
9797
const provide: typeof import('vue')['provide']
9898
const provideLocal: typeof import('@vueuse/core')['provideLocal']
99+
const provideResizing: typeof import('./composables/browser')['provideResizing']
99100
const reactify: typeof import('@vueuse/core')['reactify']
100101
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
101102
const reactive: typeof import('vue')['reactive']
102103
const reactiveComputed: typeof import('@vueuse/core')['reactiveComputed']
103104
const reactiveOmit: typeof import('@vueuse/core')['reactiveOmit']
104105
const reactivePick: typeof import('@vueuse/core')['reactivePick']
105106
const readonly: typeof import('vue')['readonly']
106-
const recalculateDetailPanels: typeof import('./composables/navigation')['recalculateDetailPanels']
107+
const recalculateDetailPanels: typeof import('./composables/browser')['recalculateDetailPanels']
107108
const ref: typeof import('vue')['ref']
108109
const refAutoReset: typeof import('@vueuse/core')['refAutoReset']
109110
const refDebounced: typeof import('@vueuse/core')['refDebounced']
110111
const refDefault: typeof import('@vueuse/core')['refDefault']
111112
const refThrottled: typeof import('@vueuse/core')['refThrottled']
112113
const refWithControl: typeof import('@vueuse/core')['refWithControl']
114+
const registerResizingListener: typeof import('./composables/browser')['registerResizingListener']
113115
const resolveComponent: typeof import('vue')['resolveComponent']
114116
const resolveRef: typeof import('@vueuse/core')['resolveRef']
115117
const resolveUnref: typeof import('@vueuse/core')['resolveUnref']
@@ -239,6 +241,7 @@ declare global {
239241
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
240242
const useNavigatorLanguage: typeof import('@vueuse/core')['useNavigatorLanguage']
241243
const useNetwork: typeof import('@vueuse/core')['useNetwork']
244+
const useNotifyResizing: typeof import('./composables/browser')['useNotifyResizing']
242245
const useNow: typeof import('@vueuse/core')['useNow']
243246
const useObjectUrl: typeof import('@vueuse/core')['useObjectUrl']
244247
const useOffsetPagination: typeof import('@vueuse/core')['useOffsetPagination']
@@ -260,6 +263,7 @@ declare global {
260263
const useRafFn: typeof import('@vueuse/core')['useRafFn']
261264
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
262265
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
266+
const useResizing: typeof import('./composables/browser')['useResizing']
263267
const useRoute: typeof import('vue-router')['useRoute']
264268
const useRouter: typeof import('vue-router')['useRouter']
265269
const useScreenOrientation: typeof import('@vueuse/core')['useScreenOrientation']

packages/ui/client/components/BrowserIframe.vue

+13-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
<script setup lang="ts">
2-
const viewport = ref('custom')
3-
import { recalculateDetailPanels } from '~/composables/navigation'
2+
import { useResizing } from '~/composables/browser'
43
5-
const sizes = {
4+
type ViewportSize = 'small-mobile' | 'large-mobile' | 'tablet' | 'custom'
5+
6+
const sizes: Record<ViewportSize, [width: string, height: string]> = {
67
'small-mobile': ['320px', '568px'],
78
'large-mobile': ['414px', '896px'],
89
tablet: ['834px', '1112px'],
910
custom: ['100%', '100%'],
1011
}
1112
12-
async function changeViewport(name: string) {
13+
const testerRef = ref<HTMLDivElement | undefined>()
14+
const viewport = ref<ViewportSize>('custom')
15+
16+
const { recalculateDetailPanels } = useResizing(testerRef)
17+
18+
async function changeViewport(name: ViewportSize) {
1319
if (viewport.value === name) {
1420
viewport.value = 'custom'
1521
} else {
1622
viewport.value = name
1723
}
1824
19-
const iframe = document.querySelector('#tester-ui iframe[data-vitest]')
25+
const iframe = document.querySelector<HTMLIFrameElement>('#tester-ui iframe[data-vitest]')
2026
if (!iframe) {
2127
console.warn('Iframe not found')
2228
return
@@ -84,8 +90,8 @@ async function changeViewport(name: string) {
8490
@click="changeViewport('tablet')"
8591
/>
8692
</div>
87-
<div flex-auto overflow-auto>
88-
<div id="tester-ui" class="flex h-full justify-center items-center font-light op70" style="overflow: auto; width: 100%; height: 100%">
93+
<div flex-auto class="scrolls">
94+
<div id="tester-ui" ref="testerRef" class="flex h-full justify-center items-center font-light op70" style="overflow: auto; width: 100%; height: 100%">
8995
Select a test to run
9096
</div>
9197
</div>
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { Ref } from 'vue'
2+
import { detailSizes } from '~/composables/navigation'
3+
4+
type ResizingListener = (isResizing: boolean) => void
5+
6+
const resizingListeners = new Set<ResizingListener>()
7+
8+
export function recalculateDetailPanels() {
9+
const iframe = document.querySelector('#tester-ui iframe[data-vitest]')!
10+
const panel = document.querySelector('#details-splitpanes')!
11+
const panelWidth = panel.clientWidth
12+
const iframeWidth = iframe.clientWidth
13+
const iframePercent = Math.min((iframeWidth / panelWidth) * 100, 95)
14+
const detailsPercent = 100 - iframePercent
15+
detailSizes.value = [iframePercent, detailsPercent]
16+
}
17+
18+
export function useResizing(testerRef: Ref<HTMLDivElement | undefined>) {
19+
function onResizing(isResizing: boolean) {
20+
const tester = testerRef.value
21+
if (!tester)
22+
return
23+
24+
tester.style.pointerEvents = isResizing ? 'none' : ''
25+
}
26+
27+
onMounted(() => {
28+
resizingListeners.add(onResizing)
29+
})
30+
31+
onUnmounted(() => {
32+
resizingListeners.delete(onResizing)
33+
})
34+
35+
return { recalculateDetailPanels }
36+
}
37+
38+
export function useNotifyResizing() {
39+
function notifyResizing(isResizing: boolean) {
40+
for (const listener of resizingListeners)
41+
listener(isResizing)
42+
}
43+
44+
return { notifyResizing }
45+
}

packages/ui/client/composables/navigation.ts

-10
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,6 @@ export const detailSizes = useLocalStorage<[left: number, right: number]>('vites
1616
initOnMounted: true,
1717
})
1818

19-
export function recalculateDetailPanels() {
20-
const iframe = document.querySelector('#tester-ui iframe[data-vitest]')!
21-
const panel = document.querySelector('#details-splitpanes')!
22-
const panelWidth = panel.clientWidth
23-
const iframeWidth = iframe.clientWidth
24-
const iframePercent = Math.min((iframeWidth / panelWidth) * 100, 95)
25-
const detailsPercent = 100 - iframePercent
26-
detailSizes.value = [iframePercent, detailsPercent]
27-
}
28-
2919
// @ts-expect-error not typed global
3020
window.__vitest_ui_api__ = {
3121
get currentModule() {

packages/ui/client/pages/index.vue

+6-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22
// @ts-expect-error missing types
33
import { Pane, Splitpanes } from 'splitpanes'
44
import { browserState } from '~/composables/client';
5-
import { coverageUrl, coverageVisible, initializeNavigation, detailSizes } from '../composables/navigation'
5+
import { coverageUrl, coverageVisible, initializeNavigation, detailSizes } from '~/composables/navigation'
6+
import { useNotifyResizing } from '~/composables/browser'
67
8+
const { notifyResizing } = useNotifyResizing()
79
const dashboardVisible = initializeNavigation()
10+
811
const mainSizes = useLocalStorage<[left: number, right: number]>('vitest-ui_splitpanes-mainSizes', [33, 67], {
912
initOnMounted: true,
1013
})
@@ -18,6 +21,7 @@ const onModuleResized = useDebounceFn((event: { size: number }[]) => {
1821
event.forEach((e, i) => {
1922
detailSizes.value[i] = e.size
2023
})
24+
notifyResizing(false)
2125
}, 0)
2226
2327
function resizeMain() {
@@ -41,7 +45,7 @@ function resizeMain() {
4145
<Coverage v-else-if="coverageVisible" key="coverage" :src="coverageUrl" />
4246
<FileDetails v-else />
4347
</transition>
44-
<Splitpanes v-else key="detail" id="details-splitpanes" @resized="onModuleResized">
48+
<Splitpanes v-else key="detail" id="details-splitpanes" @resize="notifyResizing(true)" @resized="onModuleResized">
4549
<Pane :size="detailSizes[0]">
4650
<BrowserIframe v-once />
4751
</Pane>

0 commit comments

Comments
 (0)