123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- import { haveText, test, html, haveFocus, notHaveAttribute, haveAttribute, notHaveFocus, notHaveText } from '../../utils'
- test('can trap focus',
- [html`
- <div x-data="{ open: false }">
- <input type="text" id="1">
- <button id="2" @click="open = true">open</button>
- <div>
- <div x-trap="open">
- <input type="text" id="3">
- <button @click="open = false" id="4">close</button>
- </div>
- </div>
- </div>
- `],
- ({ get }, reload) => {
- get('#1').click()
- get('#1').should(haveFocus())
- get('#2').click()
- get('#3').should(haveFocus())
- cy.focused().tab()
- get('#4').should(haveFocus())
- cy.focused().tab()
- get('#3').should(haveFocus())
- cy.focused().tab({shift: true})
- get('#4').should(haveFocus())
- cy.focused().click()
- get('#2').should(haveFocus())
- },
- )
- test.only('works with clone',
- [html`
- <div id="foo" x-data="{
- open: false,
- triggerClone() {
- var original = document.getElementById('foo');
- var copy = original.cloneNode(true);
- Alpine.clone(original, copy);
- var p = document.createElement('p');
- p.textContent = 'bar';
- copy.appendChild(p);
- original.parentNode.replaceChild(copy, original);
- }
- }">
- <button id="one" @click="open = true">Trap</button>
- <div x-trap="open">
- <input type="text">
- <button id="two" @click="triggerClone()">Test</button>
- </div>
- </div>
- `],
- ({ get, wait }, reload) => {
- get('#one').click()
- get('#two').click()
- get('p').should(haveText('bar'))
- }
- )
- test('releases focus when x-if is destroyed',
- [html`
- <div x-data="{ open: false }">
- <button id="1" @click="open = true">open</button>
- <template x-if="open">
- <div x-trap="open">
- <button @click="open = false" id="2">close</button>
- </div>
- </template>
- </div>
- `],
- ({ get }, reload) => {
- get('#1').click()
- get('#2').should(haveFocus())
- get('#2').click()
- get('#1').should(haveFocus())
- },
- )
- test('can trap focus with inert',
- [html`
- <div x-data="{ open: false }">
- <h1>I should have aria-hidden when outside trap</h1>
- <button id="open" @click="open = true">open</button>
- <div x-trap.inert="open">
- <button @click="open = false" id="close">close</button>
- </div>
- </div>
- `],
- ({ get }, reload) => {
- get('#open').should(notHaveAttribute('aria-hidden', 'true'))
- get('#open').click()
- get('#open').should(haveAttribute('aria-hidden', 'true'))
- get('#close').click()
- get('#open').should(notHaveAttribute('aria-hidden', 'true'))
- },
- )
- test('inert only applies aria-hidden once',
- [html`
- <div>
- <div id="sibling">I should have aria-hidden applied once</div>
- <div x-data="{
- open: false,
- timesApplied: 0,
- init() {
- let observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.type === 'attributes' && mutation.attributeName === 'aria-hidden') {
- this.timesApplied++
- }
- })
- })
- observer.observe(document.querySelector('#sibling'), {
- attributes: true
- })
- },
- }">
- <input type="text" id="timesApplied" x-model="timesApplied" />
- <button id="trigger" @click="open = true">open</button>
- <div x-trap.inert="open">
- Hello, I'm a friendly modal!
- </div>
- </div>
- </div>
- `],
- ({ get }, reload) => {
- get('#trigger').click()
- get('#timesApplied').should('have.value', '1')
- },
- )
- test('can trap focus with noscroll',
- [html`
- <div x-data="{ open: false }">
- <button id="open" @click="open = true">open</button>
- <div x-trap.noscroll="open">
- <button @click="open = false" id="close">close</button>
- </div>
- <div style="height: 100vh"> </div>
- </div>
- `],
- ({ get, window }, reload) => {
- window().then((win) => {
- let scrollbarWidth = win.innerWidth - win.document.documentElement.clientWidth
- get('#open').click()
- get('html').should(haveAttribute('style', `overflow: hidden; padding-right: ${scrollbarWidth}px;`))
- get('#close').click()
- get('html').should(notHaveAttribute('style', `overflow: hidden; padding-right: ${scrollbarWidth}px;`))
- })
- },
- )
- test('can trap focus with noreturn',
- [html`
- <div x-data="{ open: false }" x-trap.noreturn="open">
- <input id="input" @focus="open = true">
- <div x-show="open">
- <button @click="open = false" id="close">close</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#input').focus()
- get('#close')
- get('#close').click()
- get('#input').should(notHaveFocus())
- },
- )
- test('$focus.focus',
- [html`
- <div x-data>
- <button id="press-me" @click="$focus.focus(document.querySelector('#focus-me'))">Focus Other</button>
- <button id="focus-me">Other</button>
- </div>
- `],
- ({ get }) => {
- get('#focus-me').should(notHaveFocus())
- get('#press-me').click()
- get('#focus-me').should(haveFocus())
- },
- )
- test('$focus.focusable',
- [html`
- <div x-data>
- <div id="1" x-text="$focus.focusable($el)"></div>
- <button id="2" x-text="$focus.focusable($el)"></button>
- </div>
- `],
- ({ get }) => {
- get('#1').should(haveText('false'))
- get('#2').should(haveText('true'))
- },
- )
- test('$focus.focusables',
- [html`
- <div x-data>
- <h1 x-text="$focus.within($refs.container).focusables().length"></h1>
- <div x-ref="container">
- <button>1</button>
- <div>2</div>
- <button>3</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('h1').should(haveText('2'))
- },
- )
- test('$focus.focused',
- [html`
- <div x-data>
- <button @click="$el.textContent = $el.isSameNode($focus.focused())">im-focused</button>
- </div>
- `],
- ({ get }) => {
- get('button').click()
- get('button').should(haveText('true'))
- },
- )
- test('$focus.lastFocused',
- [html`
- <div x-data>
- <button id="1" x-ref="first">first-focused</button>
- <button id="2" @click="$el.textContent = $refs.first.isSameNode($focus.lastFocused())">second-focused</button>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#2').click()
- get('#2').should(haveText('true'))
- },
- )
- test('$focus.within',
- [html`
- <div x-data>
- <button id="1" x-text="$focus.within($refs.first).focusables().length"></button>
- <div x-ref="first">
- <button>1</button>
- <button>2</button>
- </div>
- <div>
- <button>1</button>
- <button>2</button>
- <button>3</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').should(haveText('2'))
- },
- )
- test('$focus.next',
- [html`
- <div x-data>
- <div x-ref="first">
- <button id="1" @click="$focus.within($refs.first).next(); $nextTick(() => $el.textContent = $focus.focused().textContent)">1</button>
- <button>2</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveText('2'))
- },
- )
- test('$focus.prev',
- [html`
- <div x-data>
- <div x-ref="first">
- <button>2</button>
- <button id="1" @click="$focus.within($refs.first).prev(); $nextTick(() => $el.textContent = $focus.focused().textContent)">1</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveText('2'))
- },
- )
- test('$focus.wrap',
- [html`
- <div x-data>
- <div x-ref="first">
- <button>2</button>
- <button id="1" @click="$focus.within($refs.first).wrap().next(); $nextTick(() => $el.textContent = $focus.focused().textContent)">1</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveText('2'))
- },
- )
- test('$focus.first',
- [html`
- <div x-data>
- <button id="1" @click="$focus.within($refs.first).first(); $nextTick(() => $el.textContent = $focus.focused().textContent)">1</button>
- <div x-ref="first">
- <button>2</button>
- <button>3</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveText('2'))
- },
- )
- test('$focus.last',
- [html`
- <div x-data>
- <button id="1" @click="$focus.within($refs.first).last(); $nextTick(() => $el.textContent = $focus.focused().textContent)">1</button>
- <div x-ref="first">
- <button>2</button>
- <button>3</button>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveText('3'))
- },
- )
- test('focuses element with autofocus',
- [html`
- <div x-data="{ open: false }">
- <input type="text" id="1">
- <button id="2" @click="open = true">open</button>
- <div>
- <div x-trap="open">
- <input type="text" id="3">
- <input autofocus type="text" id="4">
- <button @click="open = false" id="5">close</button>
- </div>
- </div>
- </div>
- `],
- ({ get }) => {
- get('#1').click()
- get('#1').should(haveFocus())
- get('#2').click()
- get('#4').should(haveFocus())
- cy.focused().tab()
- get('#5').should(haveFocus())
- cy.focused().tab()
- get('#3').should(haveFocus())
- }
- )
|