Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: wrap svg component directly with memo/forwardRef (#440) #441

Merged
merged 4 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,27 +90,22 @@ export default SvgComponent;"
exports[`plugin javascript with "ref" and "expandProps" option expands props 1`] = `
"import * as React from \\"react\\";

function SvgComponent({
svgRef,
...props
}) {
function SvgComponent(props, svgRef) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef((props, ref) => <SvgComponent svgRef={ref} {...props} />);
const ForwardRef = React.forwardRef(SvgComponent);
export default ForwardRef;"
`;

exports[`plugin javascript with "ref" option adds ForwardRef component 1`] = `
"import * as React from \\"react\\";

function SvgComponent({
svgRef
}) {
function SvgComponent(props, svgRef) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef((props, ref) => <SvgComponent svgRef={ref} {...props} />);
const ForwardRef = React.forwardRef(SvgComponent);
export default ForwardRef;"
`;

Expand All @@ -127,18 +122,30 @@ function SvgComponent({
export default SvgComponent;"
`;

exports[`plugin javascript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
exports[`plugin javascript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
"import * as React from \\"react\\";

function SvgComponent({
svgRef
title,
titleId,
...props
}) {
return <svg><g /></svg>;
}

const MemoSvgComponent = React.memo(SvgComponent);
const ForwardRef = React.forwardRef((props, ref) => <MemoSvgComponent svgRef={ref} {...props} />);
export default ForwardRef;"
export default SvgComponent;"
`;

exports[`plugin javascript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
"import * as React from \\"react\\";

function SvgComponent(props, svgRef) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef(SvgComponent);
const MemoForwardRef = React.memo(ForwardRef);
export default MemoForwardRef;"
`;

exports[`plugin typescript custom templates support basic template 1`] = `
Expand Down Expand Up @@ -214,34 +221,23 @@ export default SvgComponent;"

exports[`plugin typescript with "ref" and "expandProps" option expands props 1`] = `
"import * as React from \\"react\\";
interface SVGRProps {
svgRef?: React.Ref<SVGSVGElement>
}

function SvgComponent({
svgRef,
...props
}: React.SVGProps<SVGSVGElement> & SVGRProps) {
function SvgComponent(props: React.SVGProps<SVGSVGElement>, svgRef?: React.Ref<SVGSVGElement>) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <SvgComponent svgRef={ref} {...props} />);
const ForwardRef = React.forwardRef(SvgComponent);
export default ForwardRef;"
`;

exports[`plugin typescript with "ref" option adds ForwardRef component 1`] = `
"import * as React from \\"react\\";
interface SVGRProps {
svgRef?: React.Ref<SVGSVGElement>
}

function SvgComponent({
svgRef
}: SVGRProps) {
function SvgComponent(props: {}, svgRef?: React.Ref<SVGSVGElement>) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <SvgComponent svgRef={ref} {...props} />);
const ForwardRef = React.forwardRef(SvgComponent);
export default ForwardRef;"
`;

Expand All @@ -262,19 +258,32 @@ function SvgComponent({
export default SvgComponent;"
`;

exports[`plugin typescript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
exports[`plugin typescript with "titleProp" and "expandProps" adds "titleProp", "titleId" props and expands props 1`] = `
"import * as React from \\"react\\";
interface SVGRProps {
svgRef?: React.Ref<SVGSVGElement>
title?: string,
titleId?: string,
}

function SvgComponent({
svgRef
}: SVGRProps) {
title,
titleId,
...props
}: React.SVGProps<SVGSVGElement> & SVGRProps) {
return <svg><g /></svg>;
}

const MemoSvgComponent = React.memo(SvgComponent);
const ForwardRef = React.forwardRef((props, ref: React.Ref<SVGSVGElement>) => <MemoSvgComponent svgRef={ref} {...props} />);
export default ForwardRef;"
export default SvgComponent;"
`;

exports[`plugin typescript with both "memo" and "ref" option wrap component in "React.memo" and "React.forwardRef" 1`] = `
"import * as React from \\"react\\";

function SvgComponent(props: {}, svgRef?: React.Ref<SVGSVGElement>) {
return <svg><g /></svg>;
}

const ForwardRef = React.forwardRef(SvgComponent);
const MemoForwardRef = React.memo(ForwardRef);
export default MemoForwardRef;"
`;
11 changes: 11 additions & 0 deletions packages/babel-plugin-transform-svg-component/src/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,17 @@ describe('plugin', () => {
})
})

describe('with "titleProp" and "expandProps"', () => {
it('adds "titleProp", "titleId" props and expands props', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
state: { componentName: 'SvgComponent' },
expandProps: true,
titleProp: true,
})
expect(code).toMatchSnapshot()
})
})

describe('with "expandProps"', () => {
it('add props', () => {
const { code } = testPlugin(language)('<svg><g /></svg>', {
Expand Down
94 changes: 43 additions & 51 deletions packages/babel-plugin-transform-svg-component/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,6 @@ function getSvgPropsTypeAnnotation(t) {
export const getProps = ({ types: t }, opts) => {
const props = []

if (opts.ref) {
props.push(
t.objectProperty(
t.identifier('svgRef'),
t.identifier('svgRef'),
false,
true,
),
)
}

if (opts.titleProp) {
props.push(
t.objectProperty(
Expand All @@ -103,53 +92,62 @@ export const getProps = ({ types: t }, opts) => {
)
}

if (opts.expandProps) {
if (opts.expandProps && props.length > 0) {
props.push(t.restElement(t.identifier('props')))
}

if (props.length === 0) {
return null
}
const propsArgument =
props.length > 0 ? t.objectPattern(props) : t.identifier('props')
let propsTypeAnnotation
if (props.length > 0) {
propsTypeAnnotation = genericTypeAnnotation(t.identifier('SVGRProps'))

if (props.length === 1 && opts.expandProps) {
return addTypeAnotation(
t.identifier('props'),
typeAnnotation(getSvgPropsTypeAnnotation(t)),
opts,
)
if (opts.expandProps) {
propsTypeAnnotation = intersectionTypeAnnotation([
getSvgPropsTypeAnnotation(t),
propsTypeAnnotation,
])
}
} else {
propsTypeAnnotation = opts.expandProps
? getSvgPropsTypeAnnotation(t)
: t.objectPattern([])
}

return addTypeAnotation(
t.objectPattern(props),
typeAnnotation(
opts.expandProps
? intersectionTypeAnnotation([
getSvgPropsTypeAnnotation(t),
genericTypeAnnotation(t.identifier('SVGRProps')),
])
: genericTypeAnnotation(t.identifier('SVGRProps')),
),
const typedPropsArgument = addTypeAnotation(
propsArgument,
typeAnnotation(propsTypeAnnotation),
opts,
)
}

export const getInterface = ({ types: t }, opts) => {
if (!opts.typescript) return null
const properties = []
const args = []
if (opts.expandProps || props.length > 0 || opts.ref)
args.push(typedPropsArgument)

if (opts.ref) {
properties.push(
objectTypeProperty(
t.identifier('svgRef'),
const refArgument = t.identifier(opts.typescript ? 'svgRef?' : 'svgRef')
const typedRefArgument = addTypeAnotation(
refArgument,
typeAnnotation(
genericTypeAnnotation(
qualifiedTypeIdentifier(t.identifier('React'), t.identifier('Ref')),
typeParameters([
genericTypeAnnotation(t.identifier('SVGSVGElement')),
]),
),
true,
),
opts,
)

args.push(typedRefArgument)
}

return args
}

export const getInterface = ({ types: t }, opts) => {
if (!opts.typescript) return null
const properties = []
if (opts.titleProp) {
properties.push(
objectTypeProperty(t.identifier('title'), t.identifier('string'), true),
Expand Down Expand Up @@ -198,21 +196,15 @@ export const getExport = ({ template }, opts) => {
plugins.push('typescript')
}

if (opts.memo) {
const nextExportName = `Memo${exportName}`
result += `const ${nextExportName} = React.memo(${exportName})\n\n`
exportName = nextExportName
}

if (opts.ref) {
const nextExportName = `ForwardRef`
let refType = ''

if (opts.typescript) {
refType = ': React.Ref<SVGSVGElement>'
}
result += `const ${nextExportName} = React.forwardRef(${exportName})\n\n`
exportName = nextExportName
}

result += `const ${nextExportName} = React.forwardRef((props, ref${refType}) => <${exportName} svgRef={ref} {...props} />)\n\n`
if (opts.memo) {
const nextExportName = `Memo${exportName}`
result += `const ${nextExportName} = React.memo(${exportName})\n\n`
exportName = nextExportName
}

Expand Down
Loading