Skip to content

Commit f26764a

Browse files
committed
fix(NcActions): handle text-only popup keyboard navigation
- Add new semantic type - tooltip (popup with text only, without interactive elements) - Simplify semantic types logic - Manually close tooltips on blur Signed-off-by: Grigorii K. Shartsev <[email protected]>
1 parent bb05b7b commit f26764a

File tree

1 file changed

+56
-26
lines changed

1 file changed

+56
-26
lines changed

src/components/NcActions/NcActions.vue

Lines changed: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,8 @@ export default {
698698
</NcActions>
699699
</p>
700700

701-
<h2>Popover</h2>
702-
Has any elements, including text input element, or no buttons.
701+
<h2>Dialog</h2>
702+
Includes data input elements
703703
<p>
704704
<NcActions aria-label="Group management">
705705
<NcActionInput trailing-button-label="Submit" label="Rename group">
@@ -715,6 +715,19 @@ export default {
715715
</NcActionButton>
716716
</NcActions>
717717
</p>
718+
719+
<h2>Toolip</h2>
720+
Has only text and not interactive elements
721+
<p>
722+
<NcActions aria-label="Contact" :inline="1">
723+
<NcActionLink aria-label="View profile" href="/u/alice" icon="icon-user-white">
724+
View profile
725+
</NcActionLink>
726+
<NcActionText icon="icon-timezone-white">
727+
Local time: 10:12
728+
</NcActionText>
729+
</NcActions>
730+
</p>
718731
</div>
719732
</template>
720733

@@ -794,7 +807,6 @@ p {
794807
}
795808
</style>
796809
```
797-
798810
</docs>
799811

800812
<script>
@@ -836,7 +848,7 @@ export default {
836848
* Provide the role for NcAction* components in the NcActions content.
837849
* @type {import('vue').ComputedRef<boolean>}
838850
*/
839-
'NcActions:isSemanticMenu': computed(() => this.isSemanticMenu),
851+
'NcActions:isSemanticMenu': computed(() => this.actionsMenuSemanticType === 'menu'),
840852
}
841853
},
842854
@@ -992,9 +1004,10 @@ export default {
9921004
opened: this.open,
9931005
focusIndex: 0,
9941006
randomId: `menu-${GenRandomId()}`,
995-
isSemanticMenu: false,
996-
isSemanticNavigation: false,
997-
isSemanticPopoverLike: false,
1007+
/**
1008+
* @type {'menu'|'navigation'|'dialog'|'tooltip'|''}
1009+
*/
1010+
actionsMenuSemanticType: '',
9981011
}
9991012
},
10001013
@@ -1006,6 +1019,10 @@ export default {
10061019
// If it has a name, we use a secondary button
10071020
: this.menuName ? 'secondary' : 'tertiary')
10081021
},
1022+
1023+
withFocusTrap() {
1024+
return this.actionsMenuSemanticType === 'dialog'
1025+
},
10091026
},
10101027
10111028
watch: {
@@ -1097,8 +1114,10 @@ export default {
10971114
// close everything
10981115
this.focusIndex = 0
10991116
1100-
// focus back the menu button
1101-
this.$refs.menuButton.$el.focus()
1117+
if (returnFocus) {
1118+
// Focus back the menu button
1119+
this.$refs.menuButton.$el.focus()
1120+
}
11021121
},
11031122
11041123
onOpen(event) {
@@ -1136,8 +1155,10 @@ export default {
11361155
* @param {object} event The keydown event
11371156
*/
11381157
onKeydown(event) {
1139-
if (event.key === 'Tab' && !this.isSemanticPopoverLike) {
1140-
this.closeMenu(false)
1158+
if (event.key === 'Tab' && !this.withFocusTrap) {
1159+
// Return focus to restore Tab sequence
1160+
// So browser will correctly move focus to the next element
1161+
this.closeMenu(true)
11411162
}
11421163
11431164
if (event.key === 'ArrowUp') {
@@ -1231,6 +1252,12 @@ export default {
12311252
},
12321253
onBlur(event) {
12331254
this.$emit('blur', event)
1255+
1256+
// When there is no focusable elements to handle Tab press from actions menu
1257+
// It requries manual closing
1258+
if (this.actionsMenuSemanticType === 'tooltip') {
1259+
this.closeMenu(false)
1260+
}
12341261
},
12351262
},
12361263
@@ -1287,18 +1314,23 @@ export default {
12871314
const hasMenuItemAction = menuActions.some(action => menuItemsActions.includes(this.getActionName(action)))
12881315
const hasLinkAction = menuActions.some(action => linkActions.includes(this.getActionName(action)))
12891316
1290-
// We consider the NcActions to have role="menu" if it consists some button-like action and not text inputs
1291-
this.isSemanticMenu = hasMenuItemAction && !hasTextInputAction
1292-
// We consider the NcActions to be navigation if it consists some link-like action
1293-
this.isSemanticNavigation = hasLinkAction && !hasMenuItemAction && !hasTextInputAction
1294-
// If it is not a menu and not a navigation, it is a popover with items: a form or just a text
1295-
this.isSemanticPopoverLike = !this.isSemanticMenu && !this.isSemanticNavigation
1317+
if (hasTextInputAction) {
1318+
this.actionsMenuSemanticType = 'dialog'
1319+
} else if (hasMenuItemAction) {
1320+
this.actionsMenuSemanticType = 'menu'
1321+
} else if (hasLinkAction) {
1322+
this.actionsMenuSemanticType = 'navigation'
1323+
} else {
1324+
this.actionsMenuSemanticType = 'tooltip'
1325+
}
12961326
1297-
const popupRole = this.isSemanticMenu
1298-
? 'menu'
1299-
: hasTextInputAction
1300-
? 'dialog'
1301-
: 'true'
1327+
const actionsRoleToHtmlPopupRole = {
1328+
dialog: 'dialog',
1329+
menu: 'menu',
1330+
navigation: 'true',
1331+
tooltip: 'true',
1332+
}
1333+
const popupRole = actionsRoleToHtmlPopupRole[this.actionsMenuSemanticType]
13021334
13031335
/**
13041336
* Render the provided action
@@ -1404,10 +1436,8 @@ export default {
14041436
container: this.container,
14051437
popoverBaseClass: 'action-item__popper',
14061438
popupRole,
1407-
// Menu and navigation should not have focus trap
1408-
// Tab should close the menu and move focus to the next UI element
1409-
setReturnFocus: !this.isSemanticPopoverLike ? null : this.$refs.menuButton?.$el,
1410-
focusTrap: this.isSemanticPopoverLike,
1439+
setReturnFocus: this.withFocusTrap ? this.$refs.menuButton?.$el : null,
1440+
focusTrap: this.withFocusTrap,
14111441
},
14121442
// For some reason the popover component
14131443
// does not react to props given under the 'props' key,

0 commit comments

Comments
 (0)