@operato/popup 10.29.49 → 10.29.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/src/ox-floating-overlay.js +1 -1
- package/dist/src/ox-floating-overlay.js.map +1 -1
- package/dist/src/ox-popup-list.d.ts +2 -0
- package/dist/src/ox-popup-list.js +34 -1
- package/dist/src/ox-popup-list.js.map +1 -1
- package/dist/src/ox-popup.d.ts +16 -1
- package/dist/src/ox-popup.js +47 -3
- package/dist/src/ox-popup.js.map +1 -1
- package/dist/test/ox-popup-asked-size.test.d.ts +1 -0
- package/dist/test/ox-popup-asked-size.test.js +43 -0
- package/dist/test/ox-popup-asked-size.test.js.map +1 -0
- package/dist/test/ox-popup-list-search.test.d.ts +1 -0
- package/dist/test/ox-popup-list-search.test.js +61 -0
- package/dist/test/ox-popup-list-search.test.js.map +1 -0
- package/dist/test/ox-popup-placement.test.d.ts +1 -0
- package/dist/test/ox-popup-placement.test.js +69 -0
- package/dist/test/ox-popup-placement.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/test/ox-popup-asked-size.test.ts +48 -0
- package/test/ox-popup-list-search.test.ts +71 -0
- package/test/ox-popup-placement.test.ts +81 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing'
|
|
2
|
+
import { html } from 'lit'
|
|
3
|
+
|
|
4
|
+
import { OxPopup } from '../src/ox-popup.js'
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
* A popup that clears its height when it opens (2026-10-25, so a reopened list does not keep the height it was cut to)
|
|
8
|
+
* also dropped the height its opener gave it in `style`: the fmsim board preview, asked 90vw x 90vh, opened 0 high
|
|
9
|
+
* (2026-10-28). The height asked in `style` is where every opening starts.
|
|
10
|
+
*/
|
|
11
|
+
describe('ox-popup asked size', () => {
|
|
12
|
+
let popup: OxPopup | undefined
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
popup?.close()
|
|
16
|
+
popup?.remove()
|
|
17
|
+
popup = undefined
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('a popup opened with a height in its style is that high', async () => {
|
|
21
|
+
popup = OxPopup.open({
|
|
22
|
+
template: html`<div style="width:100%;height:100%"></div>`,
|
|
23
|
+
style: 'width:50vw;height:50vh;'
|
|
24
|
+
})
|
|
25
|
+
await popup.updateComplete
|
|
26
|
+
const box = popup.getBoundingClientRect()
|
|
27
|
+
expect(box.height).to.be.closeTo(window.innerHeight / 2, 1)
|
|
28
|
+
expect(box.width).to.be.closeTo(window.innerWidth / 2, 1)
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('opened again, it starts from the height it was asked, not from where it was cut', async () => {
|
|
32
|
+
popup = OxPopup.open({
|
|
33
|
+
template: html`<div></div>`,
|
|
34
|
+
style: 'width:200px;height:300px;'
|
|
35
|
+
})
|
|
36
|
+
await popup.updateComplete
|
|
37
|
+
popup.style.height = '40px'
|
|
38
|
+
popup.open({})
|
|
39
|
+
await popup.updateComplete
|
|
40
|
+
expect(popup.getBoundingClientRect().height).to.be.closeTo(300, 1)
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('a popup asked no height still grows with its content', async () => {
|
|
44
|
+
popup = OxPopup.open({ template: html`<div style="height:120px"></div>`, style: 'width:200px;' })
|
|
45
|
+
await popup.updateComplete
|
|
46
|
+
expect(popup.getBoundingClientRect().height).to.be.at.least(120)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing'
|
|
2
|
+
import { html } from 'lit'
|
|
3
|
+
|
|
4
|
+
import '../src/ox-popup-list.js'
|
|
5
|
+
import type { OxPopupList } from '../src/ox-popup-list.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A searchable list is a select with a search box on top. It opened with the focus on an option, so the user had to
|
|
9
|
+
* click the box before typing, and Enter in the box did nothing.
|
|
10
|
+
*/
|
|
11
|
+
describe('ox-popup-list with search', () => {
|
|
12
|
+
const tick = () => new Promise(resolve => setTimeout(resolve, 10))
|
|
13
|
+
|
|
14
|
+
async function opened() {
|
|
15
|
+
const list = await fixture<OxPopupList>(html`<ox-popup-list with-search>
|
|
16
|
+
<div option value="a">Alpha</div>
|
|
17
|
+
<div option value="m1">MES one</div>
|
|
18
|
+
<div option value="m2">MES two</div>
|
|
19
|
+
</ox-popup-list>`)
|
|
20
|
+
list.open({ left: 0, top: 0 })
|
|
21
|
+
await list.updateComplete
|
|
22
|
+
await tick()
|
|
23
|
+
return list
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const type = async (list: OxPopupList, text: string) => {
|
|
27
|
+
list.searchInput.value = text
|
|
28
|
+
list.searchInput.dispatchEvent(new InputEvent('input', { bubbles: true, composed: true }))
|
|
29
|
+
await list.updateComplete
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const enter = (list: OxPopupList, init: KeyboardEventInit = {}) =>
|
|
33
|
+
list.searchInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, composed: true, ...init }))
|
|
34
|
+
|
|
35
|
+
it('opens with the focus in the search box', async () => {
|
|
36
|
+
const list = await opened()
|
|
37
|
+
/* Compared as a boolean: a failed chai match on an element hangs the runner. */
|
|
38
|
+
expect(list.shadowRoot!.activeElement === list.searchInput).to.equal(true)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('picks the first match on Enter in the search box', async () => {
|
|
42
|
+
const list = await opened()
|
|
43
|
+
await type(list, 'mes')
|
|
44
|
+
let picked: unknown
|
|
45
|
+
list.addEventListener('select', (e: Event) => (picked = (e as CustomEvent).detail))
|
|
46
|
+
enter(list)
|
|
47
|
+
await tick()
|
|
48
|
+
expect(picked).to.equal('m1')
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('leaves Enter alone while a word is being composed', async () => {
|
|
52
|
+
const list = await opened()
|
|
53
|
+
await type(list, 'mes')
|
|
54
|
+
let picked = false
|
|
55
|
+
list.addEventListener('select', () => (picked = true))
|
|
56
|
+
enter(list, { isComposing: true })
|
|
57
|
+
await tick()
|
|
58
|
+
expect(picked).to.equal(false)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('clears the last search when it opens again', async () => {
|
|
62
|
+
const list = await opened()
|
|
63
|
+
await type(list, 'mes')
|
|
64
|
+
list.close()
|
|
65
|
+
list.open({ left: 0, top: 0 })
|
|
66
|
+
await list.updateComplete
|
|
67
|
+
await tick()
|
|
68
|
+
expect(list.searchInput.value).to.equal('')
|
|
69
|
+
expect(list.querySelectorAll('[option]:not([hidden])').length).to.equal(3)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { expect } from '@open-wc/testing'
|
|
2
|
+
import { html, render } from 'lit'
|
|
3
|
+
|
|
4
|
+
import '../src/ox-popup-list.js'
|
|
5
|
+
import type { OxPopupList } from '../src/ox-popup-list.js'
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
* A list taller than the room below its select was moved up over the select itself (2026-10-25). In a grid cell the
|
|
9
|
+
* second press of a double-click then landed on an option and picked it. A popup stays off the line it opens from: it
|
|
10
|
+
* shrinks to the room below, or opens above when there is more room there.
|
|
11
|
+
*/
|
|
12
|
+
describe('ox-popup placement', () => {
|
|
13
|
+
const frame = () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)))
|
|
14
|
+
let list: OxPopupList
|
|
15
|
+
|
|
16
|
+
async function open(count: number, place: { top: number; anchorTop?: number }) {
|
|
17
|
+
const host = document.createElement('div')
|
|
18
|
+
document.body.appendChild(host)
|
|
19
|
+
render(
|
|
20
|
+
html`<ox-popup-list with-search>
|
|
21
|
+
${Array.from({ length: count }, (_, i) => html`<div option value=${i}>Option ${i}</div>`)}
|
|
22
|
+
</ox-popup-list>`,
|
|
23
|
+
host
|
|
24
|
+
)
|
|
25
|
+
list = host.querySelector('ox-popup-list') as OxPopupList
|
|
26
|
+
document.body.appendChild(list)
|
|
27
|
+
host.remove()
|
|
28
|
+
await list.updateComplete
|
|
29
|
+
list.open({ left: 20, ...place })
|
|
30
|
+
await list.updateComplete
|
|
31
|
+
await frame()
|
|
32
|
+
return list.getBoundingClientRect()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
list?.close()
|
|
37
|
+
list?.remove()
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('a long list under a select with more room below shrinks to that room and keeps its search box', async () => {
|
|
41
|
+
const anchorTop = Math.round(window.innerHeight / 3)
|
|
42
|
+
const box = await open(200, { anchorTop, top: anchorTop + 30 })
|
|
43
|
+
expect(box.top).to.be.at.least(anchorTop + 30 - 0.5)
|
|
44
|
+
expect(box.bottom).to.be.at.most(window.innerHeight + 0.5)
|
|
45
|
+
const search = list.shadowRoot!.querySelector('[search]')!.getBoundingClientRect()
|
|
46
|
+
expect(search.top).to.be.at.least(anchorTop + 30 - 0.5)
|
|
47
|
+
const last = list.querySelector('[option]:last-of-type')!
|
|
48
|
+
expect(last.getBoundingClientRect().bottom > box.bottom).to.equal(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('a long list under a select near the bottom opens above the select', async () => {
|
|
52
|
+
const anchorTop = window.innerHeight - 60
|
|
53
|
+
const box = await open(200, { anchorTop, top: anchorTop + 30 })
|
|
54
|
+
expect(box.bottom).to.be.at.most(anchorTop + 0.5)
|
|
55
|
+
expect(box.top).to.be.at.least(-0.5)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('a short list that fits stays where it was asked to open', async () => {
|
|
59
|
+
const box = await open(3, { anchorTop: 40, top: 70 })
|
|
60
|
+
expect(box.top).to.be.closeTo(70, 0.5)
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('a menu at a point near the bottom opens above the point, not over it', async () => {
|
|
64
|
+
const point = window.innerHeight - 20
|
|
65
|
+
const box = await open(30, { top: point })
|
|
66
|
+
expect(box.bottom).to.be.at.most(point + 0.5)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('opened again elsewhere, it does not keep the last place or the last cut', async () => {
|
|
70
|
+
const anchorTop = Math.round(window.innerHeight / 2)
|
|
71
|
+
await open(200, { anchorTop, top: anchorTop + 30 })
|
|
72
|
+
list.close()
|
|
73
|
+
list.open({ left: 20, anchorTop: 40, top: 70 })
|
|
74
|
+
await list.updateComplete
|
|
75
|
+
await frame()
|
|
76
|
+
const box = list.getBoundingClientRect()
|
|
77
|
+
expect(box.top).to.be.closeTo(70, 0.5)
|
|
78
|
+
/* The room below now, less the margin - not the shorter cut of the last opening. */
|
|
79
|
+
expect(box.bottom).to.be.closeTo(window.innerHeight - 10, 0.5)
|
|
80
|
+
})
|
|
81
|
+
})
|