Skip to content

Commit fbd4c8a

Browse files
Copilotitsdouges
andauthored
Fix React.FC default props extraction in type inference (#368)
* Initial plan * Initial exploration and test setup Co-authored-by: itsdouges <[email protected]> * Fix React.FC default props extraction Co-authored-by: itsdouges <[email protected]> * docs(changeset): * Add changeset for React.FC default props fix Co-authored-by: itsdouges <[email protected]> * Addressing PR comments Co-authored-by: itsdouges <[email protected]> * Fix lint errors in box.tsx test file Co-authored-by: itsdouges <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: itsdouges <[email protected]>
1 parent ee062f8 commit fbd4c8a

3 files changed

Lines changed: 39 additions & 10 deletions

File tree

.changeset/perfect-emus-beg.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@triplex/server": patch
3+
---
4+
5+
Fixed React.FC components not displaying their default prop values in the editor UI. Components declared with `React.FC` will now correctly show default values when the element is active in the editor.

packages/@triplex/server/src/ast/__tests__/type-infer.test.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -678,25 +678,26 @@ describe("type infer", () => {
678678
`);
679679
});
680680

681-
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
682-
// TODO: How do we get access to the concrete AST of a component declared using
683-
// React.FC? It's currently unknown so this test isn't showing the correct data
684-
// it's missing default props!
685-
it.todo("should infer default props from a jsx element React.FC decl", () => {
681+
it("should infer default props from a jsx element React.FC decl", () => {
686682
const project = _createProject({
687683
tsConfigFilePath: join(__dirname, "__mocks__/tsconfig.json"),
688684
});
689685
const sourceFile = project.addSourceFileAtPath(
690-
join(__dirname, "__mocks__/default-props.tsx"),
686+
join(__dirname, "__mocks__/type-extraction.tsx"),
691687
);
692-
const sceneObject = getJsxElementAtOrThrow(sourceFile, 14, 7);
688+
const sceneObject = getJsxElementAtOrThrow(sourceFile, 102, 7);
693689

694690
const { props } = getJsxElementPropTypes(sceneObject);
695691

696692
expect(props).toMatchInlineSnapshot(`
697693
[
698694
{
695+
"defaultValue": {
696+
"kind": "number",
697+
"value": 700,
698+
},
699699
"description": undefined,
700+
"group": "Other",
700701
"kind": "number",
701702
"name": "scaleMax",
702703
"required": false,
@@ -708,6 +709,7 @@ describe("type infer", () => {
708709
{
709710
"column": 13,
710711
"description": undefined,
712+
"group": "Other",
711713
"kind": "string",
712714
"line": 102,
713715
"name": "seed",
@@ -717,22 +719,37 @@ describe("type infer", () => {
717719
"valueKind": "string",
718720
},
719721
{
722+
"defaultValue": {
723+
"kind": "string",
724+
"value": "WGAN",
725+
},
720726
"description": undefined,
727+
"group": "Other",
721728
"kind": "string",
722729
"literal": "foo",
723730
"name": "strategy",
724731
"required": false,
725732
"tags": {},
726733
},
727734
{
735+
"defaultValue": {
736+
"kind": "boolean",
737+
"value": true,
738+
},
728739
"description": undefined,
740+
"group": "Other",
729741
"kind": "boolean",
730742
"name": "useInterpolation",
731743
"required": false,
732744
"tags": {},
733745
},
734746
{
747+
"defaultValue": {
748+
"kind": "boolean",
749+
"value": true,
750+
},
735751
"description": undefined,
752+
"group": "Other",
736753
"kind": "boolean",
737754
"name": "useNoise",
738755
"required": false,

packages/@triplex/server/src/ast/type-infer.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,19 @@ function getJsxDeclProps(
281281
return null;
282282
}
283283

284-
const symbol = jsxType.getSymbol() || jsxType.getAliasSymbol();
285-
if (!symbol) {
284+
// For React.FC components, we want the actual component's declaration (e.g., const Home: React.FC = ...)
285+
// rather than the FunctionComponent interface declaration
286+
const componentSymbol = tagName.getSymbol();
287+
const typeSymbol = jsxType.getSymbol() || jsxType.getAliasSymbol();
288+
289+
if (!typeSymbol) {
286290
throw new Error("invariant: could not find symbol");
287291
}
288292

289-
const declaration = symbol.getDeclarations()[0];
293+
// Use the component's symbol if available (for React.FC), otherwise use the type symbol
294+
const declaration = componentSymbol
295+
? componentSymbol.getDeclarations()[0]
296+
: typeSymbol.getDeclarations()[0];
290297

291298
const propsType = element
292299
.getProject()

0 commit comments

Comments
 (0)