From a0637d49b60243bbae461f7b96dab9b47cd82d8f Mon Sep 17 00:00:00 2001
From: Ava Li <62368440+Aveline-art@users.noreply.github.com>
Date: Sun, 19 Jun 2022 02:54:54 -0700
Subject: [PATCH] feat: add descProp option (#729)
---
.../babel-plugin-svg-dynamic-title/README.md | 4 +
.../src/index.test.ts | 77 +++++++++--
.../src/index.ts | 67 +++++----
.../src/__snapshots__/index.test.ts.snap | 130 +++++++++++++++++-
.../src/index.test.ts | 46 ++++++-
.../src/types.ts | 1 +
.../src/variables.ts | 63 +++++----
packages/babel-preset/src/index.test.ts | 49 +++++++
packages/babel-preset/src/index.ts | 16 +++
packages/cli/README.md | 1 +
.../cli/src/__snapshots__/index.test.ts.snap | 52 +++++++
packages/cli/src/index.test.ts | 2 +
packages/cli/src/index.ts | 1 +
.../src/__snapshots__/config.test.ts.snap | 8 ++
.../src/__snapshots__/transform.test.ts.snap | 50 +++++++
packages/core/src/config.ts | 2 +
packages/core/src/transform.test.ts | 12 ++
packages/plugin-jsx/src/index.ts | 1 +
website/pages/docs/options.mdx | 8 ++
.../components/playground/config/settings.js | 7 +
20 files changed, 534 insertions(+), 63 deletions(-)
diff --git a/packages/babel-plugin-svg-dynamic-title/README.md b/packages/babel-plugin-svg-dynamic-title/README.md
index efe47ac1..904db858 100644
--- a/packages/babel-plugin-svg-dynamic-title/README.md
+++ b/packages/babel-plugin-svg-dynamic-title/README.md
@@ -16,6 +16,10 @@ npm install --save-dev @svgr/babel-plugin-svg-dynamic-title
}
```
+## Note
+
+This plugin handles both the titleProp and descProp options. By default, it will handle titleProp only.
+
## License
MIT
diff --git a/packages/babel-plugin-svg-dynamic-title/src/index.test.ts b/packages/babel-plugin-svg-dynamic-title/src/index.test.ts
index 32cb9dbb..4753c5dc 100644
--- a/packages/babel-plugin-svg-dynamic-title/src/index.test.ts
+++ b/packages/babel-plugin-svg-dynamic-title/src/index.test.ts
@@ -1,16 +1,16 @@
import { transform } from '@babel/core'
-import plugin from '.'
+import plugin, { Options } from '.'
-const testPlugin = (code: string) => {
+const testPlugin = (code: string, options: Options = {tag: 'title'}) => {
const result = transform(code, {
- plugins: ['@babel/plugin-syntax-jsx', plugin],
+ plugins: ['@babel/plugin-syntax-jsx', [plugin, options]],
configFile: false,
})
return result?.code
}
-describe('plugin', () => {
+describe('title plugin', () => {
it('should add title attribute if not present', () => {
expect(testPlugin('')).toMatchInlineSnapshot(
`";"`,
@@ -19,7 +19,9 @@ describe('plugin', () => {
it('should add title element and fallback to existing title', () => {
// testing when the existing title contains a simple string
- expect(testPlugin(``)).toMatchInlineSnapshot(
+ expect(
+ testPlugin(``),
+ ).toMatchInlineSnapshot(
`";"`,
)
// testing when the existing title contains an JSXExpression
@@ -38,19 +40,78 @@ describe('plugin', () => {
)
})
it('should support empty title', () => {
- expect(testPlugin('')).toMatchInlineSnapshot(
+ expect(
+ testPlugin(''),
+ ).toMatchInlineSnapshot(
`";"`,
)
})
it('should support self closing title', () => {
- expect(testPlugin('')).toMatchInlineSnapshot(
+ expect(
+ testPlugin(''),
+ ).toMatchInlineSnapshot(
`";"`,
)
})
it('should work if an attribute is already present', () => {
- expect(testPlugin('')).toMatchInlineSnapshot(
+ expect(
+ testPlugin(''),
+ ).toMatchInlineSnapshot(
`";"`,
)
})
})
+
+describe('desc plugin', () => {
+ it('should add desc attribute if not present', () => {
+ expect(testPlugin('', { tag: 'desc' })).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+
+ it('should add desc element and fallback to existing desc', () => {
+ // testing when the existing desc contains a simple string
+ expect(
+ testPlugin(``, { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ // testing when the existing desc contains an JSXExpression
+ expect(
+ testPlugin(``, { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+ it('should preserve any existing desc attributes', () => {
+ // testing when the existing desc contains a simple string
+ expect(
+ testPlugin(``, { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+ it('should support empty desc', () => {
+ expect(
+ testPlugin('', { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+ it('should support self closing desc', () => {
+ expect(
+ testPlugin('', { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+
+ it('should work if an attribute is already present', () => {
+ expect(
+ testPlugin('', { tag: 'desc' }),
+ ).toMatchInlineSnapshot(
+ `";"`,
+ )
+ })
+})
diff --git a/packages/babel-plugin-svg-dynamic-title/src/index.ts b/packages/babel-plugin-svg-dynamic-title/src/index.ts
index 6673f511..220c5962 100644
--- a/packages/babel-plugin-svg-dynamic-title/src/index.ts
+++ b/packages/babel-plugin-svg-dynamic-title/src/index.ts
@@ -3,25 +3,37 @@ import { NodePath, types as t } from '@babel/core'
const elements = ['svg', 'Svg']
-const createTitleElement = (
+type tag = 'title' | 'desc'
+
+export interface Options {
+ tag: tag | null
+}
+
+interface State {
+ opts: Options
+}
+
+const createTagElement = (
+ tag: tag,
children: t.JSXExpressionContainer[] = [],
attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[] = [],
) => {
- const title = t.jsxIdentifier('title')
+ const eleName = t.jsxIdentifier(tag)
return t.jsxElement(
- t.jsxOpeningElement(title, attributes),
- t.jsxClosingElement(title),
+ t.jsxOpeningElement(eleName, attributes),
+ t.jsxClosingElement(eleName),
children,
)
}
-const createTitleIdAttribute = () =>
+const createTagIdAttribute = (tag: tag) =>
t.jsxAttribute(
t.jsxIdentifier('id'),
- t.jsxExpressionContainer(t.identifier('titleId')),
+ t.jsxExpressionContainer(t.identifier(`${tag}Id`)),
)
-const addTitleIdAttribute = (
+const addTagIdAttribute = (
+ tag: tag,
attributes: (t.JSXAttribute | t.JSXSpreadAttribute)[],
) => {
const existingId = attributes.find(
@@ -29,19 +41,20 @@ const addTitleIdAttribute = (
) as t.JSXAttribute | undefined
if (!existingId) {
- return [...attributes, createTitleIdAttribute()]
+ return [...attributes, createTagIdAttribute(tag)]
}
existingId.value = t.jsxExpressionContainer(
t.isStringLiteral(existingId.value)
- ? t.logicalExpression('||', t.identifier('titleId'), existingId.value)
- : t.identifier('titleId'),
+ ? t.logicalExpression('||', t.identifier(`${tag}Id`), existingId.value)
+ : t.identifier(`${tag}Id`),
)
return attributes
}
const plugin = () => ({
visitor: {
- JSXElement(path: NodePath) {
+ JSXElement(path: NodePath, state: State) {
+ const tag = state.opts.tag || 'title'
if (!elements.length) return
const openingElement = path.get('openingElement')
@@ -54,22 +67,24 @@ const plugin = () => ({
return
}
- const getTitleElement = (
+ const getTagElement = (
existingTitle?: t.JSXElement,
): t.JSXExpressionContainer => {
- const titleExpression = t.identifier('title')
+ const tagExpression = t.identifier(tag)
if (existingTitle) {
- existingTitle.openingElement.attributes = addTitleIdAttribute(
+ existingTitle.openingElement.attributes = addTagIdAttribute(
+ tag,
existingTitle.openingElement.attributes,
)
}
const conditionalTitle = t.conditionalExpression(
- titleExpression,
- createTitleElement(
- [t.jsxExpressionContainer(titleExpression)],
+ tagExpression,
+ createTagElement(
+ tag,
+ [t.jsxExpressionContainer(tagExpression)],
existingTitle
? existingTitle.openingElement.attributes
- : [createTitleIdAttribute()],
+ : [createTagIdAttribute(tag)],
),
t.nullLiteral(),
)
@@ -80,7 +95,7 @@ const plugin = () => ({
t.conditionalExpression(
t.binaryExpression(
'===',
- titleExpression,
+ tagExpression,
t.identifier('undefined'),
),
existingTitle,
@@ -92,25 +107,25 @@ const plugin = () => ({
}
// store the title element
- let titleElement: t.JSXExpressionContainer | null = null
+ let tagElement: t.JSXExpressionContainer | null = null
const hasTitle = path.get('children').some((childPath) => {
- if (childPath.node === titleElement) return false
+ if (childPath.node === tagElement) return false
if (!childPath.isJSXElement()) return false
const name = childPath.get('openingElement').get('name')
if (!name.isJSXIdentifier()) return false
- if (name.node.name !== 'title') return false
- titleElement = getTitleElement(childPath.node)
- childPath.replaceWith(titleElement)
+ if (name.node.name !== tag) return false
+ tagElement = getTagElement(childPath.node)
+ childPath.replaceWith(tagElement)
return true
})
// create a title element if not already create
- titleElement = titleElement || getTitleElement()
+ tagElement = tagElement || getTagElement()
if (!hasTitle) {
// path.unshiftContainer is not working well :(
// path.unshiftContainer('children', titleElement)
- path.node.children.unshift(titleElement)
+ path.node.children.unshift(tagElement)
path.replaceWith(path.node)
}
},
diff --git a/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap b/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
index 587508f1..7029e262 100644
--- a/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
+++ b/packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.ts.snap
@@ -97,6 +97,29 @@ const SvgComponent = () => ;
export default SvgComponent;"
`;
+exports[`plugin javascript with "descProp" adds "desc" and "descId" prop 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = ({
+ desc,
+ descId
+}) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript with "descProp" and "expandProps" adds "desc", "descId" props and expands props 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = ({
+ desc,
+ descId,
+ ...props
+}) => ;
+
+export default SvgComponent;"
+`;
+
exports[`plugin javascript with "expandProps" add props 1`] = `
"import * as React from \\"react\\";
@@ -194,7 +217,21 @@ const ForwardRef = forwardRef(SvgComponent);
export default ForwardRef;"
`;
-exports[`plugin javascript with "titleProp" adds "titleProp" and "titleId" prop 1`] = `
+exports[`plugin javascript with "titleProp" "descProp" and "expandProps" adds "title", "titleId", "desc", "descId" props and expands props 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = ({
+ title,
+ titleId,
+ desc,
+ descId,
+ ...props
+}) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript with "titleProp" adds "title" and "titleId" prop 1`] = `
"import * as React from \\"react\\";
const SvgComponent = ({
@@ -205,7 +242,20 @@ const SvgComponent = ({
export default SvgComponent;"
`;
-exports[`plugin javascript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
+exports[`plugin javascript with "titleProp" and "descProp" adds "title", "titleId", "desc", and "descId prop 1`] = `
+"import * as React from \\"react\\";
+
+const SvgComponent = ({
+ title,
+ titleId,
+ desc,
+ descId
+}) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin javascript with "titleProp" and "expandProps" adds "title", "titleId" props and expands props 1`] = `
"import * as React from \\"react\\";
const SvgComponent = ({
@@ -325,6 +375,38 @@ const SvgComponent = () => ;
export default SvgComponent;"
`;
+exports[`plugin typescript with "descProp" adds "desc" and "descId" prop 1`] = `
+"import * as React from \\"react\\";
+interface SVGRProps {
+ desc?: string;
+ descId?: string;
+}
+
+const SvgComponent = ({
+ desc,
+ descId
+}: SVGRProps) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript with "descProp" and "expandProps" adds "desc", "descId" props and expands props 1`] = `
+"import * as React from \\"react\\";
+import { SVGProps } from \\"react\\";
+interface SVGRProps {
+ desc?: string;
+ descId?: string;
+}
+
+const SvgComponent = ({
+ desc,
+ descId,
+ ...props
+}: SVGProps & SVGRProps) => ;
+
+export default SvgComponent;"
+`;
+
exports[`plugin typescript with "expandProps" add props 1`] = `
"import * as React from \\"react\\";
import { SVGProps } from \\"react\\";
@@ -423,7 +505,28 @@ const ForwardRef = forwardRef(SvgComponent);
export default ForwardRef;"
`;
-exports[`plugin typescript with "titleProp" adds "titleProp" and "titleId" prop 1`] = `
+exports[`plugin typescript with "titleProp" "descProp" and "expandProps" adds "title", "titleId", "desc", "descId" props and expands props 1`] = `
+"import * as React from \\"react\\";
+import { SVGProps } from \\"react\\";
+interface SVGRProps {
+ title?: string;
+ titleId?: string;
+ desc?: string;
+ descId?: string;
+}
+
+const SvgComponent = ({
+ title,
+ titleId,
+ desc,
+ descId,
+ ...props
+}: SVGProps & SVGRProps) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript with "titleProp" adds "title" and "titleId" prop 1`] = `
"import * as React from \\"react\\";
interface SVGRProps {
title?: string;
@@ -438,7 +541,26 @@ const SvgComponent = ({
export default SvgComponent;"
`;
-exports[`plugin typescript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
+exports[`plugin typescript with "titleProp" and "descProp" adds "title", "titleId", "desc", and "descId prop 1`] = `
+"import * as React from \\"react\\";
+interface SVGRProps {
+ title?: string;
+ titleId?: string;
+ desc?: string;
+ descId?: string;
+}
+
+const SvgComponent = ({
+ title,
+ titleId,
+ desc,
+ descId
+}: SVGRProps) => ;
+
+export default SvgComponent;"
+`;
+
+exports[`plugin typescript with "titleProp" and "expandProps" adds "title", "titleId" props and expands props 1`] = `
"import * as React from \\"react\\";
import { SVGProps } from \\"react\\";
interface SVGRProps {
diff --git a/packages/babel-plugin-transform-svg-component/src/index.test.ts b/packages/babel-plugin-transform-svg-component/src/index.test.ts
index cce4d490..7272ff0d 100644
--- a/packages/babel-plugin-transform-svg-component/src/index.test.ts
+++ b/packages/babel-plugin-transform-svg-component/src/index.test.ts
@@ -57,7 +57,7 @@ describe('plugin', () => {
})
describe('with "titleProp"', () => {
- it('adds "titleProp" and "titleId" prop', () => {
+ it('adds "title" and "titleId" prop', () => {
const { code } = testPlugin(language)('', {
titleProp: true,
})
@@ -66,7 +66,7 @@ describe('plugin', () => {
})
describe('with "titleProp" and "expandProps"', () => {
- it('adds "titleProp", "titleId" props and expands props', () => {
+ it('adds "title", "titleId" props and expands props', () => {
const { code } = testPlugin(language)('', {
...defaultOptions,
expandProps: true,
@@ -76,6 +76,48 @@ describe('plugin', () => {
})
})
+ describe('with "descProp"', () => {
+ it('adds "desc" and "descId" prop', () => {
+ const { code } = testPlugin(language)('', {
+ descProp: true,
+ })
+ expect(code).toMatchSnapshot()
+ })
+ })
+
+ describe('with "descProp" and "expandProps"', () => {
+ it('adds "desc", "descId" props and expands props', () => {
+ const { code } = testPlugin(language)('', {
+ ...defaultOptions,
+ expandProps: true,
+ descProp: true,
+ })
+ expect(code).toMatchSnapshot()
+ })
+ })
+
+ describe('with "titleProp" and "descProp"', () => {
+ it('adds "title", "titleId", "desc", and "descId prop', () => {
+ const { code } = testPlugin(language)('', {
+ titleProp: true,
+ descProp: true,
+ })
+ expect(code).toMatchSnapshot()
+ })
+ })
+
+ describe('with "titleProp" "descProp" and "expandProps"', () => {
+ it('adds "title", "titleId", "desc", "descId" props and expands props', () => {
+ const { code } = testPlugin(language)('', {
+ ...defaultOptions,
+ expandProps: true,
+ titleProp: true,
+ descProp: true,
+ })
+ expect(code).toMatchSnapshot()
+ })
+ })
+
describe('with "expandProps"', () => {
it('add props', () => {
const { code } = testPlugin(language)('', {
diff --git a/packages/babel-plugin-transform-svg-component/src/types.ts b/packages/babel-plugin-transform-svg-component/src/types.ts
index 8c1c6651..c171a5bb 100644
--- a/packages/babel-plugin-transform-svg-component/src/types.ts
+++ b/packages/babel-plugin-transform-svg-component/src/types.ts
@@ -35,6 +35,7 @@ export interface JSXRuntimeImport {
export interface Options {
typescript?: boolean
titleProp?: boolean
+ descProp?: boolean
expandProps?: boolean | 'start' | 'end'
ref?: boolean
template?: Template
diff --git a/packages/babel-plugin-transform-svg-component/src/variables.ts b/packages/babel-plugin-transform-svg-component/src/variables.ts
index 844e6383..64a1481b 100644
--- a/packages/babel-plugin-transform-svg-component/src/variables.ts
+++ b/packages/babel-plugin-transform-svg-component/src/variables.ts
@@ -122,21 +122,47 @@ export const getVariables = ({
)
}
- if (opts.titleProp) {
- const prop = t.objectPattern([
- t.objectProperty(
- t.identifier('title'),
- t.identifier('title'),
+ if (opts.titleProp || opts.descProp) {
+ const properties = []
+ const propertySignatures = []
+ const createProperty = (attr: string) => {
+ return t.objectProperty(
+ t.identifier(attr),
+ t.identifier(attr),
false,
true,
- ),
- t.objectProperty(
- t.identifier('titleId'),
- t.identifier('titleId'),
- false,
- true,
- ),
- ])
+ )
+ }
+ const createSignature = (attr: string) => {
+ return tsOptionalPropertySignature(
+ t.identifier(attr),
+ t.tsTypeAnnotation(t.tsStringKeyword()),
+ )
+ }
+
+ if (opts.titleProp) {
+ properties.push(createProperty('title'), createProperty('titleId'))
+
+ if (opts.typescript) {
+ propertySignatures.push(
+ createSignature('title'),
+ createSignature('titleId'),
+ )
+ }
+ }
+
+ if (opts.descProp) {
+ properties.push(createProperty('desc'), createProperty('descId'))
+
+ if (opts.typescript) {
+ propertySignatures.push(
+ createSignature('desc'),
+ createSignature('descId'),
+ )
+ }
+ }
+
+ const prop = t.objectPattern(properties)
props.push(prop)
if (opts.typescript) {
interfaces.push(
@@ -144,16 +170,7 @@ export const getVariables = ({
t.identifier('SVGRProps'),
null,
null,
- t.tSInterfaceBody([
- tsOptionalPropertySignature(
- t.identifier('title'),
- t.tsTypeAnnotation(t.tsStringKeyword()),
- ),
- tsOptionalPropertySignature(
- t.identifier('titleId'),
- t.tsTypeAnnotation(t.tsStringKeyword()),
- ),
- ]),
+ t.tSInterfaceBody(propertySignatures),
),
)
prop.typeAnnotation = t.tsTypeAnnotation(
diff --git a/packages/babel-preset/src/index.test.ts b/packages/babel-preset/src/index.test.ts
index 90073f5a..bf09b86d 100644
--- a/packages/babel-preset/src/index.test.ts
+++ b/packages/babel-preset/src/index.test.ts
@@ -83,6 +83,55 @@ describe('preset', () => {
`)
})
+ it('handles descProp', () => {
+ expect(
+ testPreset('', {
+ descProp: true,
+ }),
+ ).toMatchInlineSnapshot(`
+ "import * as React from \\"react\\";
+
+ const SvgComponent = ({
+ desc,
+ descId
+ }) => ;
+
+ export default SvgComponent;"
+ `)
+ })
+ it('handles descProp and fallback on existing desc', () => {
+ // testing when existing desc has string as chilren
+ expect(
+ testPreset(``, {
+ descProp: true,
+ }),
+ ).toMatchInlineSnapshot(`
+ "import * as React from \\"react\\";
+
+ const SvgComponent = ({
+ desc,
+ descId
+ }) => ;
+
+ export default SvgComponent;"
+ `)
+ // testing when existing desc has JSXExpression as children
+ expect(
+ testPreset(``, {
+ descProp: true,
+ }),
+ ).toMatchInlineSnapshot(`
+ "import * as React from \\"react\\";
+
+ const SvgComponent = ({
+ desc,
+ descId
+ }) => ;
+
+ export default SvgComponent;"
+ `)
+ })
+
it('handles replaceAttrValues', () => {
expect(
testPreset('', {
diff --git a/packages/babel-preset/src/index.ts b/packages/babel-preset/src/index.ts
index 5405a032..aa8faf58 100644
--- a/packages/babel-preset/src/index.ts
+++ b/packages/babel-preset/src/index.ts
@@ -18,6 +18,7 @@ import transformSvgComponent, {
export interface Options extends TransformOptions {
ref?: boolean
titleProp?: boolean
+ descProp?: boolean
expandProps?: boolean | 'start' | 'end'
dimensions?: boolean
icon?: boolean | string | number
@@ -76,6 +77,17 @@ const plugin = (_: ConfigAPI, opts: Options) => {
]
}
+ if (opts.descProp) {
+ toAddAttributes = [
+ ...toAddAttributes,
+ {
+ name: 'aria-describedby',
+ value: 'descId',
+ literal: true,
+ },
+ ]
+ }
+
if (opts.expandProps) {
toAddAttributes = [
...toAddAttributes,
@@ -130,6 +142,10 @@ const plugin = (_: ConfigAPI, opts: Options) => {
plugins.push(svgDynamicTitle)
}
+ if (opts.descProp) {
+ plugins.push([svgDynamicTitle, { tag: 'desc' }])
+ }
+
if (opts.native) {
plugins.push(transformReactNativeSVG)
}
diff --git a/packages/cli/README.md b/packages/cli/README.md
index 6cc42ff9..be7b1bba 100644
--- a/packages/cli/README.md
+++ b/packages/cli/README.md
@@ -35,6 +35,7 @@ Options:
--index-template specify a custom index.js template to use
--no-index disable index file generation
--title-prop create a title element linked with props
+ --desc-prop create a desc element linked with props
--prettier-config Prettier config
--no-prettier disable Prettier
--svgo-config SVGO config
diff --git a/packages/cli/src/__snapshots__/index.test.ts.snap b/packages/cli/src/__snapshots__/index.test.ts.snap
index a2c6a585..fb97ba86 100644
--- a/packages/cli/src/__snapshots__/index.test.ts.snap
+++ b/packages/cli/src/__snapshots__/index.test.ts.snap
@@ -155,6 +155,27 @@ export default SvgFile
"
`;
+exports[`cli should support various args: --desc-prop 1`] = `
+"import * as React from 'react'
+
+const SvgFile = ({ desc, descId, ...props }) => (
+
+)
+
+export default SvgFile
+
+"
+`;
+
exports[`cli should support various args: --expand-props none 1`] = `
"import * as React from 'react'
@@ -484,6 +505,37 @@ export default SvgFile
"
`;
+exports[`cli should support various args: --typescript --ref --desc-prop 1`] = `
+"import * as React from 'react'
+import { SVGProps, Ref, forwardRef } from 'react'
+interface SVGRProps {
+ desc?: string;
+ descId?: string;
+}
+
+const SvgFile = (
+ { desc, descId, ...props }: SVGProps & SVGRProps,
+ ref: Ref,
+) => (
+
+)
+
+const ForwardRef = forwardRef(SvgFile)
+export default ForwardRef
+
+"
+`;
+
exports[`cli should support various args: --typescript --ref --title-prop 1`] = `
"import * as React from 'react'
import { SVGProps, Ref, forwardRef } from 'react'
diff --git a/packages/cli/src/index.test.ts b/packages/cli/src/index.test.ts
index 95750846..5de680fd 100644
--- a/packages/cli/src/index.test.ts
+++ b/packages/cli/src/index.test.ts
@@ -131,9 +131,11 @@ describe('cli', () => {
['--no-svgo'],
['--no-prettier'],
['--title-prop'],
+ ['--desc-prop'],
['--typescript'],
['--typescript --ref'],
['--typescript --ref --title-prop'],
+ ['--typescript --ref --desc-prop'],
])(
'should support various args',
async (args) => {
diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts
index bee4a0b9..bbdb4647 100644
--- a/packages/cli/src/index.ts
+++ b/packages/cli/src/index.ts
@@ -146,6 +146,7 @@ program
)
.option('--no-index', 'disable index file generation')
.option('--title-prop', 'create a title element linked with props')
+ .option('--desc-prop', 'create a desc element linked with props')
.option(
'--prettier-config ',
'Prettier config',
diff --git a/packages/core/src/__snapshots__/config.test.ts.snap b/packages/core/src/__snapshots__/config.test.ts.snap
index 4f146232..d5886aa2 100644
--- a/packages/core/src/__snapshots__/config.test.ts.snap
+++ b/packages/core/src/__snapshots__/config.test.ts.snap
@@ -2,6 +2,7 @@
exports[`svgo async #loadConfig [async] should load config using filePath 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
@@ -32,6 +33,7 @@ Object {
exports[`svgo async #loadConfig [async] should not load config with "runtimeConfig: false 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
@@ -63,6 +65,7 @@ Object {
exports[`svgo async #loadConfig [async] should use default config without state.filePath 1`] = `
Object {
+ "descProp": false,
"dimensions": false,
"expandProps": "end",
"exportType": "default",
@@ -87,6 +90,7 @@ Object {
exports[`svgo async #loadConfig [async] should work with custom config path 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
@@ -117,6 +121,7 @@ Object {
exports[`svgo sync #loadConfig [sync] should load config using filePath 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
@@ -147,6 +152,7 @@ Object {
exports[`svgo sync #loadConfig [sync] should not load config with "runtimeConfig: false 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
@@ -178,6 +184,7 @@ Object {
exports[`svgo sync #loadConfig [sync] should use default config without state.filePath 1`] = `
Object {
+ "descProp": false,
"dimensions": false,
"expandProps": "end",
"exportType": "default",
@@ -202,6 +209,7 @@ Object {
exports[`svgo sync #loadConfig [sync] should work with custom config path 1`] = `
Object {
+ "descProp": false,
"dimensions": true,
"expandProps": "end",
"exportType": "default",
diff --git a/packages/core/src/__snapshots__/transform.test.ts.snap b/packages/core/src/__snapshots__/transform.test.ts.snap
index c85ba403..1c7355a3 100644
--- a/packages/core/src/__snapshots__/transform.test.ts.snap
+++ b/packages/core/src/__snapshots__/transform.test.ts.snap
@@ -1,5 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`convert config accepts options {"descProp":true} 1`] = `
+"import * as React from 'react'
+
+const SvgComponent = ({ desc, descId, ...props }) => (
+
+)
+
+export default SvgComponent
+"
+`;
+
exports[`convert config accepts options {"dimensions":false} 1`] = `
"import * as React from 'react'
@@ -489,6 +517,28 @@ export default noop
"
`;
+exports[`convert config descProp: without desc added 1`] = `
+"import * as React from 'react'
+
+const SvgComponent = ({ desc, descId, ...props }) => (
+
+)
+
+export default SvgComponent
+"
+`;
+
exports[`convert config titleProp: without title added 1`] = `
"import * as React from 'react'
diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts
index 028f9e68..9468ca8b 100644
--- a/packages/core/src/config.ts
+++ b/packages/core/src/config.ts
@@ -9,6 +9,7 @@ import type { State } from './state'
export interface Config {
ref?: boolean
titleProp?: boolean
+ descProp?: boolean
expandProps?: boolean | 'start' | 'end'
dimensions?: boolean
icon?: boolean | string | number
@@ -59,6 +60,7 @@ export const DEFAULT_CONFIG: Config = {
template: undefined,
index: false,
titleProp: false,
+ descProp: false,
runtimeConfig: true,
namedExport: 'ReactComponent',
exportType: 'default',
diff --git a/packages/core/src/transform.test.ts b/packages/core/src/transform.test.ts
index bfea9b33..387a2029 100644
--- a/packages/core/src/transform.test.ts
+++ b/packages/core/src/transform.test.ts
@@ -317,6 +317,7 @@ describe('convert', () => {
tpl`const noop = () => null; export default noop;`,
},
{ titleProp: true },
+ { descProp: true },
{ memo: true },
{
namedExport: 'Component',
@@ -340,5 +341,16 @@ describe('convert', () => {
await convertWithAllPlugins(svg, { titleProp: true }),
).toMatchSnapshot()
})
+
+ it('descProp: without desc added', async () => {
+ const svg = `
+
+`
+ expect(
+ await convertWithAllPlugins(svg, { descProp: true }),
+ ).toMatchSnapshot()
+ })
})
})
diff --git a/packages/plugin-jsx/src/index.ts b/packages/plugin-jsx/src/index.ts
index 57e9c844..9d198df0 100644
--- a/packages/plugin-jsx/src/index.ts
+++ b/packages/plugin-jsx/src/index.ts
@@ -38,6 +38,7 @@ const jsxPlugin: Plugin = (code, config, state) => {
const svgPresetOptions: SvgrPresetOptions = {
ref: config.ref,
titleProp: config.titleProp,
+ descProp: config.descProp,
expandProps: config.expandProps,
dimensions: config.dimensions,
icon: config.icon,
diff --git a/website/pages/docs/options.mdx b/website/pages/docs/options.mdx
index 825e1a20..f59cb5ab 100644
--- a/website/pages/docs/options.mdx
+++ b/website/pages/docs/options.mdx
@@ -178,6 +178,14 @@ Add title tag via title property. If titleProp is set to true and no title is pr
| ------- | -------------- | -------------------- |
| `false` | `--title-prop` | `titleProp: boolean` |
+## Description
+
+Add desc tag via desc property. If descProp is set to true and no description is provided (`desc={undefined}`) at render time, this will fallback to an existing desc element in the svg if exists.
+
+| Default | CLI Override | API Override |
+| ------- | ------------- | ------------------- |
+| `false` | `--desc-prop` | `descProp: boolean` |
+
## Template
Specify a template file (CLI) or a template function (API) to use. For an example of template, see [the default one](https://github.com/gregberge/svgr/blob/main/packages/babel-plugin-transform-svg-component/src/defaultTemplate.ts).
diff --git a/website/src/components/playground/config/settings.js b/website/src/components/playground/config/settings.js
index 75f3f39a..5192fbfe 100644
--- a/website/src/components/playground/config/settings.js
+++ b/website/src/components/playground/config/settings.js
@@ -67,6 +67,13 @@ export const settings = [
group: 'global',
default: false,
},
+ {
+ label: 'Desc prop',
+ name: 'descProp',
+ type: 'boolean',
+ group: 'global',
+ default: false,
+ },
{
label: 'Expand props',
name: 'expandProps',