Skip to content

Commit a6de2da

Browse files
authored
fix: wrap svg component directly with memo/forwardRef (#440) (#441)
1 parent f30bb85 commit a6de2da

File tree

5 files changed

+117
-123
lines changed

5 files changed

+117
-123
lines changed

packages/babel-plugin-transform-svg-component/src/__snapshots__/index.test.js.snap

+45-36
Original file line numberDiff line numberDiff line change
@@ -90,27 +90,22 @@ export default SvgComponent;"
9090
exports[`plugin javascript with "ref" and "expandProps" option expands props 1`] = `
9191
"import * as React from \\"react\\";
9292
93-
function SvgComponent({
94-
svgRef,
95-
...props
96-
}) {
93+
function SvgComponent(props, svgRef) {
9794
return <svg><g /></svg>;
9895
}
9996
100-
const ForwardRef = React.forwardRef((props, ref) => <SvgComponent svgRef={ref} {...props} />);
97+
const ForwardRef = React.forwardRef(SvgComponent);
10198
export default ForwardRef;"
10299
`;
103100
104101
exports[`plugin javascript with "ref" option adds ForwardRef component 1`] = `
105102
"import * as React from \\"react\\";
106103
107-
function SvgComponent({
108-
svgRef
109-
}) {
104+
function SvgComponent(props, svgRef) {
110105
return <svg><g /></svg>;
111106
}
112107
113-
const ForwardRef = React.forwardRef((props, ref) => <SvgComponent svgRef={ref} {...props} />);
108+
const ForwardRef = React.forwardRef(SvgComponent);
114109
export default ForwardRef;"
115110
`;
116111
@@ -127,18 +122,30 @@ function SvgComponent({
127122
export default SvgComponent;"
128123
`;
129124
130-
exports[`plugin javascript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
125+
exports[`plugin javascript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
131126
"import * as React from \\"react\\";
132127
133128
function SvgComponent({
134-
svgRef
129+
title,
130+
titleId,
131+
...props
135132
}) {
136133
return <svg><g /></svg>;
137134
}
138135
139-
const MemoSvgComponent = React.memo(SvgComponent);
140-
const ForwardRef = React.forwardRef((props, ref) => <MemoSvgComponent svgRef={ref} {...props} />);
141-
export default ForwardRef;"
136+
export default SvgComponent;"
137+
`;
138+
139+
exports[`plugin javascript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
140+
"import * as React from \\"react\\";
141+
142+
function SvgComponent(props, svgRef) {
143+
return <svg><g /></svg>;
144+
}
145+
146+
const ForwardRef = React.forwardRef(SvgComponent);
147+
const MemoForwardRef = React.memo(ForwardRef);
148+
export default MemoForwardRef;"
142149
`;
143150
144151
exports[`plugin typescript custom templates support basic template 1`] = `
@@ -214,34 +221,23 @@ export default SvgComponent;"
214221
215222
exports[`plugin typescript with "ref" and "expandProps" option expands props 1`] = `
216223
"import * as React from \\"react\\";
217-
interface SVGRProps {
218-
svgRef?: React.Ref<SVGSVGElement>
219-
}
220224
221-
function SvgComponent({
222-
svgRef,
223-
...props
224-
}: React.SVGProps<SVGSVGElement> & SVGRProps) {
225+
function SvgComponent(props: React.SVGProps<SVGSVGElement>, svgRef?: React.Ref<SVGSVGElement>) {
225226
return <svg><g /></svg>;
226227
}
227228
228-
const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <SvgComponent svgRef={ref} {...props} />);
229+
const ForwardRef = React.forwardRef(SvgComponent);
229230
export default ForwardRef;"
230231
`;
231232
232233
exports[`plugin typescript with "ref" option adds ForwardRef component 1`] = `
233234
"import * as React from \\"react\\";
234-
interface SVGRProps {
235-
svgRef?: React.Ref<SVGSVGElement>
236-
}
237235
238-
function SvgComponent({
239-
svgRef
240-
}: SVGRProps) {
236+
function SvgComponent(props: {}, svgRef?: React.Ref<SVGSVGElement>) {
241237
return <svg><g /></svg>;
242238
}
243239
244-
const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <SvgComponent svgRef={ref} {...props} />);
240+
const ForwardRef = React.forwardRef(SvgComponent);
245241
export default ForwardRef;"
246242
`;
247243
@@ -262,19 +258,32 @@ function SvgComponent({
262258
export default SvgComponent;"
263259
`;
264260
265-
exports[`plugin typescript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
261+
exports[`plugin typescript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
266262
"import * as React from \\"react\\";
267263
interface SVGRProps {
268-
svgRef?: React.Ref<SVGSVGElement>
264+
title?: string,
265+
titleId?: string,
269266
}
270267
271268
function SvgComponent({
272-
svgRef
273-
}: SVGRProps) {
269+
title,
270+
titleId,
271+
...props
272+
}: React.SVGProps<SVGSVGElement> & SVGRProps) {
274273
return <svg><g /></svg>;
275274
}
276275
277-
const MemoSvgComponent = React.memo(SvgComponent);
278-
const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <MemoSvgComponent svgRef={ref} {...props} />);
279-
export default ForwardRef;"
276+
export default SvgComponent;"
277+
`;
278+
279+
exports[`plugin typescript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
280+
"import * as React from \\"react\\";
281+
282+
function SvgComponent(props: {}, svgRef?: React.Ref<SVGSVGElement>) {
283+
return <svg><g /></svg>;
284+
}
285+
286+
const ForwardRef = React.forwardRef(SvgComponent);
287+
const MemoForwardRef = React.memo(ForwardRef);
288+
export default MemoForwardRef;"
280289
`;

packages/babel-plugin-transform-svg-component/src/index.test.js

+11
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,17 @@ describe('plugin', () => {
6262
})
6363
})
6464

65+
describe('with "titleProp" and "expandProps"', () => {
66+
it('adds "titleProp", "titleId" props and expands props', () => {
67+
const { code } = testPlugin(language)('<svg><g /></svg>', {
68+
state: { componentName: 'SvgComponent' },
69+
expandProps: true,
70+
titleProp: true,
71+
})
72+
expect(code).toMatchSnapshot()
73+
})
74+
})
75+
6576
describe('with "expandProps"', () => {
6677
it('add props', () => {
6778
const { code } = testPlugin(language)('<svg><g /></svg>', {

packages/babel-plugin-transform-svg-component/src/util.js

+43-51
Original file line numberDiff line numberDiff line change
@@ -72,17 +72,6 @@ function getSvgPropsTypeAnnotation(t) {
7272
export const getProps = ({ types: t }, opts) => {
7373
const props = []
7474

75-
if (opts.ref) {
76-
props.push(
77-
t.objectProperty(
78-
t.identifier('svgRef'),
79-
t.identifier('svgRef'),
80-
false,
81-
true,
82-
),
83-
)
84-
}
85-
8675
if (opts.titleProp) {
8776
props.push(
8877
t.objectProperty(
@@ -103,53 +92,62 @@ export const getProps = ({ types: t }, opts) => {
10392
)
10493
}
10594

106-
if (opts.expandProps) {
95+
if (opts.expandProps && props.length > 0) {
10796
props.push(t.restElement(t.identifier('props')))
10897
}
10998

110-
if (props.length === 0) {
111-
return null
112-
}
99+
const propsArgument =
100+
props.length > 0 ? t.objectPattern(props) : t.identifier('props')
101+
let propsTypeAnnotation
102+
if (props.length > 0) {
103+
propsTypeAnnotation = genericTypeAnnotation(t.identifier('SVGRProps'))
113104

114-
if (props.length === 1 && opts.expandProps) {
115-
return addTypeAnotation(
116-
t.identifier('props'),
117-
typeAnnotation(getSvgPropsTypeAnnotation(t)),
118-
opts,
119-
)
105+
if (opts.expandProps) {
106+
propsTypeAnnotation = intersectionTypeAnnotation([
107+
getSvgPropsTypeAnnotation(t),
108+
propsTypeAnnotation,
109+
])
110+
}
111+
} else {
112+
propsTypeAnnotation = opts.expandProps
113+
? getSvgPropsTypeAnnotation(t)
114+
: t.objectPattern([])
120115
}
121116

122-
return addTypeAnotation(
123-
t.objectPattern(props),
124-
typeAnnotation(
125-
opts.expandProps
126-
? intersectionTypeAnnotation([
127-
getSvgPropsTypeAnnotation(t),
128-
genericTypeAnnotation(t.identifier('SVGRProps')),
129-
])
130-
: genericTypeAnnotation(t.identifier('SVGRProps')),
131-
),
117+
const typedPropsArgument = addTypeAnotation(
118+
propsArgument,
119+
typeAnnotation(propsTypeAnnotation),
132120
opts,
133121
)
134-
}
135122

136-
export const getInterface = ({ types: t }, opts) => {
137-
if (!opts.typescript) return null
138-
const properties = []
123+
const args = []
124+
if (opts.expandProps || props.length > 0 || opts.ref)
125+
args.push(typedPropsArgument)
126+
139127
if (opts.ref) {
140-
properties.push(
141-
objectTypeProperty(
142-
t.identifier('svgRef'),
128+
const refArgument = t.identifier(opts.typescript ? 'svgRef?' : 'svgRef')
129+
const typedRefArgument = addTypeAnotation(
130+
refArgument,
131+
typeAnnotation(
143132
genericTypeAnnotation(
144133
qualifiedTypeIdentifier(t.identifier('React'), t.identifier('Ref')),
145134
typeParameters([
146135
genericTypeAnnotation(t.identifier('SVGSVGElement')),
147136
]),
148137
),
149-
true,
150138
),
139+
opts,
151140
)
141+
142+
args.push(typedRefArgument)
152143
}
144+
145+
return args
146+
}
147+
148+
export const getInterface = ({ types: t }, opts) => {
149+
if (!opts.typescript) return null
150+
const properties = []
153151
if (opts.titleProp) {
154152
properties.push(
155153
objectTypeProperty(t.identifier('title'), t.identifier('string'), true),
@@ -198,21 +196,15 @@ export const getExport = ({ template }, opts) => {
198196
plugins.push('typescript')
199197
}
200198

201-
if (opts.memo) {
202-
const nextExportName = `Memo${exportName}`
203-
result += `const ${nextExportName} = React.memo(${exportName})\n\n`
204-
exportName = nextExportName
205-
}
206-
207199
if (opts.ref) {
208200
const nextExportName = `ForwardRef`
209-
let refType = ''
210-
211-
if (opts.typescript) {
212-
refType = ': React.Ref<SVGSVGElement>'
213-
}
201+
result += `const ${nextExportName} = React.forwardRef(${exportName})\n\n`
202+
exportName = nextExportName
203+
}
214204

215-
result += `const ${nextExportName} = React.forwardRef((props, ref${refType}) => <${exportName} svgRef={ref} {...props} />)\n\n`
205+
if (opts.memo) {
206+
const nextExportName = `Memo${exportName}`
207+
result += `const ${nextExportName} = React.memo(${exportName})\n\n`
216208
exportName = nextExportName
217209
}
218210

0 commit comments

Comments
 (0)