diff --git a/README.md b/README.md index c1ed752c..368f4c66 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Options: --template specify a custom template to use --no-expand-props disable props expanding (default: true) --ids keep ids within the svg + --ref add svgRef prop to svg --icon use "1em" as width and height --no-view-box remove viewBox (default: true) --native add react-native support with react-native-svg @@ -167,10 +168,23 @@ $ svgr --template path/to/template.js my-icon.svg **Example of template:** ```js -module.exports = (opts = {}) => (code, state) => `import React from 'react' -const ${state.componentName} = (${opts.expandProps ? 'props' : ''}) => ${code} -export default ${state.componentName} -` +export default (opts = {}) => { + let props = '' + + if (opts.expandProps && opts.ref) { + props = '{svgRef, ...props}' + } else if (opts.expandProps) { + props = 'props' + } else if (opts.ref) { + props = '{svgRef}' + } + + return (code, state) => `import React from 'react' + +const ${state.componentName} = (${props}) => ${code} + +export default ${state.componentName}` +} ``` ## Node API usage @@ -319,6 +333,14 @@ using CSS or third party library (eg: | ------- | ------------ | ------------- | | `false` | `--ids` | `ids: ` | +### Ref + +Setting this to `true` will allow you to hook into the ref of the svg components that are created by exposing a `svgRef` prop + +| Default | CLI Override | API Override | +| ------- | ------------ | ------------- | +| `false` | `--ref` | `ref: ` | + ### Replace attribute value Replace an attribute value by an other. The main usage of this option is to diff --git a/src/cli/__snapshots__/index.test.js.snap b/src/cli/__snapshots__/index.test.js.snap index 0f5dd9fb..c2d6397b 100644 --- a/src/cli/__snapshots__/index.test.js.snap +++ b/src/cli/__snapshots__/index.test.js.snap @@ -187,6 +187,36 @@ export default One; " `; +exports[`cli --ref --no-expand-props 1`] = ` +"import React from \\"react\\"; + +const One = ({ svgRef }) => ( + + Rectangle 5 + + +); + +export default One; + +" +`; + +exports[`cli --ref 1`] = ` +"import React from \\"react\\"; + +const One = ({ svgRef, ...props }) => ( + + Rectangle 5 + + +); + +export default One; + +" +`; + exports[`cli --replace-attr-value 1`] = ` "import React from \\"react\\"; diff --git a/src/cli/index.js b/src/cli/index.js index fc212ea1..7e5ff1ea 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -24,6 +24,7 @@ program .option('--template ', 'specify a custom template to use') .option('--no-expand-props', 'disable props expanding') .option('--ids', 'keep ids within the svg') + .option('--ref', 'add svgRef prop to svg') .option('--icon', 'use "1em" as width and height') .option('--no-view-box', 'remove viewBox') .option('--native', 'add react-native support with react-native-svg') diff --git a/src/cli/index.test.js b/src/cli/index.test.js index 418ed647..7fc3a544 100644 --- a/src/cli/index.test.js +++ b/src/cli/index.test.js @@ -13,16 +13,12 @@ describe('cli', () => { }) it('--no-svgo', async () => { - const [stdout] = await exec( - 'bin/svgr --no-svgo __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --no-svgo __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) it('--no-prettier', async () => { - const [stdout] = await exec( - 'bin/svgr --no-prettier __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --no-prettier __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) @@ -34,8 +30,18 @@ describe('cli', () => { }) it('--icon', async () => { + const [stdout] = await exec('bin/svgr --icon __fixtures__/one.svg') + expect(stdout).toMatchSnapshot() + }) + + it('--ref', async () => { + const [stdout] = await exec('bin/svgr --ref __fixtures__/one.svg') + expect(stdout).toMatchSnapshot() + }) + + it('--ref --no-expand-props', async () => { const [stdout] = await exec( - 'bin/svgr --icon __fixtures__/one.svg', + 'bin/svgr --ref --no-expand-props __fixtures__/one.svg', ) expect(stdout).toMatchSnapshot() }) @@ -46,9 +52,7 @@ describe('cli', () => { }) it('--no-view-box', async () => { - const [stdout] = await exec( - 'bin/svgr --no-view-box __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --no-view-box __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) @@ -60,23 +64,17 @@ describe('cli', () => { }) it('--precision', async () => { - const [stdout] = await exec( - 'bin/svgr --precision 1 __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --precision 1 __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) it('--no-title', async () => { - const [stdout] = await exec( - 'bin/svgr --no-title __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --no-title __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) it('--no-semi', async () => { - const [stdout] = await exec( - 'bin/svgr --no-semi __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --no-semi __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) @@ -88,9 +86,7 @@ describe('cli', () => { }) it('--native', async () => { - const [stdout] = await exec( - 'bin/svgr --native __fixtures__/one.svg', - ) + const [stdout] = await exec('bin/svgr --native __fixtures__/one.svg') expect(stdout).toMatchSnapshot() }) diff --git a/src/configToOptions.js b/src/configToOptions.js index d021b924..8023b81c 100644 --- a/src/configToOptions.js +++ b/src/configToOptions.js @@ -4,12 +4,14 @@ import wrapIntoNativeComponent from './transforms/wrapIntoNativeComponent' import stripAttribute from './h2x/stripAttribute' import emSize from './h2x/emSize' import expandProps from './h2x/expandProps' +import svgRef from './h2x/svgRef' import replaceAttrValue from './h2x/replaceAttrValue' import removeComments from './h2x/removeComments' import removeStyle from './h2x/removeStyle' import toReactNative from './h2x/toReactNative' const defaultConfig = { + ref: false, svgo: true, prettier: true, native: false, @@ -41,6 +43,7 @@ function configToOptions(config = {}) { config.replaceAttrValues.forEach(([oldValue, newValue]) => { plugins.push(replaceAttrValue(oldValue, newValue)) }) + if (config.ref) plugins.push(svgRef) if (config.expandProps) plugins.push(expandProps) if (config.native) plugins.push(toReactNative) diff --git a/src/h2x/svgRef.js b/src/h2x/svgRef.js new file mode 100644 index 00000000..4c4c69cc --- /dev/null +++ b/src/h2x/svgRef.js @@ -0,0 +1,23 @@ +import { JSXAttribute } from 'h2x-plugin-jsx' + +const svgRef = () => ({ + visitor: { + JSXElement: { + enter(path) { + if ( + path.node.name === 'svg' && + !path.node.attributes.some(attr => attr && attr.name === 'ref') + ) { + const props = new JSXAttribute() + props.name = 'ref' + props.value = 'svgRef' + props.litteral = true + path.node.attributes.push(props) + path.replace(path.node) + } + }, + }, + }, +}) + +export default svgRef diff --git a/src/index.js b/src/index.js index 8d171fdb..e0275bc3 100644 --- a/src/index.js +++ b/src/index.js @@ -9,6 +9,7 @@ import { pascalCase } from './transforms/rename' import stripAttribute from './h2x/stripAttribute' import emSize from './h2x/emSize' import expandProps from './h2x/expandProps' +import svgRef from './h2x/svgRef' import replaceAttrValue from './h2x/replaceAttrValue' import removeComments from './h2x/removeComments' import removeStyle from './h2x/removeStyle' @@ -20,6 +21,7 @@ export { emSize, expandProps, replaceAttrValue, + svgRef, wrapIntoComponent, removeComments, removeStyle, diff --git a/src/transforms/wrapIntoComponent.js b/src/transforms/wrapIntoComponent.js index 02fe9f39..329e34a2 100644 --- a/src/transforms/wrapIntoComponent.js +++ b/src/transforms/wrapIntoComponent.js @@ -1,5 +1,17 @@ -export default (opts = {}) => (code, state) => `import React from 'react' +export default (opts = {}) => { + let props = '' -const ${state.componentName} = (${opts.expandProps ? 'props' : ''}) => ${code} + if (opts.expandProps && opts.ref) { + props = '{svgRef, ...props}' + } else if (opts.expandProps) { + props = 'props' + } else if (opts.ref) { + props = '{svgRef}' + } + + return (code, state) => `import React from 'react' + +const ${state.componentName} = (${props}) => ${code} export default ${state.componentName}` +} diff --git a/src/transforms/wrapIntoNativeComponent.js b/src/transforms/wrapIntoNativeComponent.js index de792b0b..d593ed0f 100644 --- a/src/transforms/wrapIntoNativeComponent.js +++ b/src/transforms/wrapIntoNativeComponent.js @@ -16,6 +16,16 @@ export default (opts = {}) => (code, state) => { unsupportedComponents = new Set(), } = state + let props = '' + + if (opts.expandProps && opts.ref) { + props = '{svgRef, ...props}' + } else if (opts.expandProps) { + props = 'props' + } else if (opts.ref) { + props = '{svgRef}' + } + return `import React from 'react' import Svg, { ${componentsToList( reactNativeSvgReplacedComponents, @@ -23,7 +33,7 @@ export default (opts = {}) => (code, state) => { ${logUnsupportedComponents(unsupportedComponents)} - const ${state.componentName} = (${opts.expandProps ? 'props' : ''}) => ${code} + const ${state.componentName} = (${props}) => ${code} export default ${state.componentName}` }