Skip to content

Commit

Permalink
GH-2614: N4JSX should accept functions that return ReactNodes (#2621)
Browse files Browse the repository at this point in the history
* allow ReactNodes as a return type for jsx component functions

* adjust tests

* adjust tests
  • Loading branch information
mmews-n4 authored Apr 22, 2024
1 parent fbbeded commit 198736a
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ public class ReactHelper {
/***/
public final static String REACT_COMPONENT = "Component";
/***/
public final static String REACT_NODE = "ReactNode";
/***/
public final static String REACT_ELEMENT = "ReactElement";
/***/
public final static String REACT_FRAGMENT_NAME = "Fragment";
Expand Down Expand Up @@ -174,6 +176,20 @@ public IdentifiableElement getJsxBackendFragmentComponent(Resource resource) {
return null;
}

/**
* Look up React.Node in the index.
*
* @param context
* the EObject serving the context to look for React.Node.
*/
public TClassifier lookUpReactNode(EObject context) {
// val reactElement = lookUpReactClassifier(context, REACT_ELEMENT, TInterface)
// if (reactElement !== null) {
// return reactElement;
// }
return lookUpReactClassifier_OLD(context, REACT_NODE, TInterface.class);
}

/**
* Look up React.Element in the index.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,11 @@ class N4JSXValidator extends AbstractN4JSDeclarativeValidator {
* See Req. IDE-241116
*/
def private void checkFunctionTypeExprOrRef(JSXElement jsxElem, FunctionTypeExprOrRef exprTypeRef) {
val tReactElement = reactHelper.lookUpReactElement(jsxElem);
if (tReactElement === null)
val tReactNode = reactHelper.lookUpReactNode(jsxElem);
if (tReactNode === null)
return;

val expectedReturnTypeRef = TypeUtils.createTypeRef(tReactElement, TypingStrategy.DEFAULT, true);
val expectedReturnTypeRef = TypeUtils.createTypeRef(tReactNode, TypingStrategy.DEFAULT, true);

val expr = jsxElem.jsxElementName.expression;
val G = expr.newRuleEnvironment;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2016 NumberFour AG.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* NumberFour AG - Initial API and implementation
*/

/* XPECT_SETUP org.eclipse.n4js.spec.tests.SpecXtTest
Workspace {
Project "N4JSXSpecTest" {
Folder "src" {
ThisFile {}
}
File "package.json" { from="../package_default.json" }
}
Project "react" {
File "index.n4jsd" { from = "react/index.n4jsd" }
File "package.json" { from = "react/package.json" }
}
}
END_SETUP
*/

import * as React from "react/index";


function Comp(): React.ReactNode {
return null;
}
// XPECT noerrors -->
<Comp />
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function MyNormalFunction(props: ~React.ComponentProps with {myOtherProp: string
return "My Normal Function";
}

// XPECT errors --> "Expecting a function returning a value of type ReactElement<?> but the return type is string." at "MyNormalFunction"
// XPECT noerrors --> "Expecting a function returning a value of type ReactElement<?> but the return type is string." at "MyNormalFunction"
<MyNormalFunction myOtherProp="Hello"/>

class MyReactClassComponent extends React.Component<~React.ComponentProps with {prop: int}, Object> {
Expand Down
10 changes: 10 additions & 0 deletions tests/org.eclipse.n4js.spec.tests/xt-tests/react/index.n4jsd
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ export external public abstract class PureComponent<PropsT extends ComponentProp
extends Component<PropsT, StateT> {
}


export external public type ReactText = string | number;
export external public type ReactChild = ReactElement<?> | ReactText;

export external public type ReactNodeArray = ReactNode[];
export external public type ReactFragment = Object | ReactNodeArray;
export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal<?> | */ boolean;



// mor TODO IDE-2323
// public static <PropsT extends ComponentProps> createElement(
// type: union{string, {function(PropsT?): ReactElement}, constructor{Component<PropsT, ?>}},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ export external public abstract class Component<PropsT extends ComponentProps, S
protected componentWillUnmount(): void;
}


export external public type ReactText = string | number;
export external public type ReactChild = ReactElement<?> | ReactText;

export external public type ReactNodeArray = ReactNode[];
export external public type ReactFragment = Object | ReactNodeArray;
export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal<?> | */ boolean;




export external public abstract class PureComponent<PropsT extends ComponentProps, StateT extends Object>
extends Component<PropsT, StateT> {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,17 @@ export external public abstract class PureComponent<PropsT extends ComponentProp
extends Component<PropsT, StateT> {
}


export external public type ReactText = string | number;
export external public type ReactChild = ReactElement<?> | ReactText;

export external public type ReactNodeArray = ReactNode[];
export external public type ReactFragment = Object | ReactNodeArray;
export external public type ReactNode = ReactChild | ReactFragment | /* ReactPortal<?> | */ boolean;




// mor TODO IDE-2323
// public static <PropsT extends ComponentProps> createElement(
// type: union{string, {function(PropsT?): ReactElement}, constructor{Component<PropsT, ?>}},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export public class LowerComponent extends React.Component<~React.ComponentProps


/* XPECT scope at 'React.<|>Component' ---
Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactElement
Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactChild, ReactElement, ReactFragment, ReactNode, ReactNodeArray, ReactText
--- */
export public class MyComponent extends React.Component<~React.ComponentProps with {aProp: string}, String> {

Expand Down Expand Up @@ -72,7 +72,7 @@ export public class MyComponent extends React.Component<~React.ComponentProps wi
__proto__, arguments, console, global, constText, constructor,
decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, eval, hasOwnProperty,
index.Component, index.ComponentProps, index.Fragment, index.PropConstraint,
index.PropType, index.PropTypes, import, index.PureComponent, index.ReactElement, index.cloneElement, index.createElement,
index.PropType, index.PropTypes, import, index.PureComponent, index.ReactChild, index.ReactElement, index.ReactFragment, index.ReactNode, index.ReactNodeArray, index.ReactText, index.cloneElement, index.createElement,
index.isValidElement, index.renderToStaticMarkup, index.renderToString, isFinite, isNaN,
isPrototypeOf, null, parseFloat, parseInt, propertyIsEnumerable,
toLocaleString, toString, undefined, valueOf, void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class Props {
}

/* XPECT scope at 'React.<|>ReactElement' ---
Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactElement
Component, ComponentProps, Fragment, PropConstraint, PropType, PropTypes, PureComponent, ReactChild, ReactElement, ReactFragment, ReactNode, ReactNodeArray, ReactText
--- */
export public function NamedFnComponent(props: Props): React.ReactElement {
return (<div>{props.myProp}</div>);
Expand Down

0 comments on commit 198736a

Please sign in to comment.