Skip to content

Commit 1a03763

Browse files
committed
fix(ui-top-nav-bar,ui-buttons): display a focus ring in TopNavBar if a button has a Popover open
When a menu is active, it should be indicated as the “active” element for a11y reasons Fixes part of INSTUI-4323
1 parent b061088 commit 1a03763

File tree

6 files changed

+46
-11
lines changed

6 files changed

+46
-11
lines changed

packages/ui-buttons/src/BaseButton/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ class BaseButton extends Component<BaseButtonProps> {
251251
tabIndex,
252252
styles,
253253
makeStyles,
254+
withFocusOutline,
254255
...props
255256
} = this.props
256257

@@ -307,6 +308,7 @@ class BaseButton extends Component<BaseButtonProps> {
307308
focusRingBorderRadius={String(
308309
(styles?.content as { borderRadius?: string | number })?.borderRadius
309310
)}
311+
withFocusOutline={withFocusOutline}
310312
>
311313
<span css={styles?.content}>{this.renderChildren()}</span>
312314
</View>

packages/ui-buttons/src/BaseButton/props.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,16 @@ type BaseButtonOwnProps = {
159159

160160
/**
161161
* Specifies the tabindex of the `Button`.
162-
*
163162
*/
164163
tabIndex?: number
164+
165+
/**
166+
* Manually control if the `Button` should display a focus outline.
167+
*
168+
* When left `undefined` (which is the default) the focus outline will display
169+
* if this component is focusable and receives focus.
170+
*/
171+
withFocusOutline?: boolean
165172
}
166173

167174
type BaseButtonStyleProps = {
@@ -218,7 +225,8 @@ const propTypes: PropValidators<PropKeys> = {
218225
onClick: PropTypes.func,
219226
onKeyDown: PropTypes.func,
220227
renderIcon: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
221-
tabIndex: PropTypes.number
228+
tabIndex: PropTypes.number,
229+
withFocusOutline: PropTypes.bool
222230
}
223231

224232
const allowedProps: AllowedPropKeys = [

packages/ui-top-nav-bar/src/TopNavBar/TopNavBarItem/index.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
128128

129129
this.state = {
130130
isSubmenuOpen: false,
131-
isPopoverOpen: false
131+
isPopoverOpen: false,
132+
isFocused: false
132133
}
133134
}
134135

@@ -347,7 +348,8 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
347348
renderSubmenu,
348349
status: statusOriginal,
349350
renderAvatar,
350-
renderIcon
351+
renderIcon,
352+
withFocusOutline
351353
} = this.props
352354

353355
let href = hrefOriginal
@@ -423,18 +425,28 @@ class TopNavBarItem extends Component<TopNavBarItemProps, TopNavBarItemState> {
423425
onClick,
424426
onMouseOver,
425427
onMouseOut,
426-
onFocus,
427-
onBlur,
428+
onFocus: createChainedFunction(onFocus, this.onFocus),
429+
onBlur: createChainedFunction(onBlur, this.onBlur),
428430
onKeyDown: createChainedFunction(onKeyDown, this.handleKeyDown),
429431
onKeyUp,
430432
renderIcon,
431433
themeOverride: this.buttonThemeOverride,
432434
elementRef: (e) => {
433435
this.handleItemRef(e as HTMLButtonElement | HTMLLinkElement)
434-
}
436+
},
437+
withFocusOutline:
438+
withFocusOutline || this.hasOpenPopover || this.state.isFocused
435439
}
436440
}
437441

442+
onFocus = () => {
443+
this.setState({ isFocused: true })
444+
}
445+
446+
onBlur = () => {
447+
this.setState({ isFocused: false })
448+
}
449+
438450
handleKeyDown: TopNavBarItemProps['onKeyDown'] = (e) => {
439451
if (e.key === 'ArrowDown') {
440452
if (

packages/ui-top-nav-bar/src/TopNavBar/TopNavBarItem/props.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,14 @@ type TopNavBarItemOwnProps = {
254254
* Should close the container menu component, if clicked on the option marked with this prop
255255
*/
256256
shouldCloseOnClick?: ShouldCloseOnClick
257+
258+
/**
259+
* Manually control if this component should display a focus outline.
260+
*
261+
* When left `undefined` (which is the default) the focus outline will display
262+
* if this component is focusable and receives focus or has an open popover.
263+
*/
264+
withFocusOutline?: boolean
257265
}
258266

259267
type PropKeys = keyof TopNavBarItemOwnProps
@@ -281,6 +289,7 @@ type TopNavBarItemStyle = ComponentStyle<
281289
type TopNavBarItemState = {
282290
isSubmenuOpen: boolean
283291
isPopoverOpen: boolean
292+
isFocused: boolean
284293
}
285294

286295
type TopNavBarItemStyleProps = {
@@ -316,7 +325,8 @@ const propTypes: PropValidators<PropKeys> = {
316325
onKeyUp: PropTypes.func,
317326
elementRef: PropTypes.func,
318327
itemRef: PropTypes.func,
319-
shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never'])
328+
shouldCloseOnClick: PropTypes.oneOf(['auto', 'always', 'never']),
329+
withFocusOutline: PropTypes.bool
320330
}
321331

322332
const allowedProps: AllowedPropKeys = [
@@ -343,7 +353,8 @@ const allowedProps: AllowedPropKeys = [
343353
'onKeyUp',
344354
'elementRef',
345355
'itemRef',
346-
'shouldCloseOnClick'
356+
'shouldCloseOnClick',
357+
'withFocusOutline'
347358
]
348359

349360
export type {

packages/ui-top-nav-bar/src/TopNavBar/TopNavBarLayout/SmallViewportLayout/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ class TopNavBarSmallViewportLayout extends Component<
400400
tooltip: dropdownMenuToggleButtonTooltip,
401401
themeOverride: { itemSpacing: '0.375rem' },
402402
'aria-haspopup': 'menu',
403-
'aria-expanded': isDropdownMenuOpen
403+
'aria-expanded': isDropdownMenuOpen,
404+
withFocusOutline: isDropdownMenuOpen ? true : undefined
404405
}
405406

406407
const alternativeTitleIconProps = {

packages/ui-view/src/View/props.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,8 @@ type ViewOwnProps = {
135135
*/
136136
insetBlockEnd?: string
137137
/**
138-
* Manually control if the `View` should display a focus outline.<br/>
138+
* Manually control if the `View` should display a focus outline.
139+
*
139140
* When left `undefined` (which is the default) the focus outline will display
140141
* automatically if the `View` is focusable and receives focus.
141142
*/

0 commit comments

Comments
 (0)