Skip to content

Commit

Permalink
Initial draft of adding tabpanel aria data for tabpanel and dockpanel.
Browse files Browse the repository at this point in the history
  • Loading branch information
jasongrout authored and telamonian committed Nov 17, 2020
1 parent 67cfd6f commit 6ffad17
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 15 deletions.
22 changes: 22 additions & 0 deletions packages/widgets/src/docklayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -639,6 +639,8 @@ class DockLayout extends Layout {
return;
}

Private.removeAria(widget);

// If there are multiple tabs, just remove the widget's tab.
if (tabNode.tabBar.titles.length > 1) {
tabNode.tabBar.removeTab(widget.title);
Expand Down Expand Up @@ -770,6 +772,7 @@ class DockLayout extends Layout {
let tabNode = new Private.TabLayoutNode(this._createTabBar());
tabNode.tabBar.addTab(widget.title);
this._root = tabNode;
Private.addAria(widget, tabNode.tabBar);
return;
}

Expand All @@ -795,6 +798,7 @@ class DockLayout extends Layout {

// Insert the widget's tab relative to the target index.
refNode.tabBar.insertTab(index + (after ? 1 : 0), widget.title);
Private.addAria(widget, refNode.tabBar);
}

/**
Expand All @@ -815,6 +819,7 @@ class DockLayout extends Layout {
// Create the tab layout node to hold the widget.
let tabNode = new Private.TabLayoutNode(this._createTabBar());
tabNode.tabBar.addTab(widget.title);
Private.addAria(widget, tabNode.tabBar);

// Set the root if it does not exist.
if (!this._root) {
Expand Down Expand Up @@ -1988,6 +1993,22 @@ namespace Private {
}
}

export
async function addAria(widget: Widget, tabBar: TabBar<Widget>) {
let tabId = tabBar.renderer.createTabKey({title: widget.title, current: false, zIndex: 0});

if (tabId) {
widget.node.setAttribute('role', 'tabpanel');
widget.node.setAttribute('aria-labelledby', tabId);
}
}

export
async function removeAria(widget: Widget) {
widget.node.removeAttribute('role');
widget.node.removeAttribute('aria-labelledby');
}

/**
* Normalize a tab area config and collect the visited widgets.
*/
Expand Down Expand Up @@ -2077,6 +2098,7 @@ namespace Private {
each(config.widgets, widget => {
widget.hide();
tabBar.addTab(widget.title);
Private.addAria(widget, tabBar);
});

// Set the current index of the tab bar.
Expand Down
34 changes: 19 additions & 15 deletions packages/widgets/src/tabbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ import {
* property should be set to `false` when rotating nodes from CSS.
*/
export
class TabBar<T> extends Widget {
class TabBar<T extends Widget> extends Widget {
/**
* Construct a new tab bar.
*
Expand Down Expand Up @@ -302,6 +302,7 @@ class TabBar<T> extends Widget {
// Toggle the orientation values.
this._orientation = value;
this.dataset['orientation'] = value;
this.contentNode.setAttribute('aria-orientation', value);
}

/**
Expand Down Expand Up @@ -1406,6 +1407,8 @@ namespace TabBar {
* @returns A virtual element representing the tab.
*/
renderTab(data: IRenderData<T>): VirtualElement;

createTabKey(data: IRenderData<T>): string;
}

/**
Expand All @@ -1415,7 +1418,7 @@ namespace TabBar {
* Subclasses are free to reimplement rendering methods as needed.
*/
export
class Renderer implements IRenderer<any> {
class Renderer<T extends Widget = Widget> implements IRenderer<T> {
/**
* Construct a new renderer.
*/
Expand All @@ -1433,15 +1436,16 @@ namespace TabBar {
*
* @returns A virtual element representing the tab.
*/
renderTab(data: IRenderData<any>): VirtualElement {
renderTab(data: IRenderData<T>): VirtualElement {
let title = data.title.caption;
let key = this.createTabKey(data);
let id = key;
let style = this.createTabStyle(data);
let className = this.createTabClass(data);
let dataset = this.createTabDataset(data);
let aria = this.createTabARIA(data);
return (
h.li({ key, className, title, style, dataset, ...aria },
h.li({ id, key, className, title, style, dataset, ...aria },
this.renderIcon(data),
this.renderLabel(data),
this.renderCloseIcon(data)
Expand All @@ -1456,7 +1460,7 @@ namespace TabBar {
*
* @returns A virtual element representing the tab icon.
*/
renderIcon(data: IRenderData<any>): VirtualElement {
renderIcon(data: IRenderData<T>): VirtualElement {
const { title } = data;
let className = this.createIconClass(data);

Expand All @@ -1477,7 +1481,7 @@ namespace TabBar {
*
* @returns A virtual element representing the tab label.
*/
renderLabel(data: IRenderData<any>): VirtualElement {
renderLabel(data: IRenderData<T>): VirtualElement {
return h.div({
className: 'lm-TabBar-tabLabel'
/* <DEPRECATED> */
Expand All @@ -1493,7 +1497,7 @@ namespace TabBar {
*
* @returns A virtual element representing the tab close icon.
*/
renderCloseIcon(data: IRenderData<any>): VirtualElement {
renderCloseIcon(data: IRenderData<T>): VirtualElement {
return h.div({
className: 'lm-TabBar-tabCloseIcon'
/* <DEPRECATED> */
Expand All @@ -1514,7 +1518,7 @@ namespace TabBar {
* the key is generated. This enables efficient rendering of moved
* tabs and avoids subtle hover style artifacts.
*/
createTabKey(data: IRenderData<any>): string {
createTabKey(data: IRenderData<T>): string {
let key = this._tabKeys.get(data.title);
if (key === undefined) {
key = `tab-key-${this._tabID++}`;
Expand All @@ -1530,7 +1534,7 @@ namespace TabBar {
*
* @returns The inline style data for the tab.
*/
createTabStyle(data: IRenderData<any>): ElementInlineStyle {
createTabStyle(data: IRenderData<T>): ElementInlineStyle {
return { zIndex: `${data.zIndex}` };
}

Expand All @@ -1541,7 +1545,7 @@ namespace TabBar {
*
* @returns The full class name for the tab.
*/
createTabClass(data: IRenderData<any>): string {
createTabClass(data: IRenderData<T>): string {
let name = 'lm-TabBar-tab';
/* <DEPRECATED> */
name += ' p-TabBar-tab';
Expand Down Expand Up @@ -1571,7 +1575,7 @@ namespace TabBar {
*
* @returns The dataset for the tab.
*/
createTabDataset(data: IRenderData<any>): ElementDataset {
createTabDataset(data: IRenderData<T>): ElementDataset {
return data.title.dataset;
}

Expand All @@ -1582,8 +1586,8 @@ namespace TabBar {
*
* @returns The ARIA attributes for the tab.
*/
createTabARIA(data: IRenderData<any>): ElementARIAAttrs {
return {role: 'tab'};
createTabARIA(data: IRenderData<T>): ElementARIAAttrs {
return {role: 'tab', 'aria-controls': data.title.owner.id};
}

/**
Expand All @@ -1593,7 +1597,7 @@ namespace TabBar {
*
* @returns The full class name for the tab icon.
*/
createIconClass(data: IRenderData<any>): string {
createIconClass(data: IRenderData<T>): string {
let name = 'lm-TabBar-tabIcon';
/* <DEPRECATED> */
name += ' p-TabBar-tabIcon';
Expand All @@ -1603,7 +1607,7 @@ namespace TabBar {
}

private _tabID = 0;
private _tabKeys = new WeakMap<Title<any>, string>();
private _tabKeys = new WeakMap<Title<T>, string>();
}

/**
Expand Down
14 changes: 14 additions & 0 deletions packages/widgets/src/tabpanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
import {
Widget
} from './widget';
import { UUID } from '@phosphor/coreutils';


/**
Expand Down Expand Up @@ -270,8 +271,16 @@ class TabPanel extends Widget {
if (widget !== this.currentWidget) {
widget.hide();
}

widget.id = widget.id || `aria-${UUID.uuid4()}`;

this.stackedPanel.insertWidget(index, widget);
this.tabBar.insertTab(index, widget.title);

let tab = this.tabBar.contentNode.children[this.tabBar.titles.indexOf(widget.title)];

widget.node.setAttribute('role', 'tabpanel');
widget.node.setAttribute('aria-labelledby', tab.id);
}

/**
Expand Down Expand Up @@ -331,6 +340,11 @@ class TabPanel extends Widget {
* Handle the `widgetRemoved` signal from the stacked panel.
*/
private _onWidgetRemoved(sender: StackedPanel, widget: Widget): void {
widget.node.removeAttribute('role');
widget.node.removeAttribute('aria-labelledby');
if (widget.id.slice(5) === 'aria-') {
widget.id = '';
}
this.tabBar.removeTab(widget.title);
}

Expand Down

0 comments on commit 6ffad17

Please sign in to comment.