Skip to content

Commit

Permalink
♿ a11y(bal-nav): Connect Tabs and Overlays (#1451)
Browse files Browse the repository at this point in the history
* Create PR for #1416

* fix: implement a11y for nav and tabs

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Gery Hirschfeld <[email protected]>
  • Loading branch information
github-actions[bot] and hirsch88 authored Sep 13, 2024
1 parent 6c7a3f6 commit d42d3d3
Show file tree
Hide file tree
Showing 15 changed files with 119 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-queens-brush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@baloise/ds-core': patch
---

**nav**: add aria control to nav tabs and connect them to the flyout
5 changes: 5 additions & 0 deletions .changeset/nervous-jobs-roll.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@baloise/ds-core': minor
---

**tabs**: tabs can be created without a integrated panel
24 changes: 24 additions & 0 deletions packages/core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,10 @@ export namespace Components {
"position": BalProps.BalNavMenuBarPosition;
}
interface BalNavMenuFlyout {
/**
* This is used to connect the flyout to the aria controls
*/
"navId": string;
}
interface BalNavMetaBar {
/**
Expand Down Expand Up @@ -3100,6 +3104,10 @@ export namespace Components {
* Tells if this route is active and overrides the bal-tabs value property.
*/
"active": boolean;
/**
* A11y attributes for the native tab element.
*/
"aria"?: BalProps.BalTabItemAria;
/**
* If `true` a small red bubble is added to the tab.
*/
Expand Down Expand Up @@ -3128,6 +3136,10 @@ export namespace Components {
* Label for the tab.
*/
"label": string;
/**
* If `true` the tab does not have a panel
*/
"noPanel": boolean;
/**
* Tell's if the linking is done by a router.
*/
Expand Down Expand Up @@ -7138,6 +7150,10 @@ declare namespace LocalJSX {
"position"?: BalProps.BalNavMenuBarPosition;
}
interface BalNavMenuFlyout {
/**
* This is used to connect the flyout to the aria controls
*/
"navId"?: string;
}
interface BalNavMetaBar {
/**
Expand Down Expand Up @@ -8143,6 +8159,10 @@ declare namespace LocalJSX {
* Tells if this route is active and overrides the bal-tabs value property.
*/
"active"?: boolean;
/**
* A11y attributes for the native tab element.
*/
"aria"?: BalProps.BalTabItemAria;
/**
* If `true` a small red bubble is added to the tab.
*/
Expand All @@ -8167,6 +8187,10 @@ declare namespace LocalJSX {
* Label for the tab.
*/
"label"?: string;
/**
* If `true` the tab does not have a panel
*/
"noPanel"?: boolean;
/**
* Emitted when the link element has clicked
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@
align-self: flex-start
z-index: 1
height: auto
padding-top: .8em
margin-top: .8em
+desktop
padding-top: 1rem
margin-top: 1rem
+widescreen
padding-top: 1.5rem
margin-top: 1.5rem

// Space helpers for the content after the bar
// --------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, h, ComponentInterface, Host, Element, State } from '@stencil/core'
import { Component, h, ComponentInterface, Host, Element, State, Prop } from '@stencil/core'
import { BEM } from '../../../utils/bem'
import { LogInstance, Loggable, Logger } from '../../../utils/log'
import { BalResizeObserver, ListenToResize } from '../../../utils/resize'
Expand All @@ -9,7 +9,6 @@ import { BalScrollHandler } from '../../../utils/scroll'
styleUrl: 'bal-nav-menu-flyout.sass',
})
export class NavMenuFlyout implements ComponentInterface, Loggable, BalResizeObserver {
private navMenuFlyoutId = `bal-nav-menu-flyout-${NavMenuFlyOutIds++}`
private bodyScrollHandler = new BalScrollHandler()

@Element() el!: HTMLElement
Expand All @@ -28,6 +27,11 @@ export class NavMenuFlyout implements ComponentInterface, Loggable, BalResizeObs
* ------------------------------------------------------
*/

/**
* This is used to connect the flyout to the aria controls
* */
@Prop() navId = `bal-nav-x${NavMenuFlyOutIds++}`

/**
* LIFECYCLE
* ------------------------------------------------------
Expand Down Expand Up @@ -75,7 +79,7 @@ export class NavMenuFlyout implements ComponentInterface, Loggable, BalResizeObs

return (
<Host
id={this.navMenuFlyoutId}
id={`${this.navId}-menu-flyout`}
class={{
...block.class(),
}}
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/components/bal-nav/bal-nav.sass
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,15 @@
display: block
width: 100%

// Logo
// --------------------------------------------------
.bal-nav__logo:focus-visible
@extend %focus-shadow

// Flyout
// --------------------------------------------------
.bal-nav__flyout
background: var(--bal-color-white)
scroll-padding-top: 1rem
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/components/bal-nav/bal-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,11 @@ export class Nav
value={this.activeMetaLinkValue}
onBalChange={ev => this.onMetaBarTabChange(ev)}
>
{this.linkItems.map(item => item.render())}
{this.linkItems.map(item =>
item.render({
flyoutId: `${this.navId}-menu-flyout`,
}),
)}
</bal-tabs>
) : (
<span></span>
Expand All @@ -450,13 +454,14 @@ export class Nav
.find(item => item.value === this.activeMetaLinkValue)
?.mainLinkItems.map(item =>
item.render({
flyoutId: `${this.navId}-menu-flyout`,
onClick: () => this.onMenuBarTabChange(item.value),
}),
)}
</bal-tabs>
</bal-stack>
{this.isFlyoutActive ? (
<bal-nav-menu-flyout>
<bal-nav-menu-flyout navId={this.navId}>
<bal-nav-link
role="listitem"
variant="overview"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class NavLinkItem implements BalProps.BalNavLinkItem {
)
}

render(_context?: { onClick: () => void }) {
render(_context?: { onClick: () => void; flyoutId: string }) {
return (
<bal-nav-link
role="listitem"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,16 +86,27 @@ export class NavMenuLinkItem extends NavLinkItem implements BalProps.BalNavMenuL
)
}

override render(context?: { onClick: () => void }) {
override render(context?: { onClick: () => void; flyoutId: string }) {
const hasChildren = this.sectionLinkItems.length > 0 || this.serviceLinkItems.length > 0
if (!hasChildren && this.isLink) {
return <bal-tab-item label={this.label} value={this.value} href={this.href} target={this.target}></bal-tab-item>
return (
<bal-tab-item
aria={{ controls: context.flyoutId }}
label={this.label}
value={this.value}
href={this.href}
target={this.target}
no-panel
></bal-tab-item>
)
}

return (
<bal-tab-item
aria={{ controls: context.flyoutId }}
label={this.label}
value={this.value}
no-panel
onBalNavigate={ev => {
context?.onClick()
if (this.onClick) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,15 +77,26 @@ export class NavMetaLinkItem extends NavLinkItem implements BalProps.BalNavMetaL
)
}

override render() {
override render(context?: { flyoutId: string }) {
if (this.isLink) {
return <bal-tab-item label={this.label} value={this.value} href={this.href} target={this.target}></bal-tab-item>
return (
<bal-tab-item
aria={{ controls: context.flyoutId }}
label={this.label}
value={this.value}
href={this.href}
target={this.target}
no-panel
></bal-tab-item>
)
}

return (
<bal-tab-item
aria={{ controls: context.flyoutId }}
label={this.label}
value={this.value}
no-panel
onBalNavigate={ev => {
if (this.onClick) {
this.onClick(ev.detail)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ export class TabItem {
*/
@Prop({ reflect: true }) icon?: string = undefined

/**
* If `true` the tab does not have a panel
*/
@Prop() noPanel = false

/**
* A11y attributes for the native tab element.
*/
@Prop() aria?: BalProps.BalTabItemAria = undefined

/**
* Emitted when the link element has clicked
*/
Expand Down Expand Up @@ -105,21 +115,27 @@ export class TabItem {
prevent: this.prevent,
navigate: this.balNavigate,
trackingData: this.inheritAttributes,
noPanel: this.noPanel,
aria: this.aria,
}
}

render() {
const hasPanel = !this.noPanel
const noPanelOrInactive = !this.isActive || this.noPanel

return (
<Host
id={this.tabPanelID}
role="tabpanel"
aria-label={this.label}
aria-hidden={!this.isActive ? 'true' : 'false'}
tabindex={this.isActive ? undefined : '-1'}
class={{
'bal-tab-item': true,
'bal-tab-item--active': this.isActive,
}}
role={hasPanel ? 'tabpanel' : undefined}
aria-label={hasPanel ? this.label : undefined}
aria-hidden={noPanelOrInactive ? 'true' : 'false'}
tabindex={noPanelOrInactive ? '-1' : undefined}
hidden={noPanelOrInactive ? true : undefined}
>
<slot />
</Host>
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/components/bal-tabs/bal-tab.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface BalTabOption {
navigate?: EventEmitter<Event>
trackingData?: Attributes
hidden?: boolean // deprecated use invisible instead
noPanel?: boolean
aria?: BalProps.BalTabItemAria
}

export interface TabLineProps {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/components/bal-tabs/bal-tabs.interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ namespace BalProps {
export type BalTabsVertical = boolean | 'mobile' | 'tablet'
export type BalTabsFloat = 'left' | 'right'
export type BalTabsColSize = 'one-quarter' | 'one-third' | 'half' | 'two-thirds' | 'three-quarters' | 'full'
export type BalTabItemAria = {
controls?: string
}
}

namespace BalEvents {
Expand Down
10 changes: 8 additions & 2 deletions packages/core/src/components/bal-tabs/components/tab-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export interface TabButtonProps {
isVertical: boolean
accordion: boolean
isAccordionOpen: boolean
isLinkList: boolean
inverted: boolean
expanded: boolean
spaceless: boolean
Expand All @@ -32,6 +33,7 @@ export const TabButton: FunctionalComponent<TabButtonProps> = ({
isVertical,
accordion,
isAccordionOpen,
isLinkList,
inverted,
expanded,
spaceless,
Expand Down Expand Up @@ -68,8 +70,8 @@ export const TabButton: FunctionalComponent<TabButtonProps> = ({
? {
'type': 'button',
'role': 'tab',
'tabindex': item.active ? '0' : '-1',
'aria-controls': item.tabPanelID,
'aria-controls': item.aria?.controls || item.tabPanelID || undefined,
'aria-expanded': item.active ? 'true' : 'false',
'aria-disabled': `${item.disabled}`,
'aria-label': item.label,
}
Expand All @@ -78,6 +80,10 @@ export const TabButton: FunctionalComponent<TabButtonProps> = ({
target: item.target,
}

if (!isLinkList) {
attrs['tabindex'] = item.active ? '0' : '-1'
}

return (
<TagType
id={`${tabsId}-button-${toKebabCase(item.value)}`}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const TabNav: FunctionalComponent<TabNavProps> = ({
const Button: FunctionalComponent<{ item: BalTabOption; index: number }> = ({ item, index }) => (
<TabButton
item={item}
isLinkList={isLinkList}
tabsId={tabsId}
isFirst={index === 0}
isLast={index === tabs.length - 1}
Expand Down

0 comments on commit d42d3d3

Please sign in to comment.