Repository: DevExpress/devextreme-react
Pull request #774 (2022-10-18 06:09:36)

Fix acceptable types for props
Cherry-pick of #772 


Patch:
diff --git a/packages/devextreme-react-generator/package.json b/packages/devextreme-react-generator/package.json
index be2eb43..61b7a18 100644
--- a/packages/devextreme-react-generator/package.json
+++ b/packages/devextreme-react-generator/package.json
@@ -1,7 +1,7 @@
 {
   "author": "Developer Express Inc.",
   "name": "devextreme-react-generator",
-  "version": "1.3.0",
+  "version": "1.3.2",
   "description": "DevExtreme React UI and Visualization Components",
   "repository": {
     "type": "git",
diff --git a/packages/devextreme-react-generator/src/component-generator.ts b/packages/devextreme-react-generator/src/component-generator.ts
index 1cd7e04..0d3569c 100644
--- a/packages/devextreme-react-generator/src/component-generator.ts
+++ b/packages/devextreme-react-generator/src/component-generator.ts
@@ -1,4 +1,6 @@
-import { createTempate, L1, L2 } from './template';
+import {
+  createTempate, L1, L2, L3,
+} from './template';
 
 import {
   createKeyComparator,
@@ -61,6 +63,7 @@ type IOption = {
 interface IPropTyping {
   propName: string;
   types: string[];
+  acceptableType?: string;
   acceptableValues?: string[];
 }
 
@@ -427,10 +430,19 @@ function buildPropsTypeName(className: string) {
   return `I${className}Props`;
 }
 
+function acceptableTypes(acceptableType: string | undefined, acceptableValues: string[]): string {
+  const indent = acceptableType ? L3 : L2;
+  const acceptableTemplate = `PropTypes.oneOf([${indent}${acceptableValues.join(`,${indent}`)}])`;
+  return acceptableType ? `PropTypes.oneOfType([
+    PropTypes.${acceptableType},
+    ${acceptableTemplate}
+  ])` : acceptableTemplate;
+}
+
 function createPropTypingModel(typing: IPropTyping): IRenderedPropTyping {
   const types = typing.types.map((t) => `PropTypes.${t}`);
   if (typing.acceptableValues && isNotEmptyArray(typing.acceptableValues)) {
-    types.push(`PropTypes.oneOf([\n    ${typing.acceptableValues.join(',\n    ')}\n  ])`);
+    types.push(acceptableTypes(typing.acceptableType, typing.acceptableValues));
   }
   return {
     propName: typing.propName,
diff --git a/packages/devextreme-react-generator/src/generator.ts b/packages/devextreme-react-generator/src/generator.ts
index 0bb7355..ed946fd 100644
--- a/packages/devextreme-react-generator/src/generator.ts
+++ b/packages/devextreme-react-generator/src/generator.ts
@@ -129,6 +129,7 @@ export function createPropTyping(
     return {
       propName: option.name,
       types: types || [],
+      acceptableType: restrictedTypes[0].type.toLowerCase(),
       acceptableValues: restrictedTypes[0].acceptableValues,
     };
   }


Test patch:
diff --git a/packages/devextreme-react-generator/src/component-generator.test.ts b/packages/devextreme-react-generator/src/component-generator.test.ts
index 132a0b9..289c6b1 100644
--- a/packages/devextreme-react-generator/src/component-generator.test.ts
+++ b/packages/devextreme-react-generator/src/component-generator.test.ts
@@ -1004,6 +1004,61 @@ export {
     ).toBe(EXPECTED);
   });
 
+  it('adds check for acceptable values with common type', () => {
+    // #region EXPECTED
+    const EXPECTED = `
+import dxCLASS_NAME, {
+    Properties
+} from "DX/WIDGET/PATH";
+
+import * as PropTypes from "prop-types";
+import { Component as BaseComponent, IHtmlOptions } from "BASE_COMPONENT_PATH";
+
+type ICLASS_NAMEOptions = React.PropsWithChildren<Properties & IHtmlOptions & {
+}>
+
+class CLASS_NAME extends BaseComponent<React.PropsWithChildren<ICLASS_NAMEOptions>> {
+
+  public get instance(): dxCLASS_NAME {
+    return this._instance;
+  }
+
+  protected _WidgetClass = dxCLASS_NAME;
+}
+(CLASS_NAME as any).propTypes = {
+  PROP1: PropTypes.oneOfType([
+    PropTypes.string,
+    PropTypes.oneOf([
+      "VALUE_1",
+      "VALUE_2"])
+  ])
+};
+export default CLASS_NAME;
+export {
+  CLASS_NAME,
+  ICLASS_NAMEOptions
+};
+`.trimLeft();
+    // #endregion
+
+    expect(
+      generate({
+        name: 'CLASS_NAME',
+        baseComponentPath: 'BASE_COMPONENT_PATH',
+        extensionComponentPath: 'EXTENSION_COMPONENT_PATH',
+        dxExportPath: 'DX/WIDGET/PATH',
+        propTypings: [
+          {
+            propName: 'PROP1',
+            types: [],
+            acceptableType: 'string',
+            acceptableValues: ['"VALUE_1"', '"VALUE_2"'],
+          },
+        ],
+      }),
+    ).toBe(EXPECTED);
+  });
+
   it('adds check for acceptable values', () => {
     // #region EXPECTED
     const EXPECTED = `
@@ -1028,8 +1083,7 @@ class CLASS_NAME extends BaseComponent<React.PropsWithChildren<ICLASS_NAMEOption
 (CLASS_NAME as any).propTypes = {
   PROP1: PropTypes.oneOf([
     "VALUE_1",
-    "VALUE_2"
-  ])
+    "VALUE_2"])
 };
 export default CLASS_NAME;
 export {