From f8ef60e6fc8448b198624f32bc498a0e9dd6b9b4 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Mon, 18 Mar 2024 18:25:30 +0400 Subject: [PATCH 01/10] work for the https://github.com/ai/keyux/issues/17 (spike) --- focus-group.js | 12 ++++++++++-- test/focus-group.test.ts | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/focus-group.js b/focus-group.js index 1edaad6..9ce36af 100644 --- a/focus-group.js +++ b/focus-group.js @@ -18,6 +18,13 @@ export function focusGroupKeyUX(options) { } function findGroupNodeByEventTarget(eventTarget) { + if ( + (eventTarget.tagName === 'BUTTON' || eventTarget.role === 'button') && + eventTarget.closest('[role=toolbar]') + ) { + return eventTarget.closest('[role=toolbar]'); + } + let itemRole = eventTarget.role let groupRoles = ROLES[itemRole] if (!groupRoles) return null @@ -34,7 +41,7 @@ export function focusGroupKeyUX(options) { if (ariaOrientation === 'horizontal') return true let role = group.role - return role === 'menubar' || role === 'tablist' + return role === 'menubar' || role === 'tablist' || role === 'toolbar' } function keyDown(event) { @@ -45,7 +52,8 @@ export function focusGroupKeyUX(options) { return } - let items = group.querySelectorAll(`[role=${event.target.role}]`) + let itemsSelector = event.target.role ? `[role=${event.target.role}]`: `${event.target.tagName}` + let items = group.querySelectorAll(itemsSelector) let index = Array.from(items).indexOf(event.target) let nextKey = 'ArrowDown' diff --git a/test/focus-group.test.ts b/test/focus-group.test.ts index 5e8bd12..e617203 100644 --- a/test/focus-group.test.ts +++ b/test/focus-group.test.ts @@ -423,3 +423,36 @@ test('is ready to click after focus', () => { '' ) }) + +test('adds toolbar widget', () => { + let window = new JSDOM().window + startKeyUX(window, [focusGroupKeyUX()]) + window.document.body.innerHTML = + '
' + + '' + + '' + + '' + + '
' + let items = window.document.querySelectorAll('button') + items[0].focus() + + equal(window.document.activeElement, items[0]) + + press(window, 'ArrowRight') + equal(window.document.activeElement, items[1]) + + press(window, 'ArrowLeft') + equal(window.document.activeElement, items[0]) + + press(window, 'End') + equal(window.document.activeElement, items[2]) + + press(window, 'Home') + equal(window.document.activeElement, items[0]) + + press(window, 'ArrowLeft') + equal(window.document.activeElement, items[2]) + + press(window, 'ArrowRight') + equal(window.document.activeElement, items[0]) +}) From eac2178e3aa94c4466b79c337c3728b503001338 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Tue, 19 Mar 2024 12:48:00 +0400 Subject: [PATCH 02/10] work for the https://github.com/ai/keyux/issues/17 (toolbar->button support) --- focus-group.js | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/focus-group.js b/focus-group.js index 9ce36af..ba8f2f0 100644 --- a/focus-group.js +++ b/focus-group.js @@ -1,7 +1,8 @@ const ROLES = { menuitem: ['menu', 'menubar'], option: ['listbox'], - tab: ['tablist'] + tab: ['tablist'], + button: ['toolbar'] } export function focusGroupKeyUX(options) { @@ -18,15 +19,10 @@ export function focusGroupKeyUX(options) { } function findGroupNodeByEventTarget(eventTarget) { - if ( - (eventTarget.tagName === 'BUTTON' || eventTarget.role === 'button') && - eventTarget.closest('[role=toolbar]') - ) { - return eventTarget.closest('[role=toolbar]'); - } + let itemRole = eventTarget.role || eventTarget.tagName + if (!itemRole) return null; - let itemRole = eventTarget.role - let groupRoles = ROLES[itemRole] + let groupRoles = ROLES[itemRole.toLowerCase()] if (!groupRoles) return null for (let role of groupRoles) { From 45d08368e7a50e66646e7e3e21328c7769d499c6 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Wed, 20 Mar 2024 16:02:49 +0400 Subject: [PATCH 03/10] work for the https://github.com/ai/keyux/issues/17 (different item type support - spike) --- focus-group.js | 18 ++++++++++++++---- test/focus-group.test.ts | 30 ++++++++++++++++++------------ 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/focus-group.js b/focus-group.js index ba8f2f0..9f40abf 100644 --- a/focus-group.js +++ b/focus-group.js @@ -2,7 +2,8 @@ const ROLES = { menuitem: ['menu', 'menubar'], option: ['listbox'], tab: ['tablist'], - button: ['toolbar'] + button: ['toolbar'], + checkbox: ['toolbar'] } export function focusGroupKeyUX(options) { @@ -19,7 +20,7 @@ export function focusGroupKeyUX(options) { } function findGroupNodeByEventTarget(eventTarget) { - let itemRole = eventTarget.role || eventTarget.tagName + let itemRole = eventTarget.role || eventTarget.type || eventTarget.tagName if (!itemRole) return null; let groupRoles = ROLES[itemRole.toLowerCase()] @@ -48,8 +49,17 @@ export function focusGroupKeyUX(options) { return } - let itemsSelector = event.target.role ? `[role=${event.target.role}]`: `${event.target.tagName}` - let items = group.querySelectorAll(itemsSelector) + let items + if (group.role == "toolbar") { + items = group.querySelectorAll(`[role=${event.target.role}]`) + items = items.concat(group.querySelectorAll(`${event.target.tagName}`)) + items = items.concat(group.querySelectorAll(`[type=${event.target.type}]`)) + items = [...new Set(items)] + } else { + let itemsSelector = event.target.role ? `[role=${event.target.role}]` : `${event.target.tagName}` + items = group.querySelectorAll(itemsSelector) + } + let index = Array.from(items).indexOf(event.target) let nextKey = 'ArrowDown' diff --git a/test/focus-group.test.ts b/test/focus-group.test.ts index e617203..5a5bb57 100644 --- a/test/focus-group.test.ts +++ b/test/focus-group.test.ts @@ -429,30 +429,36 @@ test('adds toolbar widget', () => { startKeyUX(window, [focusGroupKeyUX()]) window.document.body.innerHTML = '
' + - '' + - '' + - '' + + '
' + + '' + + '' + + '' + + '
' + + '
' + + '' + + '
' + '
' - let items = window.document.querySelectorAll('button') - items[0].focus() + let buttons = window.document.querySelectorAll('button') + let checkboxes = window.document.querySelectorAll('[type="checkbox"]') + buttons[0].focus() - equal(window.document.activeElement, items[0]) + equal(window.document.activeElement, buttons[0]) press(window, 'ArrowRight') - equal(window.document.activeElement, items[1]) + equal(window.document.activeElement, buttons[1]) press(window, 'ArrowLeft') - equal(window.document.activeElement, items[0]) + equal(window.document.activeElement, buttons[0]) press(window, 'End') - equal(window.document.activeElement, items[2]) + equal(window.document.activeElement, checkboxes[0]) press(window, 'Home') - equal(window.document.activeElement, items[0]) + equal(window.document.activeElement, buttons[0]) press(window, 'ArrowLeft') - equal(window.document.activeElement, items[2]) + equal(window.document.activeElement, checkboxes[0]) press(window, 'ArrowRight') - equal(window.document.activeElement, items[0]) + equal(window.document.activeElement, buttons[0]) }) From 4189145437ed6f5183faffceba5d70cb77b4dc27 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Thu, 21 Mar 2024 20:26:18 +0400 Subject: [PATCH 04/10] work for the https://github.com/ai/keyux/issues/17 (different item type support - spike-2) --- focus-group.js | 32 +++++++++++++++++++------------- test/focus-group.test.ts | 6 +++--- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/focus-group.js b/focus-group.js index 9f40abf..8c2d368 100644 --- a/focus-group.js +++ b/focus-group.js @@ -32,6 +32,22 @@ export function focusGroupKeyUX(options) { } } + function getItems(eventTarget, group) { + return group.role === "toolbar" ? + getToolbarItems(group) : + group.querySelectorAll(`[role=${eventTarget.role}]`) + } + + function getToolbarItems(group) {//TODO need to think about selectors order and refactoring + let items + let nodesByRole = group.querySelectorAll(`[role="button"]`) + let nodesByTagName = group.querySelectorAll(`button`) + let nodesByType = group.querySelectorAll(`[type="checkbox"]`) + items = [...nodesByRole, ...nodesByTagName, ...nodesByType] + items = [...new Set(items)] + return items + } + function isHorizontalOrientation(group) { let ariaOrientation = group.getAttribute('aria-orientation') if (ariaOrientation === 'vertical') return false @@ -49,17 +65,7 @@ export function focusGroupKeyUX(options) { return } - let items - if (group.role == "toolbar") { - items = group.querySelectorAll(`[role=${event.target.role}]`) - items = items.concat(group.querySelectorAll(`${event.target.tagName}`)) - items = items.concat(group.querySelectorAll(`[type=${event.target.type}]`)) - items = [...new Set(items)] - } else { - let itemsSelector = event.target.role ? `[role=${event.target.role}]` : `${event.target.tagName}` - items = group.querySelectorAll(itemsSelector) - } - + let items = getItems(event.target, group); let index = Array.from(items).indexOf(event.target) let nextKey = 'ArrowDown' @@ -120,7 +126,7 @@ export function focusGroupKeyUX(options) { inGroup = true window.addEventListener('keydown', keyDown) } - let items = group.querySelectorAll(`[role=${event.target.role}]`) + let items = getItems(event.target, group); for (let item of items) { if (item !== event.target) { item.setAttribute('tabindex', -1) @@ -140,7 +146,7 @@ export function focusGroupKeyUX(options) { function click(event) { let group = findGroupNodeByEventTarget(event.target) if (group) { - let items = group.querySelectorAll(`[role=${event.target.role}]`) + let items = getItems(event.target, group); for (let item of items) { if (item !== event.target) { item.setAttribute('tabindex', -1) diff --git a/test/focus-group.test.ts b/test/focus-group.test.ts index 5a5bb57..4a09ac8 100644 --- a/test/focus-group.test.ts +++ b/test/focus-group.test.ts @@ -430,9 +430,9 @@ test('adds toolbar widget', () => { window.document.body.innerHTML = '
' + '
' + - '' + - '' + - '' + + '' + + '' + + '' + '
' + '
' + '' + From fdaff2850d554dcf7adbc6de5dee4f8c9ff2f860 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Fri, 22 Mar 2024 18:09:53 +0400 Subject: [PATCH 05/10] work for the https://github.com/ai/keyux/issues/17 (different item type support - spike - 3) --- focus-group.js | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/focus-group.js b/focus-group.js index 8c2d368..1377270 100644 --- a/focus-group.js +++ b/focus-group.js @@ -33,18 +33,15 @@ export function focusGroupKeyUX(options) { } function getItems(eventTarget, group) { - return group.role === "toolbar" ? - getToolbarItems(group) : - group.querySelectorAll(`[role=${eventTarget.role}]`) + if (group.role === "toolbar") return getToolbarItems(group) + return group.querySelectorAll(`[role=${eventTarget.role}]`) } - function getToolbarItems(group) {//TODO need to think about selectors order and refactoring - let items - let nodesByRole = group.querySelectorAll(`[role="button"]`) - let nodesByTagName = group.querySelectorAll(`button`) - let nodesByType = group.querySelectorAll(`[type="checkbox"]`) - items = [...nodesByRole, ...nodesByTagName, ...nodesByType] - items = [...new Set(items)] + function getToolbarItems(group) { + let items = [...group.querySelectorAll('*')] + items.filter((item)=> { + return item.role === 'button' || item.type === 'button' || item.role === 'checkbox' ||item.type === 'checkbox' + }) return items } From 102b422a5003d88f56840d46fa17348c1059307f Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Sat, 23 Mar 2024 18:20:53 +0400 Subject: [PATCH 06/10] work for the https://github.com/ai/keyux/issues/17 (different item type support) --- focus-group.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/focus-group.js b/focus-group.js index 1377270..e1ba006 100644 --- a/focus-group.js +++ b/focus-group.js @@ -39,10 +39,9 @@ export function focusGroupKeyUX(options) { function getToolbarItems(group) { let items = [...group.querySelectorAll('*')] - items.filter((item)=> { + return items.filter((item)=> { return item.role === 'button' || item.type === 'button' || item.role === 'checkbox' ||item.type === 'checkbox' }) - return items } function isHorizontalOrientation(group) { From 29f50f288d5dc8861c95a0ae508ac2056a0b5412 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Sun, 24 Mar 2024 18:37:46 +0400 Subject: [PATCH 07/10] work for the https://github.com/ai/keyux/issues/17 (added toolbar doc) --- README.md | 37 +++++++++++++++++++++++++++++++++++-- focus-group.js | 4 ++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d2e9f65..a0ba299 100644 --- a/README.md +++ b/README.md @@ -247,7 +247,7 @@ may contain images. ``` -Users will use Tab to get inside the menu, and will use either +Users will use Tab to get inside the listbox, and will use either arrows or Home, End or an item name to navigate inside. @@ -276,7 +276,40 @@ The tab content should be marked by `[role="tabpanel']`.
``` -Users will use Tab to get inside the menu, and will use either +Users will use Tab to get inside the tablist, and will use either +arrows or Home, +End. + +To enable this feature, call `focusGroupKeyUX`. + +```js +import { focusGroupKeyUX } from 'keyux' + +startKeyUX(window, [ + focusGroupKeyUX() +]) +``` + + +### Toolbar + +The [`role="toolbar"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/toolbar_role) +defines the containing element as a collection of commonly used function buttons or controls represented in a compact visual forms. + +```html +
+
+ + + +
+
+ +
+
+``` + +Users will use Tab to get inside the tablist, and will use either arrows or Home, End. diff --git a/focus-group.js b/focus-group.js index e1ba006..1d35d4c 100644 --- a/focus-group.js +++ b/focus-group.js @@ -1,9 +1,9 @@ const ROLES = { + button: ['toolbar'], + checkbox: ['toolbar'], menuitem: ['menu', 'menubar'], option: ['listbox'], tab: ['tablist'], - button: ['toolbar'], - checkbox: ['toolbar'] } export function focusGroupKeyUX(options) { From 0227a49af71bda1fbcf9128ff740e4528ff52e32 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Mon, 25 Mar 2024 17:54:42 +0400 Subject: [PATCH 08/10] work for the https://github.com/ai/keyux/issues/17 (toolbar demo) --- README.md | 2 +- test/demo/index.html | 24 ++++++++++++++++++++++++ test/demo/index.tsx | 21 +++++++++++++++++++++ test/focus-group.test.ts | 2 +- 4 files changed, 47 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0ba299..b818abe 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ defines the containing element as a collection of commonly used function buttons
- +
``` diff --git a/test/demo/index.html b/test/demo/index.html index 015d238..b5f60a5 100644 --- a/test/demo/index.html +++ b/test/demo/index.html @@ -121,6 +121,30 @@ border: 1px solid #aaa; padding: 1em; } + .toolbar { + margin-top: 1em; + display: flex; + gap: 16px; + align-items: center; + border: 1px solid #aaa; + padding: 0.5em 1em; + background: #eee; + } + + .toolbar_group { + display: flex; + gap: 8px; + } + + .toolbar_button { + background-color: white; + } + + .toolbar_label { + display: flex; + gap: 4px;; + cursor: pointer; + } diff --git a/test/demo/index.tsx b/test/demo/index.tsx index 4545107..5a2e0b1 100644 --- a/test/demo/index.tsx +++ b/test/demo/index.tsx @@ -338,6 +338,26 @@ const Tabs: FC = () => { ) } +const Toolbar: FC = () => { + return ( + <> +
+
+ + + +
+
+ +
+
+ + ) +} + const App: FC = () => { let [, setUpdate] = useState({}) let [router, setRouter] = useState('home') @@ -354,6 +374,7 @@ const App: FC = () => { }} /> + ) } diff --git a/test/focus-group.test.ts b/test/focus-group.test.ts index 4a09ac8..69e80b2 100644 --- a/test/focus-group.test.ts +++ b/test/focus-group.test.ts @@ -435,7 +435,7 @@ test('adds toolbar widget', () => { '' + '' + '
' + - '' + + '' + '
' + '' let buttons = window.document.querySelectorAll('button') From 7cbd649676587c53d364ca1f292cbf114f464821 Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Mon, 25 Mar 2024 20:04:28 +0400 Subject: [PATCH 09/10] work for the https://github.com/ai/keyux/issues/17 (prettier and lint) --- focus-group.js | 21 +++++++++++++-------- test/demo/index.tsx | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/focus-group.js b/focus-group.js index 1d35d4c..e28ad05 100644 --- a/focus-group.js +++ b/focus-group.js @@ -3,7 +3,7 @@ const ROLES = { checkbox: ['toolbar'], menuitem: ['menu', 'menubar'], option: ['listbox'], - tab: ['tablist'], + tab: ['tablist'] } export function focusGroupKeyUX(options) { @@ -21,7 +21,7 @@ export function focusGroupKeyUX(options) { function findGroupNodeByEventTarget(eventTarget) { let itemRole = eventTarget.role || eventTarget.type || eventTarget.tagName - if (!itemRole) return null; + if (!itemRole) return null let groupRoles = ROLES[itemRole.toLowerCase()] if (!groupRoles) return null @@ -33,14 +33,19 @@ export function focusGroupKeyUX(options) { } function getItems(eventTarget, group) { - if (group.role === "toolbar") return getToolbarItems(group) + if (group.role === 'toolbar') return getToolbarItems(group) return group.querySelectorAll(`[role=${eventTarget.role}]`) } function getToolbarItems(group) { let items = [...group.querySelectorAll('*')] - return items.filter((item)=> { - return item.role === 'button' || item.type === 'button' || item.role === 'checkbox' ||item.type === 'checkbox' + return items.filter(item => { + return ( + item.role === 'button' || + item.type === 'button' || + item.role === 'checkbox' || + item.type === 'checkbox' + ) }) } @@ -61,7 +66,7 @@ export function focusGroupKeyUX(options) { return } - let items = getItems(event.target, group); + let items = getItems(event.target, group) let index = Array.from(items).indexOf(event.target) let nextKey = 'ArrowDown' @@ -122,7 +127,7 @@ export function focusGroupKeyUX(options) { inGroup = true window.addEventListener('keydown', keyDown) } - let items = getItems(event.target, group); + let items = getItems(event.target, group) for (let item of items) { if (item !== event.target) { item.setAttribute('tabindex', -1) @@ -142,7 +147,7 @@ export function focusGroupKeyUX(options) { function click(event) { let group = findGroupNodeByEventTarget(event.target) if (group) { - let items = getItems(event.target, group); + let items = getItems(event.target, group) for (let item of items) { if (item !== event.target) { item.setAttribute('tabindex', -1) diff --git a/test/demo/index.tsx b/test/demo/index.tsx index 5a2e0b1..8846a17 100644 --- a/test/demo/index.tsx +++ b/test/demo/index.tsx @@ -341,7 +341,7 @@ const Tabs: FC = () => { const Toolbar: FC = () => { return ( <> -
+
From 57992fcbc24344ccd147466577ae91e7cac9ca2d Mon Sep 17 00:00:00 2001 From: dmitrykurmanov Date: Wed, 27 Mar 2024 21:10:52 +0400 Subject: [PATCH 10/10] work for the https://github.com/ai/keyux/issues/17 (review-1) --- README.md | 1 + package.json | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b818abe..02975c5 100644 --- a/README.md +++ b/README.md @@ -295,6 +295,7 @@ startKeyUX(window, [ The [`role="toolbar"`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/toolbar_role) defines the containing element as a collection of commonly used function buttons or controls represented in a compact visual forms. +Buttons inside the `toolbar` must have `type="button"` attribute because the default one is `submit`. ```html
diff --git a/package.json b/package.json index d572d4b..5586adc 100644 --- a/package.json +++ b/package.json @@ -85,7 +85,7 @@ "import": { "./index.js": "{ startKeyUX, hotkeyKeyUX, pressKeyUX, focusGroupKeyUX, jumpKeyUX, hiddenKeyUX, likelyWithKeyboard, getHotKeyHint }" }, - "limit": "1891 B" + "limit": "1960 B" } ], "clean-publish": {