chore: Migrate react Highlight component to Vue (#23973)

* add test.

* Add Highlight vue component + remove react component.

* Remove floating-ui dependency.

* fix test failure

Co-authored-by: Lachlan Miller <lachlan.miller.1990@outlook.com>
This commit is contained in:
Kukhyeon Heo
2022-10-12 03:41:54 +09:00
committed by GitHub
parent 53eef4fbd7
commit 0bb705c185
15 changed files with 221 additions and 225 deletions

View File

@@ -47,6 +47,5 @@ export const createTestAutIframe = (eventManager = createEventManager()) => {
'Test Project',
eventManager,
null, // CypressJQuery, shouldn't be using driver in component tests anyway
window.top?.UnifiedRunner.highlight,
)
}

View File

@@ -0,0 +1,71 @@
function launchApp () {
cy.scaffoldProject('selector-playground')
cy.openProject('selector-playground')
cy.startAppServer('e2e')
cy.visitApp()
cy.get(`[data-cy-row="spec.cy.js"]`).click()
cy.waitForSpecToFinish()
}
describe('selector playground', () => {
it('highlight the element when hover over it.', () => {
launchApp()
cy.get('[data-cy="playground-activator"]').click()
const backgroundColor = 'rgba(159, 196, 231, 0.6)'
cy.getAutIframe().within(() => {
cy.get('h1').realHover()
cy.get('.__cypress-selector-playground').shadow().within(() => {
// Test highlight exists
cy.get('.highlight').should('exist')
cy.get('.highlight').should('have.css', 'background-color', backgroundColor)
// Test tooltip text is correct
cy.get('.tooltip').should('have.text', 'h1')
// Test placement of tooltip
let highlightTop: any
let tooltipTop: any
cy.get('.highlight').then(($el) => {
highlightTop = parseFloat($el.css('top'))
})
cy.get('.tooltip').then(($el) => {
tooltipTop = parseFloat($el.css('top'))
expect(tooltipTop).to.be.greaterThan(highlightTop)
})
})
})
cy.getAutIframe().within(() => {
cy.get('h2').realHover()
cy.get('.__cypress-selector-playground').shadow().within(() => {
// Test highlight exists
cy.get('.highlight').should('exist')
cy.get('.highlight').should('have.css', 'background-color', backgroundColor)
// Test tooltip text is correct
cy.get('.tooltip').should('have.text', '[data-cy="h2-contents"]')
// Test placement of tooltip
let highlightTop: any
let tooltipTop: any
cy.get('.highlight').then(($el) => {
highlightTop = parseFloat($el.css('top'))
})
cy.get('.tooltip').then(($el) => {
tooltipTop = parseFloat($el.css('top'))
expect(tooltipTop).to.be.lessThan(highlightTop)
})
})
})
})
})

View File

