Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accessibility in accordion panel #537

Merged
merged 5 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions packages/widgets/src/accordionlayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*/

import { ArrayExt } from '@lumino/algorithm';
import { UUID } from '@lumino/coreutils';
import { SplitLayout } from './splitlayout';
import { Title } from './title';
import Utils from './utils';
Expand Down Expand Up @@ -83,6 +84,28 @@ export class AccordionLayout extends SplitLayout {
this.parent!.node.replaceChild(newTitle, oldTitle);
}

/**
* Insert a widget into the layout at the specified index.
*
* @param index - The index at which to insert the widget.
*
* @param widget - The widget to insert into the layout.
*
* #### Notes
* The index will be clamped to the bounds of the widgets.
*
* If the widget is already added to the layout, it will be moved.
*
* #### Undefined Behavior
* An `index` which is non-integral.
*/
insertWidget(index: number, widget: Widget): void {
if (!widget.id) {
widget.id = `id-${UUID.uuid4()}`;
}
super.insertWidget(index, widget);
}

/**
* Attach a widget to the parent's DOM node.
*
Expand Down
1 change: 0 additions & 1 deletion packages/widgets/src/accordionpanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,6 @@ export namespace AccordionPanel {
*/
createSectionTitle(data: Title<Widget>): HTMLElement {
const handle = document.createElement('h3');
handle.setAttribute('role', 'tab');
handle.setAttribute('tabindex', '0');
handle.id = this.createTitleKey(data);
handle.className = this.titleClassName;
Expand Down
30 changes: 30 additions & 0 deletions packages/widgets/tests/src/accordionpanel.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,36 @@ describe('@lumino/widgets', () => {
});
});

describe('#accessibility()', () => {
it('should create a widget ID if it does not exist', () => {
let panel = new LogAccordionPanel();
let w1 = new Widget();
let w2 = new Widget();

// Expects a widget ID to be created.
expect(w1.id).to.be.empty;
panel.addWidget(w1);
expect(w1.id).to.match(/id-[0-9a-z\-]+/);

// Expects the widget ID to be unchanged.
w2.id = 'test-id';
panel.addWidget(w2);
expect(w2.id).to.equal('test-id');
});

it('should link the widget to its title', () => {
let layout = new AccordionLayout({ renderer });
let panel = new LogAccordionPanel({ layout });
let w = new Widget();
panel.addWidget(w);

expect(layout.titles[0].getAttribute('aria-controls')).to.equal(w.id);
expect(layout.widgets[0].node.getAttribute('aria-labelledby')).to.equal(
layout.titles[0].id
);
});
});

describe('#dispose()', () => {
it('should dispose of the resources held by the panel', () => {
let panel = new LogAccordionPanel();
Expand Down
1 change: 1 addition & 0 deletions review/api/widgets.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export class AccordionLayout extends SplitLayout {
protected attachWidget(index: number, widget: Widget): void;
protected detachWidget(index: number, widget: Widget): void;
dispose(): void;
insertWidget(index: number, widget: Widget): void;
protected moveWidget(fromIndex: number, toIndex: number, widget: Widget): void;
readonly renderer: AccordionLayout.IRenderer;
get titles(): ReadonlyArray<HTMLElement>;
Expand Down