Skip to content

Commit af0da61

Browse files
sainthkhBlue F
and
Blue F
authored
chore: Migrate AssertionsOptions menu to Vue. (#24286)
* React to vue * Goodbye dom. * Use event modifier. * Remove leading underscore in names * Use modifier. * Add flip, shift options. * Use createPopper instead of floating-ui. * Remove unnecessary dependencies. * addAssertion to event. * setPopperElement to event. * Strong types. * clarify test. Co-authored-by: Blue F <blue@cypress.io>
1 parent 51f30a2 commit af0da61

19 files changed

+574
-543
lines changed

packages/app/cypress/e2e/studio/studio.cy.ts

+23
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,27 @@ it('visits a basic html page', () => {
255255
// Cypress in Cypress, it redirects us the the spec page, which is not what normally
256256
// would happen in production.
257257
})
258+
259+
it('shows menu and submenu correctly', () => {
260+
launchStudio()
261+
262+
cy.getAutIframe().within(() => {
263+
// Show menu
264+
cy.get('h1').realClick({
265+
button: 'right',
266+
})
267+
268+
cy.get('.__cypress-studio-assertions-menu').shadow()
269+
.find('.assertions-menu').should('be.visible')
270+
271+
// Show submenu
272+
cy.get('.__cypress-studio-assertions-menu').shadow()
273+
.find('.assertion-type-text:first').realHover()
274+
275+
cy.get('.__cypress-studio-assertions-menu').shadow()
276+
.find('.assertion-option')
277+
.should('have.text', 'Hello, Studio!')
278+
.should('be.visible')
279+
})
280+
})
258281
})

