mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-09-25 18:30:10 +02:00
set describeby to accessible description element id
This commit is contained in:

parent
68b1805c40
commit
b51759d36e
@@ -33,46 +33,95 @@ describe('accessibility', () => {
|
|||||||
expect(noInsert_attr_spy).not.toHaveBeenCalled();
|
expect(noInsert_attr_spy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----------------
|
||||||
|
// Convenience functions to DRY up the spec
|
||||||
|
|
||||||
|
function expectAriaLabelledByIsTitleId(
|
||||||
|
svgD3Node: any,
|
||||||
|
title: string | null | undefined,
|
||||||
|
desc: string | null | undefined,
|
||||||
|
givenId: string
|
||||||
|
) {
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const svg_attr_spy = vi.spyOn(svgD3Node, 'attr').mockReturnValue(svgD3Node);
|
||||||
|
addSVGa11yTitleDescription(svgD3Node, title, desc, givenId);
|
||||||
|
expect(svg_attr_spy).toHaveBeenCalledWith('aria-labelledby', `chart-title-${givenId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectAriaDescribedByIsDescId(
|
||||||
|
svgD3Node: any,
|
||||||
|
title: string | null | undefined,
|
||||||
|
desc: string | null | undefined,
|
||||||
|
givenId: string
|
||||||
|
) {
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const svg_attr_spy = vi.spyOn(svgD3Node, 'attr').mockReturnValue(svgD3Node);
|
||||||
|
addSVGa11yTitleDescription(svgD3Node, title, desc, givenId);
|
||||||
|
expect(svg_attr_spy).toHaveBeenCalledWith('aria-describedby', `chart-desc-${givenId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function a11yTitleTagInserted(
|
||||||
|
svgD3Node: any,
|
||||||
|
title: string | null | undefined,
|
||||||
|
desc: string | null | undefined,
|
||||||
|
givenId: string,
|
||||||
|
callNumber: number
|
||||||
|
) {
|
||||||
|
a11yTagInserted(svgD3Node, title, desc, givenId, callNumber, 'title', title);
|
||||||
|
}
|
||||||
|
|
||||||
|
function a11yDescTagInserted(
|
||||||
|
svgD3Node: any,
|
||||||
|
title: string | null | undefined,
|
||||||
|
desc: string | null | undefined,
|
||||||
|
givenId: string,
|
||||||
|
callNumber: number
|
||||||
|
) {
|
||||||
|
a11yTagInserted(svgD3Node, title, desc, givenId, callNumber, 'desc', desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function a11yTagInserted(
|
||||||
|
svgD3Node: any,
|
||||||
|
title: string | null | undefined,
|
||||||
|
desc: string | null | undefined,
|
||||||
|
givenId: string,
|
||||||
|
callNumber: number,
|
||||||
|
expectedPrefix: string,
|
||||||
|
expectedText: string | null | undefined
|
||||||
|
) {
|
||||||
|
const faux_insertedD3 = new MockedD3();
|
||||||
|
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_insertedD3);
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const title_attr_spy = vi.spyOn(faux_insertedD3, 'attr').mockReturnValue(faux_insertedD3);
|
||||||
|
const title_text_spy = vi.spyOn(faux_insertedD3, 'text');
|
||||||
|
|
||||||
|
addSVGa11yTitleDescription(fauxSvgNode, title, desc, givenId);
|
||||||
|
expect(svg_insert_spy).toHaveBeenCalledWith(expectedPrefix, ':first-child');
|
||||||
|
expect(title_attr_spy).toHaveBeenCalledWith('id', `chart-${expectedPrefix}-${givenId}`);
|
||||||
|
expect(title_text_spy).toHaveBeenNthCalledWith(callNumber, expectedText);
|
||||||
|
}
|
||||||
|
// ----------------
|
||||||
|
|
||||||
describe('given an a11y title', () => {
|
describe('given an a11y title', () => {
|
||||||
const a11yTitle = 'a11y title';
|
const a11yTitle = 'a11y title';
|
||||||
|
|
||||||
describe('given an a11y description', () => {
|
describe('given an a11y description', () => {
|
||||||
const a11yDesc = 'a11y description';
|
const a11yDesc = 'a11y description';
|
||||||
|
|
||||||
it('sets aria-labelledby to the title id and the description id inserted as children', () => {
|
it('sets aria-labelledby to the title id inserted as a child', () => {
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
expectAriaLabelledByIsTitleId(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
});
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_attr_spy).toHaveBeenCalledWith(
|
it('sets aria-describedby to the description id inserted as a child', () => {
|
||||||
'aria-labelledby',
|
expectAriaDescribedByIsDescId(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
`chart-title-${givenId} chart-desc-${givenId}`
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inserts a title tag as the first child with the text set to the accTitle given', () => {
|
it('inserts a title tag as the first child with the text set to the accTitle given', () => {
|
||||||
const faux_title = new MockedD3();
|
a11yTitleTagInserted(fauxSvgNode, a11yTitle, a11yDesc, givenId, 2);
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
|
||||||
const title_attr_spy = vi.spyOn(faux_title, 'attr').mockReturnValue(faux_title);
|
|
||||||
const title_text_spy = vi.spyOn(faux_title, 'text');
|
|
||||||
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_insert_spy).toHaveBeenCalledWith('desc', ':first-child');
|
|
||||||
expect(title_attr_spy).toHaveBeenCalledWith('id', `chart-desc-` + givenId);
|
|
||||||
expect(title_text_spy).toHaveBeenNthCalledWith(1, 'a11y description');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inserts a desc tag as the 2nd child with the text set to accDescription given', () => {
|
it('inserts a desc tag as the 2nd child with the text set to accDescription given', () => {
|
||||||
const faux_desc = new MockedD3();
|
a11yDescTagInserted(fauxSvgNode, a11yTitle, a11yDesc, givenId, 1);
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_desc);
|
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
|
||||||
const desc_attr_spy = vi.spyOn(faux_desc, 'attr').mockReturnValue(faux_desc);
|
|
||||||
const desc_text_spy = vi.spyOn(faux_desc, 'text');
|
|
||||||
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_insert_spy).toHaveBeenCalledWith('desc', ':first-child');
|
|
||||||
expect(desc_attr_spy).toHaveBeenCalledWith('id', `chart-desc-` + givenId);
|
|
||||||
expect(desc_text_spy).toHaveBeenNthCalledWith(1, 'a11y description');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -80,23 +129,18 @@ describe('accessibility', () => {
|
|||||||
const a11yDesc = undefined;
|
const a11yDesc = undefined;
|
||||||
|
|
||||||
it('sets aria-labelledby to the title id inserted as a child', () => {
|
it('sets aria-labelledby to the title id inserted as a child', () => {
|
||||||
|
expectAriaLabelledByIsTitleId(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('no aria-describedby is set', () => {
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
expect(svg_attr_spy).toHaveBeenCalledWith('aria-labelledby', `chart-title-${givenId}`);
|
expect(svg_attr_spy).not.toHaveBeenCalledWith('aria-describedby', expect.anything());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inserts a title tag as the first child with the text set to the accTitle given', () => {
|
it('inserts a title tag as the first child with the text set to the accTitle given', () => {
|
||||||
const faux_title = new MockedD3();
|
a11yTitleTagInserted(fauxSvgNode, a11yTitle, a11yDesc, givenId, 1);
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
|
||||||
const title_attr_spy = vi.spyOn(faux_title, 'attr').mockReturnValue(faux_title);
|
|
||||||
const title_text_spy = vi.spyOn(faux_title, 'text');
|
|
||||||
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_insert_spy).toHaveBeenCalledWith('title', ':first-child');
|
|
||||||
expect(title_attr_spy).toHaveBeenCalledWith('id', `chart-title-` + givenId);
|
|
||||||
expect(title_text_spy).toHaveBeenNthCalledWith(1, 'a11y title');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('no description tag is inserted', () => {
|
it('no description tag is inserted', () => {
|
||||||
@@ -114,6 +158,13 @@ describe('accessibility', () => {
|
|||||||
describe('given an a11y description', () => {
|
describe('given an a11y description', () => {
|
||||||
const a11yDesc = 'a11y description';
|
const a11yDesc = 'a11y description';
|
||||||
|
|
||||||
|
it('no aria-labelledby is set', () => {
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
||||||
|
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
|
expect(svg_attr_spy).not.toHaveBeenCalledWith('aria-labelledby', expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
it('no title tag inserted', () => {
|
it('no title tag inserted', () => {
|
||||||
const faux_title = new MockedD3();
|
const faux_title = new MockedD3();
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
||||||
@@ -121,30 +172,32 @@ describe('accessibility', () => {
|
|||||||
expect(svg_insert_spy).not.toHaveBeenCalledWith('title', ':first-child');
|
expect(svg_insert_spy).not.toHaveBeenCalledWith('title', ':first-child');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('sets aria-labelledby to the description id inserted as a child', () => {
|
it('sets aria-describedby to the description id inserted as a child', () => {
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
expectAriaDescribedByIsDescId(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_attr_spy).toHaveBeenCalledWith('aria-labelledby', `chart-desc-${givenId}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('inserts a desc tag as a child with the text set to accDescription given', () => {
|
it('inserts a desc tag as the 2nd child with the text set to accDescription given', () => {
|
||||||
const faux_desc = new MockedD3();
|
a11yDescTagInserted(fauxSvgNode, a11yTitle, a11yDesc, givenId, 1);
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_desc);
|
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
|
||||||
const desc_attr_spy = vi.spyOn(faux_desc, 'attr').mockReturnValue(faux_desc);
|
|
||||||
const desc_text_spy = vi.spyOn(faux_desc, 'text');
|
|
||||||
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_insert_spy).toHaveBeenCalledWith('desc', ':first-child');
|
|
||||||
expect(desc_attr_spy).toHaveBeenCalledWith('id', `chart-desc-` + givenId);
|
|
||||||
expect(desc_text_spy).toHaveBeenNthCalledWith(1, 'a11y description');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('no a11y description', () => {
|
describe('no a11y description', () => {
|
||||||
const a11yDesc = undefined;
|
const a11yDesc = undefined;
|
||||||
|
|
||||||
|
it('no aria-labelledby is set', () => {
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
||||||
|
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
|
expect(svg_attr_spy).not.toHaveBeenCalledWith('aria-labelledby', expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('no aria-describedby is set', () => {
|
||||||
|
// @ts-ignore Required to easily handle the d3 select types
|
||||||
|
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
||||||
|
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
|
expect(svg_attr_spy).not.toHaveBeenCalledWith('aria-describedby', expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
it('no title tag inserted', () => {
|
it('no title tag inserted', () => {
|
||||||
const faux_title = new MockedD3();
|
const faux_title = new MockedD3();
|
||||||
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
const svg_insert_spy = vi.spyOn(fauxSvgNode, 'insert').mockReturnValue(faux_title);
|
||||||
@@ -158,13 +211,6 @@ describe('accessibility', () => {
|
|||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
||||||
expect(svg_insert_spy).not.toHaveBeenCalledWith('desc', ':first-child');
|
expect(svg_insert_spy).not.toHaveBeenCalledWith('desc', ':first-child');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('no aria-labelledby is set', () => {
|
|
||||||
// @ts-ignore Required to easily handle the d3 select types
|
|
||||||
const svg_attr_spy = vi.spyOn(fauxSvgNode, 'attr').mockReturnValue(fauxSvgNode);
|
|
||||||
addSVGa11yTitleDescription(fauxSvgNode, a11yTitle, a11yDesc, givenId);
|
|
||||||
expect(svg_attr_spy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -40,14 +40,15 @@ export function addSVGa11yTitleDescription(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const titleId = a11yTitle ? 'chart-title-' + baseId : null;
|
|
||||||
const descId = a11yDesc ? 'chart-desc-' + baseId : null;
|
|
||||||
if (a11yTitle || a11yDesc) {
|
if (a11yTitle || a11yDesc) {
|
||||||
svg.attr('aria-labelledby', compact([titleId, descId]).join(' '));
|
|
||||||
if (a11yDesc) {
|
if (a11yDesc) {
|
||||||
|
const descId = 'chart-desc-' + baseId;
|
||||||
|
svg.attr('aria-describedby', descId);
|
||||||
svg.insert('desc', ':first-child').attr('id', descId).text(a11yDesc);
|
svg.insert('desc', ':first-child').attr('id', descId).text(a11yDesc);
|
||||||
}
|
}
|
||||||
if (a11yTitle) {
|
if (a11yTitle) {
|
||||||
|
const titleId = 'chart-title-' + baseId;
|
||||||
|
svg.attr('aria-labelledby', titleId);
|
||||||
svg.insert('title', ':first-child').attr('id', titleId).text(a11yTitle);
|
svg.insert('title', ':first-child').attr('id', titleId).text(a11yTitle);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Reference in New Issue
Block a user