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( `"{title ? {title} : null};"`, @@ -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(`Hello`)).toMatchInlineSnapshot( + expect( + testPlugin(`Hello`), + ).toMatchInlineSnapshot( `"{title === undefined ? Hello : title ? {title} : null};"`, ) // 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( `"{title ? {title} : null};"`, ) }) it('should support self closing title', () => { - expect(testPlugin('')).toMatchInlineSnapshot( + expect( + testPlugin(''), + ).toMatchInlineSnapshot( `"{title ? {title} : null};"`, ) }) it('should work if an attribute is already present', () => { - expect(testPlugin('')).toMatchInlineSnapshot( + expect( + testPlugin(''), + ).toMatchInlineSnapshot( `"{title ? {title} : null};"`, ) }) }) + +describe('desc plugin', () => { + it('should add desc attribute if not present', () => { + expect(testPlugin('', { tag: 'desc' })).toMatchInlineSnapshot( + `"{desc ? {desc} : null};"`, + ) + }) + + it('should add desc element and fallback to existing desc', () => { + // testing when the existing desc contains a simple string + expect( + testPlugin(`Hello`, { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc === undefined ? Hello : desc ? {desc} : null};"`, + ) + // testing when the existing desc contains an JSXExpression + expect( + testPlugin(`{"Hello"}`, { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc === undefined ? {\\"Hello\\"} : desc ? {desc} : null};"`, + ) + }) + it('should preserve any existing desc attributes', () => { + // testing when the existing desc contains a simple string + expect( + testPlugin(`Hello`, { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc === undefined ? Hello : desc ? {desc} : null};"`, + ) + }) + it('should support empty desc', () => { + expect( + testPlugin('', { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc ? {desc} : null};"`, + ) + }) + it('should support self closing desc', () => { + expect( + testPlugin('', { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc ? {desc} : null};"`, + ) + }) + + it('should work if an attribute is already present', () => { + expect( + testPlugin('', { tag: 'desc' }), + ).toMatchInlineSnapshot( + `"{desc ? {desc} : null};"`, + ) + }) +}) 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 + }) => {desc ? {desc} : null}; + + export default SvgComponent;" + `) + }) + it('handles descProp and fallback on existing desc', () => { + // testing when existing desc has string as chilren + expect( + testPreset(`Hello`, { + descProp: true, + }), + ).toMatchInlineSnapshot(` + "import * as React from \\"react\\"; + + const SvgComponent = ({ + desc, + descId + }) => {desc === undefined ? Hello : desc ? {desc} : null}; + + export default SvgComponent;" + `) + // testing when existing desc has JSXExpression as children + expect( + testPreset(`{"Hello"}`, { + descProp: true, + }), + ).toMatchInlineSnapshot(` + "import * as React from \\"react\\"; + + const SvgComponent = ({ + desc, + descId + }) => {desc === undefined ? {\\"Hello\\"} : desc ? {desc} : null}; + + 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 }) => ( + + {desc ? {desc} : null} + + +) + +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, +) => ( + + {desc ? {desc} : null} + + +) + +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 }) => ( + + {desc ? {desc} : null} + + + + +) + +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 }) => ( + + {desc ? {desc} : null} + + +) + +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',