Skip to content

Commit

Permalink
feat: add titleId prop to enhance a11y
Browse files Browse the repository at this point in the history
Closes #360
  • Loading branch information
gregberge committed Dec 23, 2019
1 parent b56407e commit 21409ae
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 23 deletions.
35 changes: 34 additions & 1 deletion packages/babel-plugin-svg-dynamic-title/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,46 @@ const plugin = ({ types: t }) => ({
children,
)
}

function createTitleIdAttribute() {
return t.jsxAttribute(
t.jsxIdentifier('id'),
t.jsxExpressionContainer(t.identifier('titleId')),
)
}

function enhanceAttributes(attributes) {
const existingId = attributes.find(
attribute => attribute.name.name === 'id',
)
if (!existingId) {
return [...attributes, createTitleIdAttribute()]
}
existingId.value = t.jsxExpressionContainer(
t.logicalExpression('||', t.identifier('titleId'), existingId.value),
)
return attributes
}

function getTitleElement(existingTitle) {
const titleExpression = t.identifier('title')
if (existingTitle) {
existingTitle.openingElement.attributes = enhanceAttributes(
existingTitle.openingElement.attributes,
)
}
let titleElement = t.conditionalExpression(
titleExpression,
createTitle(
[t.jsxExpressionContainer(titleExpression)],
existingTitle ? existingTitle.openingElement.attributes : [],
existingTitle
? existingTitle.openingElement.attributes
: [
t.jsxAttribute(
t.jsxIdentifier('id'),
t.jsxExpressionContainer(t.identifier('titleId')),
),
],
),
t.nullLiteral(),
)
Expand Down
16 changes: 8 additions & 8 deletions packages/babel-plugin-svg-dynamic-title/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,44 @@ const testPlugin = (code, options) => {
describe('plugin', () => {
it('should add title attribute if not present', () => {
expect(testPlugin('<svg></svg>')).toMatchInlineSnapshot(
`"<svg>{title ? <title>{title}</title> : null}</svg>;"`,
`"<svg>{title ? <title id={titleId}>{title}</title> : null}</svg>;"`,
)
})

it('should add title element and fallback to existing title', () => {
// testing when the existing title contains a simple string
expect(testPlugin(`<svg><title>Hello</title></svg>`)).toMatchInlineSnapshot(
`"<svg>{title === undefined ? <title>Hello</title> : title ? <title>{title}</title> : null}</svg>;"`,
`"<svg>{title === undefined ? <title id={titleId}>Hello</title> : title ? <title id={titleId}>{title}</title> : null}</svg>;"`,
)
// testing when the existing title contains an JSXExpression
expect(
testPlugin(`<svg><title>{"Hello"}</title></svg>`),
).toMatchInlineSnapshot(
`"<svg>{title === undefined ? <title>{\\"Hello\\"}</title> : title ? <title>{title}</title> : null}</svg>;"`,
`"<svg>{title === undefined ? <title id={titleId}>{\\"Hello\\"}</title> : title ? <title id={titleId}>{title}</title> : null}</svg>;"`,
)
})
it('should preserve any existing title attributes', () => {
// testing when the existing title contains a simple string
expect(
testPlugin(`<svg><title attr='a'>Hello</title></svg>`),
testPlugin(`<svg><title id='a'>Hello</title></svg>`),
).toMatchInlineSnapshot(
`"<svg>{title === undefined ? <title attr='a'>Hello</title> : title ? <title attr='a'>{title}</title> : null}</svg>;"`,
`"<svg>{title === undefined ? <title id={titleId || 'a'}>Hello</title> : title ? <title id={titleId || 'a'}>{title}</title> : null}</svg>;"`,
)
})
it('should support empty title', () => {
expect(testPlugin('<svg><title></title></svg>')).toMatchInlineSnapshot(
`"<svg>{title ? <title>{title}</title> : null}</svg>;"`,
`"<svg>{title ? <title id={titleId}>{title}</title> : null}</svg>;"`,
)
})
it('should support self closing title', () => {
expect(testPlugin('<svg><title /></svg>')).toMatchInlineSnapshot(
`"<svg>{title ? <title>{title}</title> : null}</svg>;"`,
`"<svg>{title ? <title id={titleId}>{title}</title> : null}</svg>;"`,
)
})

it('should work if an attribute is already present', () => {
expect(testPlugin('<svg><foo /></svg>')).toMatchInlineSnapshot(
`"<svg>{title ? <title>{title}</title> : null}<foo /></svg>;"`,
`"<svg>{title ? <title id={titleId}>{title}</title> : null}<foo /></svg>;"`,
)
})
})
9 changes: 9 additions & 0 deletions packages/babel-plugin-transform-svg-component/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ export const getProps = ({ types: t }, opts) => {
true,
),
)

props.push(
t.objectProperty(
t.identifier('titleId'),
t.identifier('titleId'),
false,
true,
),
)
}