@@ -7,6 +7,8 @@ import type { DebouncedFunc } from 'lodash'
import { useStudioStore } from '../store/studio-store'
import { getElementDimensions, setOffset } from './dimensions'
import { getOrCreateHelperDom, getSelectorHighlightStyles, getZIndex, INT32_MAX } from './dom'
import highlightMounter from './selector-playground/highlight-mounter'
import Highlight from './selector-playground/Highlight.ce.vue'
// JQuery bundled w/ Cypress
type $CypressJQuery = any
@@ -22,7 +24,6 @@ export class AutIframe {
private projectName: string,
private eventManager: any,
private $: $CypressJQuery,
private highlight: any,
) {
this.debouncedToggleSelectorPlayground = _.debounce(this.toggleSelectorPlayground, 300)
}
@@ -758,10 +759,10 @@ export class AutIframe {
private listeners: any[] = []
private _addOrUpdateSelectorPlaygroundHighlight ({ $el, $body, selector, showTooltip, onClick }: any) {
const { container, shadowRoot, vueContainer } = getOrCreateHelperDom({
const { container, vueContainer } = getOrCreateHelperDom({
body: $body?.get(0) || document.body,
className: '__cypress-selector-playground',
css: this.highlight.css,
css: Highlight.styles[0],
})
const removeContainerClickListeners = () => {
@@ -773,7 +774,6 @@ export class AutIframe {
}
if (!$el) {
this.highlight.unmount(vueContainer)
removeContainerClickListeners()
container.remove()
@@ -792,11 +792,6 @@ export class AutIframe {
}
}
this.highlight.render(vueContainer, {
selector,
appendTo: shadowRoot,
showTooltip,
styles,
})
highlightMounter.mount(vueContainer, selector, styles)
}
}

View File

@@ -147,7 +147,6 @@ function setupRunner () {
'Test Project',
getEventManager(),
window.UnifiedRunner.CypressJQuery,
window.UnifiedRunner.highlight,
)
createIframeModel()

View File

@@ -0,0 +1,81 @@
<template>
<div
class="highlight"
:style="highlightStyle"
/>
<div
class="tooltip"
:style="tooltipStyle"
>
<span>{{ selector }}</span>
<div
class="arrow"
:style="arrowStyle"
/>
</div>
</template>
<script lang="ts" setup>
import type { StyleValue, CSSProperties } from 'vue'
const props = defineProps <{
selector: string
style: StyleValue
}>()
const highlightStyle = props.style as CSSProperties || {}
const highlightTop = parseFloat(highlightStyle.top as string)
const highlightLeft = parseFloat(highlightStyle.left as string)
const highlightHeight = parseFloat(highlightStyle.height as string)
const placeOnBottom = highlightTop < 35
const tooltipStyle =
placeOnBottom
? {
top: `${highlightTop + highlightHeight + 10}px`,
left: `${highlightLeft}px`,
}
: {
top: `${highlightTop - 33}px`,
left: `${highlightLeft}px`,
}
const arrowStyle =
placeOnBottom
? {
left: `8px`,
top: `-6px`,
transform: 'rotate(0deg)',
}
: {
left: `8px`,
top: `24px`,
transform: 'rotate(180deg)',
}
</script>
<style>
.highlight {
background: rgba(159, 196, 231, 0.6);
border: solid 2px #9FC4E7;
cursor: pointer;
}
.tooltip {
position: absolute;
background: #333;
border: solid 1px #333;
border-radius: 3px;
color: #e3e3e3;
font-size: 12px;
padding: 4px 6px;
text-align: center;
}
.arrow {
position: absolute;
width: 0;
height: 0;
border-style: solid;
border-width: 0 6px 6px 6px;
border-color: transparent transparent #333 transparent;
}
</style>

View File

@@ -0,0 +1,17 @@
<template>
<Highlight
v-for="(style, i) in styles"
:key="i"
:selector="selector"
:style="style"
/>
</template>
<script lang="ts" setup>
import Highlight from './Highlight.ce.vue'
defineProps <{
selector: string
styles: any[]
}>()
</script>

View File

@@ -0,0 +1,19 @@
import { App, createApp } from 'vue'
import HighlightApp from './HighlightApp.ce.vue'
let app: App<Element> | null = null
export default {
mount (container: Element, selector: string, styles: any[]) {
if (app) {
app.unmount()
}
app = createApp(HighlightApp, {
selector,
styles,
})
app.mount(container)
},
}

View File

@@ -1,166 +0,0 @@
export const css = `
/**
* This is a standalone file that gets included with the selector playground
* highlight in the user's app html
*/
.tooltip {
background: #333;
border: solid 1px #333;
border-radius: 3px;
color: #e3e3e3;
display: inline-block;
font-size: 12px;
left: 0;
max-width: 200px;
padding: 4px 6px;
text-align: center;
top: 0;
z-index: 100;
}
.tooltip-arrow {
height: 0;
overflow: hidden;
position: absolute;
width: 0;
}
.tooltip-arrow svg {
fill: #333;
position: relative;
stroke: #333;
stroke-width: 1px;
}
.tooltip-top,
.tooltip-top-start,
.tooltip-top-end,
.tooltip-bottom.tooltip-flipped,
.tooltip-bottom-start.tooltip-flipped,
.tooltip-bottom-end.tooltip-flipped {
margin-bottom: 5px;
}
.tooltip-top .tooltip-arrow,
.tooltip-top-start .tooltip-arrow,
.tooltip-top-end .tooltip-arrow,
.tooltip-bottom.tooltip-flipped .tooltip-arrow,
.tooltip-bottom-start.tooltip-flipped .tooltip-arrow,
.tooltip-bottom-end.tooltip-flipped .tooltip-arrow {
top: auto;
bottom: -6px;
height: 6px;
width: 12px;
}
.tooltip-top .tooltip-arrow svg,
.tooltip-top-start .tooltip-arrow svg,
.tooltip-top-end .tooltip-arrow svg,
.tooltip-bottom.tooltip-flipped .tooltip-arrow svg,
.tooltip-bottom-start.tooltip-flipped .tooltip-arrow svg,
.tooltip-bottom-end.tooltip-flipped .tooltip-arrow svg {
top: -5px;
}
.tooltip-right,
.tooltip-right-start,
.tooltip-right-end,
.tooltip-left.tooltip-flipped,
.tooltip-left-start.tooltip-flipped,
.tooltip-left-end.tooltip-flipped {
margin-left: 5px;
}
.tooltip-right .tooltip-arrow,
.tooltip-right-start .tooltip-arrow,
.tooltip-right-end .tooltip-arrow,
.tooltip-left.tooltip-flipped .tooltip-arrow,
.tooltip-left-start.tooltip-flipped .tooltip-arrow,
.tooltip-left-end.tooltip-flipped .tooltip-arrow {
right: auto;
left: -6px;
height: 12px;
width: 6px;
}
.tooltip-right svg,
.tooltip-right-start svg,
.tooltip-right-end svg,
.tooltip-left.tooltip-flipped svg,
.tooltip-left-start.tooltip-flipped svg,
.tooltip-left-end.tooltip-flipped svg {
left: 0;
}
.tooltip-left,
.tooltip-left-start,
.tooltip-left-end,
.tooltip-right.tooltip-flipped,
.tooltip-right-start.tooltip-flipped,
.tooltip-right-end.tooltip-flipped {
margin-right: 5px;
}
.tooltip-left .tooltip-arrow,
.tooltip-left-start .tooltip-arrow,
.tooltip-left-end .tooltip-arrow,
.tooltip-right.tooltip-flipped .tooltip-arrow,
.tooltip-right-start.tooltip-flipped .tooltip-arrow,
.tooltip-right-end.tooltip-flipped .tooltip-arrow {
left: auto;
right: -6px;
height: 12px;
width: 6px;
}
.tooltip-left .tooltip-arrow svg,
.tooltip-left-start .tooltip-arrow svg,
.tooltip-left-end .tooltip-arrow svg,
.tooltip-right.tooltip-flipped .tooltip-arrow svg,
.tooltip-right-start.tooltip-flipped .tooltip-arrow svg,
.tooltip-right-end.tooltip-flipped .tooltip-arrow svg {
left: -5px;
}
.tooltip-bottom,
.tooltip-bottom-start,
.tooltip-bottom-end,
.tooltip-top.tooltip-flipped,
.tooltip-top-start.tooltip-flipped,
.tooltip-top-end.tooltip-flipped {
margin-top: 5px;
}
.tooltip-bottom .tooltip-arrow,
.tooltip-bottom-start .tooltip-arrow,
.tooltip-bottom-end .tooltip-arrow,
.tooltip-top.tooltip-flipped .tooltip-arrow,
.tooltip-top-start.tooltip-flipped .tooltip-arrow,
.tooltip-top-end.tooltip-flipped .tooltip-arrow {
bottom: auto;
height: 6px;
top: -6px;
width: 12px;
}
.tooltip-bottom svg,
.tooltip-bottom-start svg,
.tooltip-bottom-end svg,
.tooltip-top.tooltip-flipped svg,
.tooltip-top-start.tooltip-flipped svg,
.tooltip-top-end.tooltip-flipped svg {
top: 0;
}
.tooltip-top-start .tooltip-arrow,
.tooltip-bottom-start .tooltip-arrow {
left: 0;
}
.tooltip-top-end .tooltip-arrow,
.tooltip-bottom-end .tooltip-arrow {
right: 0;
}
.tooltip-left-start .tooltip-arrow,
.tooltip-right-start .tooltip-arrow {
top: 0;
}
.tooltip-left-end .tooltip-arrow,
.tooltip-right-end .tooltip-arrow {
bottom: 0;
}
.highlight {
background: rgba(159, 196, 231, 0.6);
border: solid 2px #9FC4E7;
cursor: pointer;
}
.tooltip {
font-family: sans-serif;
font-size: 14px;
max-width: 400px !important;
}
`

View File

@@ -1,37 +0,0 @@
import _ from 'lodash'
import React from 'react'
import { render, unmountComponentAtNode } from 'react-dom'
import Tooltip from '@cypress/react-tooltip'
const Highlight = ({ selector, appendTo, styles, showTooltip = true }) => {
return (
<div>
{_.map(styles, (style, i) => {
// indicates that tooltip should change if one of these props change
const updateCue = _.values(_.pick(style, 'width', 'height', 'top', 'left', 'transform')).join()
return (
<Tooltip
key={i}
title={selector}
visible={showTooltip}
placement='top-start'
appendTo={appendTo}
updateCue={updateCue}
>
<div className='highlight' style={style} />
</Tooltip>
)
})}
</div>
)
}
function renderHighlight (container, props) {
render(<Highlight {...props} />, container)
}
export const selectorPlaygroundHighlight = {
render: renderHighlight,
unmount: unmountComponentAtNode,
}

View File

@@ -1,8 +0,0 @@
import { selectorPlaygroundHighlight } from './highlight'
import { css } from './css'
export const highlight = {
render: selectorPlaygroundHighlight.render,
unmount: selectorPlaygroundHighlight.unmount,
css,
}

View File

@@ -6,7 +6,6 @@ import shortcuts from '@packages/reporter/src/lib/shortcuts'
import * as MobX from 'mobx'
import { dom } from './src/dom'
import { highlight } from './src/selector-playground'
export const UnifiedRunner = {
CypressJQuery: $Cypress.$,
@@ -14,7 +13,6 @@ export const UnifiedRunner = {
CypressDriver: $Cypress,
dom,
highlight,
shortcuts,

View File

@@ -0,0 +1,5 @@
module.exports = {
e2e: {
supportFile: false,
},
}

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello Selector Playground</title>
</head>
<body>
<h1>Hello, Selector Playground!</h1>
<hr />
<h2 data-cy="h2-contents">Some contents</h2>
<p>Para 1</p>
<p>Para 2</p>
<p>Para 3</p>
<p>Para 4</p>
</body>
</html>

View File

@@ -0,0 +1,3 @@
it('visits a basic html page', () => {
cy.visit('cypress/e2e/index.html')
})