packages/app/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@intlify/vite-plugin-vue-i18n": "2.4.0",
3030
"@packages/frontend-shared": "0.0.0-development",
3131
"@percy/cypress": "^3.1.0",
32+
"@popperjs/core": "2.11.6",
3233
"@testing-library/cypress": "BlueWinds/cypress-testing-library#119054b5963b0d2e064b13c5cc6fc9db32c8b7b5",
3334
"@types/faker": "5.5.8",
3435
"@urql/core": "2.4.4",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<template>
2+
<div
3+
ref="popper"
4+
class="assertion-options"
5+
>
6+
<div
7+
v-for="{ name, value } in options"
8+
:key="`${name}${value}`"
9+
class="assertion-option"
10+
@click.stop="() => onClick(name, value)"
11+
>
12+
<span
13+
v-if="name"
14+
class="assertion-option-name"
15+
>
16+
{{ truncate(name) }}:{{ ' ' }}
17+
</span>
18+
<span
19+
v-else
20+
class="assertion-option-value"
21+
>
22+
{{ typeof value === 'string' && truncate(value) }}
23+
</span>
24+
</div>
25+
</div>
26+
</template>
27+
28+
<script lang="ts" setup>
29+
import { createPopper } from '@popperjs/core'
30+
import { onMounted, ref, nextTick, Ref } from 'vue'
31+
import type { AssertionOption } from './types'
32+
33+
const props = defineProps<{
34+
type: string
35+
options: AssertionOption[]
36+
}>()
37+
38+
const emit = defineEmits<{
39+
(eventName: 'addAssertion', value: { type: string, name: string, value: string })
40+
(eventName: 'setPopperElement', value: HTMLElement)
41+
}>()
42+
43+
const truncate = (str: string) => {
44+
if (str && str.length > 80) {
45+
return `${str.substr(0, 77)}...`
46+
}
47+
48+
return str
49+
}
50+
51+
const popper: Ref<HTMLElement | null> = ref(null)
52+
53+
onMounted(() => {
54+
nextTick(() => {
55+
const popperEl = popper.value as HTMLElement
56+
const reference = popperEl.parentElement as HTMLElement
57+
58+
createPopper(reference, popperEl, {
59+
placement: 'right-start',
60+
})
61+
62+
emit('setPopperElement', popperEl)
63+
})
64+
})
65+
66+
const onClick = (name, value) => {
67+
emit('addAssertion', { type: props.type, name, value })
68+
}
69+
</script>
70+
71+
<style lang="scss">
72+
@import './assertions-style.scss';
73+
74+
.assertion-options {
75+
@include menu-style;
76+
77+
font-size: 14px;
78+
max-width: 150px;
79+
overflow: hidden;
80+
overflow-wrap: break-word;
81+
position: absolute;
82+
83+
.assertion-option {
84+
cursor: pointer;
85+
padding: 0.4rem 0.6rem;
86+
87+
&:hover {
88+
background-color: #e9ecef;
89+
}
90+
91+
.assertion-option-value {
92+
font-weight: 600;
93+
}
94+
}
95+
}
96+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<template>
2+
<div
3+
:class="['assertion-type', { 'single-assertion': !hasOptions }]"
4+
@click.stop="onClick"
5+
@mouseover.stop="onOpen"
6+
@mouseout.stop="onClose"
7+
>
8+
<div class="assertion-type-text">
9+
<span>
10+
{{ type.replace(/\./g, ' ') }}
11+
</span>
12+
<span
13+
v-if="hasOptions"
14+
class="dropdown-arrow"
15+
>
16+
<svg
17+
xmlns="http://www.w3.org/2000/svg"
18+
width="12"
19+
height="12"
20+
fill="currentColor"
21+
viewBox="0 0 16 16"
22+
>
23+
<path
24+
fillRule="evenodd"
25+
d="M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z"
26+
/>
27+
</svg>
28+
</span>
29+
</div>
30+
<AssertionOptions
31+
v-if="hasOptions && isOpen"
32+
:type="type"
33+
:options="options"
34+
@set-popper-element="setPopperElement"
35+
@add-assertion="addAssertion"
36+
/>
37+
</div>
38+
</template>
39+
40+
<script lang="ts" setup>
41+
import { Ref, ref } from 'vue'
42+
import AssertionOptions from './AssertionOptions.ce.vue'
43+
44+
const props = defineProps<{
45+
type: string
46+
options: any
47+
}>()
48+
49+
const emit = defineEmits<{
50+
(eventName: 'addAssertion', value: { type: string, name?: string, value?: string })
51+
}>()
52+
53+
const isOpen = ref(false)
54+
const hasOptions = props.options && !!props.options.length
55+
const popperElement: Ref<HTMLElement | null> = ref(null)
56+
57+
const onOpen = () => {
58+
isOpen.value = true
59+
}
60+
61+
const onClose = (e: MouseEvent) => {
62+
if (e.relatedTarget instanceof Element &&
63+
popperElement.value && popperElement.value.contains(e.relatedTarget)) {
64+
return
65+
}
66+
67+
isOpen.value = false
68+
}
69+
70+
const onClick = () => {
71+
if (!hasOptions) {
72+
emit('addAssertion', { type: props.type })
73+
}
74+
}
75+
76+
const setPopperElement = (el: HTMLElement) => {
77+
popperElement.value = el
78+
}
79+
80+
const addAssertion = ({ type, name, value }) => {
81+
emit('addAssertion', { type, name, value })
82+
}
83+
</script>
84+
85+
<style lang="scss">
86+
@import './assertions-style.scss';
87+
88+
.assertion-type {
89+
color: #202020;
90+
cursor: default;
91+
font-size: 14px;
92+
padding: 0.4rem 0.4rem 0.4rem 0.7rem;
93+
position: static;
94+
95+
&:first-of-type {
96+
padding-top: 0.5rem;
97+
}
98+
99+
&:last-of-type {
100+
border-bottom-left-radius: $border-radius;
101+
border-bottom-right-radius: $border-radius;
102+
padding-bottom: 0.5rem;
103+
}
104+
105+
&:hover {
106+
background-color: #e9ecef;
107+
}
108+
109+
&.single-assertion {
110+
cursor: pointer;
111+
font-weight: 600;
112+
}
113+
114+
.assertion-type-text {
115+
align-items: center;
116+
display: flex;
117+
118+
.dropdown-arrow {
119+
margin-left: auto;
120+
}
121+
}
122+
}
123+
</style>

0 commit comments

Comments
 (0)