Skip to content

Commit d3e5662

Browse files
committed
update snippet react/prevent-default
1 parent 262549c commit d3e5662

File tree

2 files changed

+115
-11
lines changed

2 files changed

+115
-11
lines changed

lib/react/prevent-default.js

Lines changed: 85 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,58 @@
1-
function capitalizeFirstLetter(string) {
2-
return string.charAt(0).toUpperCase() + string.slice(1);
1+
function convertToFunctionName(name) {
2+
if (name.startsWith('on')) {
3+
return name.replace(/^on/, 'handle');
4+
} else {
5+
return `on${name.charAt(0).toUpperCase() + name.slice(1)}`;
6+
}
7+
}
8+
9+
function invertKeyValues(obj) {
10+
return Object.fromEntries(
11+
Object.entries(obj).map(entry => entry.reverse())
12+
);
313
}
414

515
new Synvert.Rewriter("react", "prevent-default", () => {
616
description(`
7-
convert foo to bar
17+
Force add preventDefault() to onClick event handler.
18+
19+
\`\`\`javascript
20+
const Post = ({ newPost, doDelete }) => {
21+
const deletePost = () => {
22+
doDelete();
23+
}
24+
25+
return (
26+
<a href="#" onClick={newPost}>New</a>
27+
<a href="#" onClick={deletePost}>Delete</a>
28+
)
29+
}
30+
export default Post;
31+
\`\`\`
32+
33+
=>
34+
35+
\`\`\`javascript
36+
const Post = ({ newPost, doDelete }) => {
37+
const deletePost = (event) => {
38+
event.preventDefault();
39+
40+
doDelete();
41+
}
42+
43+
const onNewPost = (event) => {
44+
event.preventDefault();
45+
46+
newPost();
47+
}
48+
49+
return (
50+
<a href="#" onClick={onNewPost}>New</a>
51+
<a href="#" onClick={deletePost}>Delete</a>
52+
)
53+
}
54+
export default Post;
55+
\`\`\`
856
`);
957

1058
configure({ parser: Synvert.Parser.TYPESCRIPT });
@@ -16,6 +64,7 @@ new Synvert.Rewriter("react", "prevent-default", () => {
1664
// { functionName: 'preventDefaultNotCalled' } the function is defined with event parameter but not call preventDefault();
1765
// { functionName: 'preventDefaultCalled' } the function is defined with event parameter and call preventDefault();
1866
const onClickPreventDefault = {};
67+
const onClickCode = {};
1968

2069
// find a elements whose href attribute is '#'
2170
findNode(`
@@ -31,8 +80,17 @@ new Synvert.Rewriter("react", "prevent-default", () => {
3180
]
3281
[closingElement=.JsxClosingElement[tagName=a]]`,
3382
() => {
34-
findNode(`.JsxAttribute[name=onClick]`, () => {
83+
findNode(`.JsxAttribute[name=onClick][initializer=.JsxExpression[expression=.Identifier]]`, () => {
3584
onClickPreventDefault[this.currentNode.initializer.expression.escapedText] = 'functionNotDefined';
85+
onClickCode[this.currentNode.initializer.expression.escapedText] = this.mutationAdapter.getSource(this.currentNode.initializer.expression) + '()';
86+
});
87+
findNode(`.JsxAttribute[name=onClick][initializer=.JsxExpression[expression=.ArrowFunction[body=.CallExpression]]]`, () => {
88+
onClickPreventDefault[this.currentNode.initializer.expression.body.expression.escapedText] = 'functionNotDefined';
89+
onClickCode[this.currentNode.initializer.expression.body.expression.escapedText] = this.mutationAdapter.getSource(this.currentNode.initializer.expression.body);
90+
});
91+
findNode(`.JsxAttribute[name=onClick][initializer=.JsxExpression[expression=.ArrowFunction[body=.Block[statements.length>0]]]]`, () => {
92+
onClickPreventDefault[this.currentNode.initializer.expression.body.statements[0].expression.expression.escapedText] = 'functionNotDefined';
93+
onClickCode[this.currentNode.initializer.expression.body.statements[0].expression.expression.escapedText] = this.currentNode.initializer.expression.body.statements.map(statement => this.mutationAdapter.getSource(statement)).join("\n");
3694
});
3795
}
3896
);
@@ -66,18 +124,36 @@ new Synvert.Rewriter("react", "prevent-default", () => {
66124
]
67125
]
68126
]
69-
[closingElement=.JsxClosingElement[tagName=a]] .JsxAttribute[name=onClick][initializer=.JsxExpression[expression=${functionName}]]`,
127+
[closingElement=.JsxClosingElement[tagName=a]]`,
70128
() => {
71-
replace('initializer', { with: `{on${capitalizeFirstLetter(functionName)}}` });
129+
findNode(`
130+
.JsxAttribute
131+
[name=onClick]
132+
[initializer=.JsxExpression
133+
[expression IN (
134+
${functionName}
135+
.ArrowFunction
136+
[body=.CallExpression
137+
[expression=${functionName}]
138+
]
139+
.ArrowFunction
140+
[body=.Block
141+
[statements.length>0]
142+
[statements.0.expression.expression=${functionName}]
143+
]
144+
)]
145+
]`, () => {
146+
replace('initializer', { with: `{${convertToFunctionName(functionName)}}` });
147+
});
72148
}
73149
);
74150

75-
findNode(`.ReturnStatement`, () => {
151+
findNode(`.Block:first-child > .ReturnStatement`, () => {
76152
insertBefore(`
77-
const on${capitalizeFirstLetter(functionName)} = (event) => {
153+
const ${convertToFunctionName(functionName)} = (event) => {
78154
event.preventDefault();
79155
80-
${functionName}();
156+
${onClickCode[functionName]};
81157
}
82158
`.trim() + "\n", { fixIndent: true });
83159
});

test/react/prevent-default.spec.js

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { assertConvert } = require("../utils");
44
describe(snippet, () => {
55
describe('add a function to prevent default', () => {
66
const input = `
7-
const Post = ({ newPost, editPost, doDelete }) => {
7+
const Post = ({ newPost, editPost, doDelete, onPrint, doPop }) => {
88
const onEditPost = (event) => {
99
event.preventDefault();
1010
@@ -15,16 +15,24 @@ describe(snippet, () => {
1515
doDelete();
1616
}
1717
18+
const printIcon = (
19+
<a href="#" onClick={() => onPrint()}>
20+
<img src="/svg/print.svg" alt="Print" className="marginlft10" />
21+
</a>
22+
);
23+
1824
return (
1925
<a href="#" onClick={newPost}>New</a>
2026
<a href="#" onClick={onEditPost}>Edit</a>
2127
<a href="#" onClick={deletePost}>Delete</a>
28+
{printIcon}
29+
<a href="#" onClick={() => { doPop(false) }}>Pop</a>
2230
)
2331
}
2432
export default Post;
2533
`;
2634
const output = `
27-
const Post = ({ newPost, editPost, doDelete }) => {
35+
const Post = ({ newPost, editPost, doDelete, onPrint, doPop }) => {
2836
const onEditPost = (event) => {
2937
event.preventDefault();
3038
@@ -37,16 +45,36 @@ describe(snippet, () => {
3745
doDelete();
3846
}
3947
48+
const printIcon = (
49+
<a href="#" onClick={handlePrint}>
50+
<img src="/svg/print.svg" alt="Print" className="marginlft10" />
51+
</a>
52+
);
53+
54+
const handlePrint = (event) => {
55+
event.preventDefault();
56+
57+
onPrint();
58+
}
59+
4060
const onNewPost = (event) => {
4161
event.preventDefault();
4262
4363
newPost();
4464
}
4565
66+
const onDoPop = (event) => {
67+
event.preventDefault();
68+
69+
doPop(false);
70+
}
71+
4672
return (
4773
<a href="#" onClick={onNewPost}>New</a>
4874
<a href="#" onClick={onEditPost}>Edit</a>
4975
<a href="#" onClick={deletePost}>Delete</a>
76+
{printIcon}
77+
<a href="#" onClick={onDoPop}>Pop</a>
5078
)
5179
}
5280
export default Post;

0 commit comments

Comments
 (0)