feat: left/right keys can change columns or focus (#1516)
* feat: left/right keys can change columns or focus * fixup * fixup, add tests
This commit is contained in:
parent
8f3b0ac80a
commit
3d58c86963
|
@ -8,6 +8,10 @@
|
||||||
<Shortcut key="s" on:pressed="goto('/search')"/>
|
<Shortcut key="s" on:pressed="goto('/search')"/>
|
||||||
<Shortcut key="h|?" on:pressed="showShortcutHelpDialog()"/>
|
<Shortcut key="h|?" on:pressed="showShortcutHelpDialog()"/>
|
||||||
<Shortcut key="c|7" on:pressed="showComposeDialog()"/>
|
<Shortcut key="c|7" on:pressed="showComposeDialog()"/>
|
||||||
|
{#if !$leftRightChangesFocus}
|
||||||
|
<Shortcut key="ArrowLeft" on:pressed="goLeftOrRight(true)" />
|
||||||
|
<Shortcut key="ArrowRight" on:pressed="goLeftOrRight(false)" />
|
||||||
|
{/if}
|
||||||
{#each $navPages as navPage, i}
|
{#each $navPages as navPage, i}
|
||||||
<Shortcut key={(i + 1).toString()} on:pressed="goto(navPage.href)" />
|
<Shortcut key={(i + 1).toString()} on:pressed="goto(navPage.href)" />
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -35,6 +39,21 @@
|
||||||
async showComposeDialog () {
|
async showComposeDialog () {
|
||||||
const showComposeDialog = await importShowComposeDialog()
|
const showComposeDialog = await importShowComposeDialog()
|
||||||
showComposeDialog()
|
showComposeDialog()
|
||||||
|
},
|
||||||
|
goLeftOrRight (left) {
|
||||||
|
let { currentPage, navPages } = this.store.get()
|
||||||
|
if (currentPage === 'notifications/mentions') { // special case
|
||||||
|
currentPage = 'notifications'
|
||||||
|
}
|
||||||
|
const idx = navPages.findIndex(_ => _.name === currentPage)
|
||||||
|
if (idx === -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (left && idx > 0) {
|
||||||
|
goto(navPages[idx - 1].href)
|
||||||
|
} else if (!left && idx < navPages.length - 1) {
|
||||||
|
goto(navPages[idx + 1].href)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,19 @@
|
||||||
{@html `
|
{@html `
|
||||||
<h2>Global</h2>
|
<h2>Global</h2>
|
||||||
<div class="hotkey-group">
|
<div class="hotkey-group">
|
||||||
|
${$leftRightChangesFocus ?
|
||||||
|
`
|
||||||
|
<ul>
|
||||||
|
<li><kbd>→</kbd> to go to the next focusable element</li>
|
||||||
|
<li><kbd>←</kbd> to go to the previous focusable element</li>
|
||||||
|
</ul>
|
||||||
|
` : ''}
|
||||||
<ul>
|
<ul>
|
||||||
<li><kbd>1</kbd> - <kbd>6</kbd> to switch columns</li>
|
<li>
|
||||||
|
<kbd>1</kbd> - <kbd>6</kbd>
|
||||||
|
${$leftRightChangesFocus ? '' : `or <kbd>←</kbd>/<kbd>→</kbd>`}
|
||||||
|
to switch columns
|
||||||
|
</li>
|
||||||
<li><kbd>7</kbd> or <kbd>c</kbd> to compose a new toot</li>
|
<li><kbd>7</kbd> or <kbd>c</kbd> to compose a new toot</li>
|
||||||
<li><kbd>s</kbd> to search</li>
|
<li><kbd>s</kbd> to search</li>
|
||||||
<li><kbd>g</kbd> + <kbd>h</kbd> to go home</li>
|
<li><kbd>g</kbd> + <kbd>h</kbd> to go home</li>
|
||||||
|
@ -40,7 +51,7 @@
|
||||||
<h2>Media</h2>
|
<h2>Media</h2>
|
||||||
<div class="hotkey-group">
|
<div class="hotkey-group">
|
||||||
<ul>
|
<ul>
|
||||||
<li><kbd>←</kbd> - <kbd>→</kbd> to go to next or previous</li>
|
<li><kbd>←</kbd> / <kbd>→</kbd> to go to next or previous</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`}
|
`}
|
||||||
|
@ -83,7 +94,10 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
|
import { store } from '../_store/store'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
store: () => store,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
inDialog: false
|
inDialog: false
|
||||||
})
|
})
|
||||||
|
|
|
@ -82,8 +82,10 @@
|
||||||
</div>
|
</div>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
|
||||||
<Shortcut scope='modal-{id}' key="ArrowLeft" on:pressed="prev()" />
|
{#if !$leftRightChangesFocus }
|
||||||
<Shortcut scope='modal-{id}' key="ArrowRight" on:pressed="next()" />
|
<Shortcut scope='modal-{id}' key="ArrowLeft" on:pressed="prev()" />
|
||||||
|
<Shortcut scope='modal-{id}' key="ArrowRight" on:pressed="next()" />
|
||||||
|
{/if}
|
||||||
<style>
|
<style>
|
||||||
:global(.media-modal-dialog) {
|
:global(.media-modal-dialog) {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
|
@ -3,10 +3,21 @@
|
||||||
|
|
||||||
<h2 class="sr-only">Preferences</h2>
|
<h2 class="sr-only">Preferences</h2>
|
||||||
<form class="ui-settings" aria-label="Hotkey settings">
|
<form class="ui-settings" aria-label="Hotkey settings">
|
||||||
<label class="setting-group">
|
<label class="setting-group {allowChangeHotkeySetting ? '' : 'disabled-style'}">
|
||||||
<input type="checkbox" id="choice-disable-hotkeys"
|
<input type="checkbox" id="choice-disable-hotkeys"
|
||||||
bind:checked="$disableHotkeys" on:change="onChange()">
|
bind:checked="$disableHotkeys"
|
||||||
Disable hotkeys
|
on:change="onChange()"
|
||||||
|
disabled={!allowChangeHotkeySetting}
|
||||||
|
>
|
||||||
|
Disable all hotkeys
|
||||||
|
</label>
|
||||||
|
<label class="setting-group {allowChangeLeftRightSetting ? '' : 'disabled-style'}">
|
||||||
|
<input type="checkbox" id="choice-left-right-focus"
|
||||||
|
bind:checked="$leftRightChangesFocus"
|
||||||
|
on:change="onChange()"
|
||||||
|
disabled={!allowChangeLeftRightSetting}
|
||||||
|
>
|
||||||
|
Left/right arrow keys change focus rather than columns/media
|
||||||
</label>
|
</label>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
@ -28,6 +39,10 @@
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label.disabled-style {
|
||||||
|
color: var(--deemphasized-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 240px) {
|
@media (max-width: 240px) {
|
||||||
.ui-settings {
|
.ui-settings {
|
||||||
padding: 20px 10px;
|
padding: 20px 10px;
|
||||||
|
@ -38,9 +53,18 @@
|
||||||
import SettingsLayout from '../../_components/settings/SettingsLayout.html'
|
import SettingsLayout from '../../_components/settings/SettingsLayout.html'
|
||||||
import ShortcutHelpInfo from '../../_components/ShortcutHelpInfo.html'
|
import ShortcutHelpInfo from '../../_components/ShortcutHelpInfo.html'
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
|
import { isKaiOS } from '../../_utils/userAgent'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
store: () => store,
|
store: () => store,
|
||||||
|
data: () => ({
|
||||||
|
allowChangeHotkeySetting: !isKaiOS() // In general KaiOS users cannot change this because they need the hotkeys
|
||||||
|
}),
|
||||||
|
computed: {
|
||||||
|
allowChangeLeftRightSetting: ({ $disableHotkeys }) => (
|
||||||
|
!$disableHotkeys && !isKaiOS()
|
||||||
|
)
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onChange () {
|
onChange () {
|
||||||
this.store.save()
|
this.store.save()
|
||||||
|
|
114
src/routes/_store/observers/leftRightFocusObservers.js
Normal file
114
src/routes/_store/observers/leftRightFocusObservers.js
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
// Makes it so the left and right arrows change focus, ala Tab/Shift+Tab. This is mostly designed
|
||||||
|
// for KaiOS devices.
|
||||||
|
|
||||||
|
export function leftRightFocusObservers (store) {
|
||||||
|
if (!process.browser) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDialogParent (element) {
|
||||||
|
let parent = element.parentElement
|
||||||
|
while (parent) {
|
||||||
|
if (parent.classList.contains('modal-dialog')) {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
parent = parent.parentElement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFocusableElements (activeElement) {
|
||||||
|
const query = `
|
||||||
|
a,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
input[type=text],
|
||||||
|
input[type=number],
|
||||||
|
input[type=search],
|
||||||
|
input[type=radio],
|
||||||
|
input[type=checkbox],
|
||||||
|
select,
|
||||||
|
[tabindex="0"]
|
||||||
|
`
|
||||||
|
|
||||||
|
// Respect focus trap inside of dialogs
|
||||||
|
const dialogParent = getDialogParent(activeElement)
|
||||||
|
const root = dialogParent || document
|
||||||
|
|
||||||
|
return Array.from(root.querySelectorAll(query))
|
||||||
|
.filter(element => {
|
||||||
|
if (element === activeElement) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return !element.disabled &&
|
||||||
|
element.getAttribute('tabindex') !== '-1' &&
|
||||||
|
(element.offsetWidth > 0 || element.offsetHeight > 0)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldIgnoreEvent (activeElement, key) {
|
||||||
|
const isTextarea = activeElement.tagName === 'TEXTAREA'
|
||||||
|
const isTextInput = activeElement.tagName === 'INPUT' &&
|
||||||
|
['input', 'search'].includes(activeElement.getAttribute('type'))
|
||||||
|
|
||||||
|
if (!isTextarea && !isTextInput) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const { selectionStart, selectionEnd } = activeElement
|
||||||
|
// if the cursor is at the start or end of the textarea and the user wants to navigate out of it,
|
||||||
|
// then do so
|
||||||
|
if (key === 'ArrowLeft' && selectionStart === selectionEnd && selectionStart === 0) {
|
||||||
|
return false
|
||||||
|
} else if (key === 'ArrowRight' && selectionStart === selectionEnd && selectionStart === activeElement.value.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function focusNextOrPrevious (e, key) {
|
||||||
|
const { activeElement } = document
|
||||||
|
if (shouldIgnoreEvent(activeElement, key)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const focusable = getFocusableElements(activeElement)
|
||||||
|
const index = focusable.indexOf(activeElement)
|
||||||
|
let element
|
||||||
|
if (key === 'ArrowLeft') {
|
||||||
|
console.log('focus previous')
|
||||||
|
element = focusable[index - 1] || focusable[0]
|
||||||
|
} else { // ArrowRight
|
||||||
|
console.log('focus next')
|
||||||
|
element = focusable[index + 1] || focusable[focusable.length - 1]
|
||||||
|
}
|
||||||
|
element.focus()
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleEnter (e) {
|
||||||
|
const { activeElement } = document
|
||||||
|
if (activeElement.tagName === 'INPUT' && ['checkbox', 'radio'].includes(activeElement.getAttribute('type'))) {
|
||||||
|
// Explicitly override "enter" on an input and make it fire the checkbox/radio
|
||||||
|
activeElement.click()
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyListener (e) {
|
||||||
|
const { key } = e
|
||||||
|
if (key === 'ArrowRight' || key === 'ArrowLeft') {
|
||||||
|
focusNextOrPrevious(e, key)
|
||||||
|
} else if (key === 'Enter') {
|
||||||
|
handleEnter(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
store.observe('leftRightChangesFocus', leftRightChangesFocus => {
|
||||||
|
if (leftRightChangesFocus) {
|
||||||
|
window.addEventListener('keydown', keyListener)
|
||||||
|
} else {
|
||||||
|
window.removeEventListener('keydown', keyListener)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ import { setupLoggedInObservers } from './setupLoggedInObservers'
|
||||||
import { logOutObservers } from './logOutObservers'
|
import { logOutObservers } from './logOutObservers'
|
||||||
import { touchObservers } from './touchObservers'
|
import { touchObservers } from './touchObservers'
|
||||||
import { grayscaleObservers } from './grayscaleObservers'
|
import { grayscaleObservers } from './grayscaleObservers'
|
||||||
|
import { leftRightFocusObservers } from './leftRightFocusObservers'
|
||||||
|
|
||||||
export function observers (store) {
|
export function observers (store) {
|
||||||
onlineObservers(store)
|
onlineObservers(store)
|
||||||
|
@ -17,5 +18,6 @@ export function observers (store) {
|
||||||
touchObservers(store)
|
touchObservers(store)
|
||||||
logOutObservers(store)
|
logOutObservers(store)
|
||||||
grayscaleObservers(store)
|
grayscaleObservers(store)
|
||||||
|
leftRightFocusObservers(store)
|
||||||
setupLoggedInObservers(store)
|
setupLoggedInObservers(store)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { computations } from './computations/computations'
|
||||||
import { mixins } from './mixins/mixins'
|
import { mixins } from './mixins/mixins'
|
||||||
import { LocalStorageStore } from './LocalStorageStore'
|
import { LocalStorageStore } from './LocalStorageStore'
|
||||||
import { observe } from 'svelte-extras'
|
import { observe } from 'svelte-extras'
|
||||||
|
import { isKaiOS } from '../_utils/userAgent'
|
||||||
|
|
||||||
const persistedState = {
|
const persistedState = {
|
||||||
autoplayGifs: false,
|
autoplayGifs: false,
|
||||||
|
@ -15,7 +16,7 @@ const persistedState = {
|
||||||
disableFavCounts: false,
|
disableFavCounts: false,
|
||||||
disableFollowerCounts: false,
|
disableFollowerCounts: false,
|
||||||
disableHotkeys: false,
|
disableHotkeys: false,
|
||||||
disableInfiniteScroll: false,
|
disableInfiniteScroll: isKaiOS(),
|
||||||
disableLongAriaLabels: false,
|
disableLongAriaLabels: false,
|
||||||
disableNotificationBadge: false,
|
disableNotificationBadge: false,
|
||||||
disableReblogCounts: false,
|
disableReblogCounts: false,
|
||||||
|
@ -23,6 +24,7 @@ const persistedState = {
|
||||||
enableGrayscale: false,
|
enableGrayscale: false,
|
||||||
hideCards: false,
|
hideCards: false,
|
||||||
largeInlineMedia: false,
|
largeInlineMedia: false,
|
||||||
|
leftRightChangesFocus: isKaiOS(),
|
||||||
instanceNameInSearch: '',
|
instanceNameInSearch: '',
|
||||||
instanceThemes: {},
|
instanceThemes: {},
|
||||||
instanceSettings: {},
|
instanceSettings: {},
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { store } from '../_store/store'
|
||||||
|
|
||||||
// Rough guess at whether this is a "mobile" device or not, for the purposes
|
// Rough guess at whether this is a "mobile" device or not, for the purposes
|
||||||
// of "device class" estimations
|
// of "device class" estimations
|
||||||
const IS_MOBILE = process.browser && navigator.userAgent.match(/(?:iPhone|iPod|iPad|Android)/)
|
const IS_MOBILE = process.browser && navigator.userAgent.match(/(?:iPhone|iPod|iPad|Android|KAIOS)/)
|
||||||
|
|
||||||
// Run a task that doesn't need to be processed immediately, but should
|
// Run a task that doesn't need to be processed immediately, but should
|
||||||
// probably be delayed if we're on a mobile device. Also run it sooner
|
// probably be delayed if we're on a mobile device. Also run it sooner
|
||||||
|
|
|
@ -179,7 +179,8 @@ function acceptShortcutEvent (event) {
|
||||||
(event.shiftKey && event.key !== '?') || // '?' is a special case - it is allowed
|
(event.shiftKey && event.key !== '?') || // '?' is a special case - it is allowed
|
||||||
(target && (
|
(target && (
|
||||||
target.isContentEditable ||
|
target.isContentEditable ||
|
||||||
['INPUT', 'TEXTAREA', 'SELECT'].includes(target.tagName)
|
['TEXTAREA', 'SELECT'].includes(target.tagName) ||
|
||||||
|
(target.tagName === 'INPUT' && !['radio', 'checkbox'].includes(target.getAttribute('type')))
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
3
src/routes/_utils/userAgent.js
Normal file
3
src/routes/_utils/userAgent.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import { thunk } from './thunk'
|
||||||
|
|
||||||
|
export const isKaiOS = thunk(() => process.browser && /KAIOS/.test(navigator.userAgent))
|
|
@ -1,10 +1,13 @@
|
||||||
import {
|
import {
|
||||||
|
disableHotkeys,
|
||||||
|
getActiveElementAriaLabel,
|
||||||
getNthStatus,
|
getNthStatus,
|
||||||
getUrl, isNthStatusActive,
|
getUrl, homeNavButton, isNthStatusActive, leftRightChangesFocus, modalDialog,
|
||||||
modalDialogContents,
|
modalDialogContents,
|
||||||
notificationsNavButton, scrollToStatus
|
notificationsNavButton, scrollToStatus, settingsNavButton, sleep
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
import { loginAsFoobar } from '../roles'
|
import { loginAsFoobar } from '../roles'
|
||||||
|
import { Selector as $ } from 'testcafe'
|
||||||
|
|
||||||
fixture`024-shortcuts-navigation.js`
|
fixture`024-shortcuts-navigation.js`
|
||||||
.page`http://localhost:4002`
|
.page`http://localhost:4002`
|
||||||
|
@ -122,3 +125,66 @@ test('Shortcut . scrolls to top and focuses', async t => {
|
||||||
.pressKey('.')
|
.pressKey('.')
|
||||||
.expect(isNthStatusActive(1)).ok()
|
.expect(isNthStatusActive(1)).ok()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Shortcut left and right changes columns', async t => {
|
||||||
|
await loginAsFoobar(t)
|
||||||
|
|
||||||
|
const steps = [
|
||||||
|
['right', 'notifications'],
|
||||||
|
['right', 'local'],
|
||||||
|
['right', 'community'],
|
||||||
|
['right', 'search'],
|
||||||
|
['right', 'settings'],
|
||||||
|
['right', 'settings'],
|
||||||
|
['left', 'search'],
|
||||||
|
['left', 'community'],
|
||||||
|
['left', 'local'],
|
||||||
|
['left', 'notifications'],
|
||||||
|
['left', ''],
|
||||||
|
['left', '']
|
||||||
|
]
|
||||||
|
|
||||||
|
await t
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/')
|
||||||
|
|
||||||
|
for (const [key, page] of steps) {
|
||||||
|
await t.pressKey(key)
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/' + page)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shortcut left and right can change focus', async t => {
|
||||||
|
await loginAsFoobar(t)
|
||||||
|
await t
|
||||||
|
.click(settingsNavButton)
|
||||||
|
.click($('a[href="/settings/hotkeys"]'))
|
||||||
|
.click(leftRightChangesFocus)
|
||||||
|
.expect(leftRightChangesFocus.checked).ok()
|
||||||
|
.click(homeNavButton)
|
||||||
|
await sleep(1000)
|
||||||
|
await t
|
||||||
|
.pressKey('right')
|
||||||
|
.expect(getActiveElementAriaLabel()).eql('Home (current page)')
|
||||||
|
.pressKey('right')
|
||||||
|
.expect(getActiveElementAriaLabel()).eql('Notifications')
|
||||||
|
.pressKey('left')
|
||||||
|
.expect(getActiveElementAriaLabel()).eql('Home (current page)')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shortcuts can be disabled', async t => {
|
||||||
|
await loginAsFoobar(t)
|
||||||
|
await t
|
||||||
|
.click(settingsNavButton)
|
||||||
|
.click($('a[href="/settings/hotkeys"]'))
|
||||||
|
.click(disableHotkeys)
|
||||||
|
.expect(disableHotkeys.checked).ok()
|
||||||
|
.click(homeNavButton)
|
||||||
|
.pressKey('2')
|
||||||
|
await sleep(500)
|
||||||
|
await t
|
||||||
|
.expect(getUrl()).eql('http://localhost:4002/')
|
||||||
|
.pressKey('h')
|
||||||
|
await sleep(500)
|
||||||
|
await t
|
||||||
|
.expect(modalDialog.exists).false
|
||||||
|
})
|
||||||
|
|
|
@ -50,6 +50,8 @@ export const neverMarkMediaSensitiveInput = $('#choice-never-mark-media-sensitiv
|
||||||
export const removeEmojiFromDisplayNamesInput = $('#choice-omit-emoji-in-display-names')
|
export const removeEmojiFromDisplayNamesInput = $('#choice-omit-emoji-in-display-names')
|
||||||
export const disableInfiniteScroll = $('#choice-disable-infinite-scroll')
|
export const disableInfiniteScroll = $('#choice-disable-infinite-scroll')
|
||||||
export const disableUnreadNotifications = $('#choice-disable-unread-notification-counts')
|
export const disableUnreadNotifications = $('#choice-disable-unread-notification-counts')
|
||||||
|
export const leftRightChangesFocus = $('#choice-left-right-focus')
|
||||||
|
export const disableHotkeys = $('#choice-disable-hotkeys')
|
||||||
export const dialogOptionsOption = $('.modal-dialog button')
|
export const dialogOptionsOption = $('.modal-dialog button')
|
||||||
export const emojiSearchInput = $('.emoji-mart-search input')
|
export const emojiSearchInput = $('.emoji-mart-search input')
|
||||||
export const confirmationDialogOKButton = $('.confirmation-dialog-form-flex button:nth-child(1)')
|
export const confirmationDialogOKButton = $('.confirmation-dialog-form-flex button:nth-child(1)')
|
||||||
|
@ -136,6 +138,10 @@ export const getActiveElementAriaPosInSet = exec(() => (
|
||||||
(document.activeElement && document.activeElement.getAttribute('aria-posinset')) || ''
|
(document.activeElement && document.activeElement.getAttribute('aria-posinset')) || ''
|
||||||
))
|
))
|
||||||
|
|
||||||
|
export const getActiveElementAriaLabel = exec(() => (
|
||||||
|
(document.activeElement && document.activeElement.getAttribute('aria-label')) || ''
|
||||||
|
))
|
||||||
|
|
||||||
export const getActiveElementInsideNthStatus = exec(() => {
|
export const getActiveElementInsideNthStatus = exec(() => {
|
||||||
let element = document.activeElement
|
let element = document.activeElement
|
||||||
while (element) {
|
while (element) {
|
||||||
|
|
Loading…
Reference in a new issue