if (opts.expandProps) {
Expand Down
11 changes: 11 additions & 0 deletions packages/babel-preset/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ const plugin = (api, opts) => {
]
}

if (opts.titleProp) {
toAddAttributes = [
...toAddAttributes,
{
name: 'aria-labelledby',
value: 'titleId',
literal: true,
},
]
}

if (opts.expandProps) {
toAddAttributes = [
...toAddAttributes,
Expand Down
15 changes: 9 additions & 6 deletions packages/babel-preset/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ describe('preset', () => {
"import React from \\"react\\";
function SvgComponent({
title
title,
titleId
}) {
return <svg>{title ? <title>{title}</title> : null}</svg>;
return <svg aria-labelledby={titleId}>{title ? <title id={titleId}>{title}</title> : null}</svg>;
}
export default SvgComponent;"
Expand All @@ -87,9 +88,10 @@ describe('preset', () => {
"import React from \\"react\\";
function SvgComponent({
title
title,
titleId
}) {
return <svg>{title === undefined ? <title>Hello</title> : title ? <title>{title}</title> : null}</svg>;
return <svg aria-labelledby={titleId}>{title === undefined ? <title id={titleId}>Hello</title> : title ? <title id={titleId}>{title}</title> : null}</svg>;
}
export default SvgComponent;"
Expand All @@ -106,9 +108,10 @@ describe('preset', () => {
"import React from \\"react\\";
function SvgComponent({
title
title,
titleId
}) {
return <svg>{title === undefined ? <title>{\\"Hello\\"}</title> : title ? <title>{title}</title> : null}</svg>;
return <svg aria-labelledby={titleId}>{title === undefined ? <title id={titleId}>{\\"Hello\\"}</title> : title ? <title id={titleId}>{title}</title> : null}</svg>;
}
export default SvgComponent;"
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/__snapshots__/index.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,10 @@ export default SvgFile
exports[`cli should support various args: --title-prop 1`] = `
"import React from 'react'
function SvgFile({ title, ...props }) {
function SvgFile({ title, titleId, ...props }) {
return (
<svg width={48} height={1} {...props}>
{title ? <title>{title}</title> : null}
<svg width={48} height={1} aria-labelledby={titleId} {...props}>
{title ? <title id={titleId}>{title}</title> : null}
<path d=\\"M0 0h48v1H0z\\" fill=\\"#063855\\" fillRule=\\"evenodd\\" />
</svg>
)
Expand Down
11 changes: 6 additions & 5 deletions packages/core/src/__snapshots__/convert.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,10 @@ export default noop
exports[`convert config should support options 16 1`] = `
"import React from 'react'
function SvgComponent({ title, ...props }) {
function SvgComponent({ title, titleId, ...props }) {
return (
<svg width={88} height={88} {...props}>
{title ? <title>{title}</title> : null}
<svg width={88} height={88} aria-labelledby={titleId} {...props}>
{title ? <title id={titleId}>{title}</title> : null}
<g
stroke=\\"#063855\\"
strokeWidth={2}
Expand Down Expand Up @@ -408,17 +408,18 @@ export default MemoSvgComponent
exports[`convert config titleProp: without title added 1`] = `
"import React from 'react'
function SvgComponent({ title, ...props }) {
function SvgComponent({ title, titleId, ...props }) {
return (
<svg
width={0}
height={0}
style={{
position: 'absolute',
}}
aria-labelledby={titleId}
{...props}
>
{title ? <title>{title}</title> : null}
{title ? <title id={titleId}>{title}</title> : null}
<path d=\\"M0 0h24v24H0z\\" fill=\\"none\\" />
</svg>
)
Expand Down

0 comments on commit 21409ae

Please sign in to comment.