From a0be486680941ca5c038f215df3ee5da8072124b Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 20 Jan 2022 19:11:05 +0100 Subject: [PATCH 01/17] work on prebundling lib/component --- .eslintrc.js | 7 ++++++- lib/components/package.json | 18 ++++++++++-------- lib/components/src/index.ts | 3 +++ yarn.lock | 1 + 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 0a3ad1dcff07..0cfd430c2024 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -74,7 +74,12 @@ module.exports = { }, { // this package uses pre-bundling, dependencies will be bundled, and will be in devDepenencies - files: ['**/lib/theming/**/*', '**/lib/router/**/*', '**/lib/ui/**/*'], + files: [ + '**/lib/theming/**/*', + '**/lib/router/**/*', + '**/lib/ui/**/*', + '**/lib/components/**/*', + ], rules: { 'import/no-extraneous-dependencies': 'off', }, diff --git a/lib/components/package.json b/lib/components/package.json index 937b091f8cfb..30056e19297b 100644 --- a/lib/components/package.json +++ b/lib/components/package.json @@ -37,20 +37,25 @@ "*.d.ts" ], "scripts": { - "prepare": "node ../../scripts/prepare.js" + "prepare": "ts-node ../../scripts/prebundle.ts" }, "dependencies": { - "@popperjs/core": "^2.6.0", "@storybook/client-logger": "6.5.0-alpha.22", "@storybook/csf": "0.0.2--canary.87bc651.0", "@storybook/theming": "6.5.0-alpha.22", + "core-js": "^3.8.2", + "regenerator-runtime": "^0.13.7" + }, + "devDependencies": { + "@popperjs/core": "^2.6.0", "@types/color-convert": "^2.0.0", "@types/overlayscrollbars": "^1.12.0", "@types/react-syntax-highlighter": "11.0.5", "color-convert": "^2.0.1", - "core-js": "^3.8.2", + "css": "^3.0.0", "fast-deep-equal": "^3.1.3", "global": "^4.4.0", + "jest": "^26.6.3", "lodash": "^4.17.21", "markdown-to-jsx": "^7.1.3", "memoizerific": "^1.11.3", @@ -62,14 +67,10 @@ "react-popper-tooltip": "^3.1.1", "react-syntax-highlighter": "^15.4.5", "react-textarea-autosize": "^8.3.0", - "regenerator-runtime": "^0.13.7", "ts-dedent": "^2.0.0", + "ts-node": "^10.4.0", "util-deprecate": "^1.0.2" }, - "devDependencies": { - "css": "^3.0.0", - "jest": "^26.6.3" - }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0", "react-dom": "^16.8.0 || ^17.0.0" @@ -77,6 +78,7 @@ "publishConfig": { "access": "public" }, + "bundlerEntrypoint": "./src/index.ts", "gitHead": "fc1a84084556d61fea8bcaa197f7d85f3a9a107d", "sbmodern": "dist/modern/index.js" } diff --git a/lib/components/src/index.ts b/lib/components/src/index.ts index c142453d8540..54d8578b4aec 100644 --- a/lib/components/src/index.ts +++ b/lib/components/src/index.ts @@ -1,3 +1,6 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// + import { createElement, forwardRef, ElementType } from 'react'; import { components as rawComponents } from './typography/DocumentFormatting'; diff --git a/yarn.lock b/yarn.lock index 2f3e44cf6f35..a0a61f067b90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7495,6 +7495,7 @@ __metadata: react-textarea-autosize: ^8.3.0 regenerator-runtime: ^0.13.7 ts-dedent: ^2.0.0 + ts-node: ^10.4.0 util-deprecate: ^1.0.2 peerDependencies: react: ^16.8.0 || ^17.0.0 From 4b25db157867aef062f008843e05b9fedac12817 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 21 Jan 2022 12:30:33 +0100 Subject: [PATCH 02/17] wip --- .eslintignore | 1 - .../{JsonAddValue.js => JsonAddValue.tsx} | 0 .../components/{JsonArray.js => JsonArray.tsx} | 0 ...onFunctionValue.js => JsonFunctionValue.tsx} | 0 .../components/{JsonNode.js => JsonNode.tsx} | 0 .../{JsonObject.js => JsonObject.tsx} | 0 .../components/{JsonValue.js => JsonValue.tsx} | 0 .../{index.js => index.tsx} | 2 +- .../types/{dataTypes.js => dataTypes.ts} | 0 .../types/{deltaTypes.js => deltaTypes.ts} | 0 .../{inputUsageTypes.js => inputUsageTypes.ts} | 0 .../utils/{objectTypes.js => objectTypes.ts} | 7 ++----- .../utils/{parse.js => parse.ts} | 4 +--- .../utils/{styles.js => styles.ts} | 12 +++++------- lib/components/tsconfig.json | 3 --- package.json | 1 + scripts/prebundle.ts | 17 ++++++++++++++++- yarn.lock | 10 ++++++++++ 18 files changed, 36 insertions(+), 21 deletions(-) rename lib/components/src/controls/react-editable-json-tree/components/{JsonAddValue.js => JsonAddValue.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/components/{JsonArray.js => JsonArray.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/components/{JsonFunctionValue.js => JsonFunctionValue.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/components/{JsonNode.js => JsonNode.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/components/{JsonObject.js => JsonObject.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/components/{JsonValue.js => JsonValue.tsx} (100%) rename lib/components/src/controls/react-editable-json-tree/{index.js => index.tsx} (99%) rename lib/components/src/controls/react-editable-json-tree/types/{dataTypes.js => dataTypes.ts} (100%) rename lib/components/src/controls/react-editable-json-tree/types/{deltaTypes.js => deltaTypes.ts} (100%) rename lib/components/src/controls/react-editable-json-tree/types/{inputUsageTypes.js => inputUsageTypes.ts} (100%) rename lib/components/src/controls/react-editable-json-tree/utils/{objectTypes.js => objectTypes.ts} (80%) rename lib/components/src/controls/react-editable-json-tree/utils/{parse.js => parse.ts} (88%) rename lib/components/src/controls/react-editable-json-tree/utils/{styles.js => styles.ts} (87%) diff --git a/.eslintignore b/.eslintignore index 7ba4c1389c28..262232534b26 100644 --- a/.eslintignore +++ b/.eslintignore @@ -11,7 +11,6 @@ lib/manager-webpack4/prebuilt lib/manager-webpack5/prebuilt lib/core-server/prebuilt lib/codemod/src/transforms/__testfixtures__ -lib/components/src/controls/react-editable-json-tree scripts/storage scripts/repros-generator *.bundle.js diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.js b/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonArray.js b/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonArray.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.js b/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonNode.js b/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonNode.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonObject.js b/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonObject.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonValue.js b/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/components/JsonValue.js rename to lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx diff --git a/lib/components/src/controls/react-editable-json-tree/index.js b/lib/components/src/controls/react-editable-json-tree/index.tsx similarity index 99% rename from lib/components/src/controls/react-editable-json-tree/index.js rename to lib/components/src/controls/react-editable-json-tree/index.tsx index cc55b51b9ba1..b8f2ee91c4be 100644 --- a/lib/components/src/controls/react-editable-json-tree/index.js +++ b/lib/components/src/controls/react-editable-json-tree/index.tsx @@ -7,7 +7,7 @@ import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from './types/de import { getObjectType } from './utils/objectTypes'; import DATA_TYPES from './types/dataTypes'; import INPUT_USAGE_TYPES from './types/inputUsageTypes'; -import parse from './utils/parse'; +import { parse } from './utils/parse'; class JsonTree extends Component { constructor(props) { diff --git a/lib/components/src/controls/react-editable-json-tree/types/dataTypes.js b/lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/types/dataTypes.js rename to lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts diff --git a/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.js b/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/types/deltaTypes.js rename to lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts diff --git a/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.js b/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts similarity index 100% rename from lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.js rename to lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts diff --git a/lib/components/src/controls/react-editable-json-tree/utils/objectTypes.js b/lib/components/src/controls/react-editable-json-tree/utils/objectTypes.ts similarity index 80% rename from lib/components/src/controls/react-editable-json-tree/utils/objectTypes.js rename to lib/components/src/controls/react-editable-json-tree/utils/objectTypes.ts index 098f32e3b59e..e2c5bf670eb7 100644 --- a/lib/components/src/controls/react-editable-json-tree/utils/objectTypes.js +++ b/lib/components/src/controls/react-editable-json-tree/utils/objectTypes.ts @@ -3,7 +3,7 @@ * @param obj {*} object to get type * @returns {*} */ -function getObjectType(obj) { +export function getObjectType(obj: any) { if ( obj !== null && typeof obj === 'object' && @@ -21,11 +21,8 @@ function getObjectType(obj) { * @param newValue {*} new value * @returns {boolean} result */ -function isComponentWillChange(oldValue, newValue) { +export function isComponentWillChange(oldValue: T, newValue: T): boolean { const oldType = getObjectType(oldValue); const newType = getObjectType(newValue); return (oldType === 'Function' || newType === 'Function') && newType !== oldType; } - -export { getObjectType }; -export { isComponentWillChange }; diff --git a/lib/components/src/controls/react-editable-json-tree/utils/parse.js b/lib/components/src/controls/react-editable-json-tree/utils/parse.ts similarity index 88% rename from lib/components/src/controls/react-editable-json-tree/utils/parse.js rename to lib/components/src/controls/react-editable-json-tree/utils/parse.ts index 75238d695707..06c772d0e784 100644 --- a/lib/components/src/controls/react-editable-json-tree/utils/parse.js +++ b/lib/components/src/controls/react-editable-json-tree/utils/parse.ts @@ -3,7 +3,7 @@ * @param string {String} string to parse * @returns {*} */ -function parse(string) { +export function parse(string: string) { let result = string; // Check if string contains 'function' and start with it to eval it @@ -18,5 +18,3 @@ function parse(string) { } return result; } - -export default parse; diff --git a/lib/components/src/controls/react-editable-json-tree/utils/styles.js b/lib/components/src/controls/react-editable-json-tree/utils/styles.ts similarity index 87% rename from lib/components/src/controls/react-editable-json-tree/utils/styles.js rename to lib/components/src/controls/react-editable-json-tree/utils/styles.ts index b446bda82ab3..77607150955e 100644 --- a/lib/components/src/controls/react-editable-json-tree/utils/styles.js +++ b/lib/components/src/controls/react-editable-json-tree/utils/styles.ts @@ -1,4 +1,4 @@ -const object = { +export const object = { minus: { color: 'red', }, @@ -19,7 +19,8 @@ const object = { }, addForm: {}, }; -const array = { + +export const array = { minus: { color: 'red', }, @@ -40,7 +41,8 @@ const array = { }, addForm: {}, }; -const value = { + +export const value = { minus: { color: 'red', }, @@ -57,7 +59,3 @@ const value = { color: '#2287CD', }, }; - -export { object }; -export { array }; -export { value }; diff --git a/lib/components/tsconfig.json b/lib/components/tsconfig.json index 5878184e06a1..73d4cb45a958 100644 --- a/lib/components/tsconfig.json +++ b/lib/components/tsconfig.json @@ -2,9 +2,6 @@ "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./src", - "types": [ - "jest", "@testing-library/jest-dom" - ] }, "include": [ "src/**/*" diff --git a/package.json b/package.json index c8729966868c..624bb851db1a 100644 --- a/package.json +++ b/package.json @@ -280,6 +280,7 @@ "remark-lint": "^8.0.0", "remark-preset-lint-recommended": "^5.0.0", "rollup": "^2.60.2", + "rollup-plugin-extensions": "^0.1.0", "rollup-plugin-jsx": "^1.0.3", "rollup-plugin-terser": "^7.0.2", "serve-static": "^1.14.1", diff --git a/scripts/prebundle.ts b/scripts/prebundle.ts index ea825e87e4a8..fe46284ef9c4 100644 --- a/scripts/prebundle.ts +++ b/scripts/prebundle.ts @@ -10,6 +10,7 @@ import commonjs from '@rollup/plugin-commonjs'; import json from '@rollup/plugin-json'; import { babel } from '@rollup/plugin-babel'; import { terser } from 'rollup-plugin-terser'; +import extensions from 'rollup-plugin-extensions'; import { generateDtsBundle } from 'dts-bundle-generator'; import * as dtsLozalize from './dts-localize'; @@ -27,10 +28,24 @@ async function buildESM({ cwd, input, externals }: BuildOptions) { nodeResolve({ browser: true, preferBuiltins: true, + extensions: ['.tsx', '.ts', '.jsx', 'js'], }), commonjs(), json(), - babel({ babelHelpers: 'runtime', skipPreflightCheck: true }), + babel({ + babelHelpers: 'runtime', + skipPreflightCheck: true, + // presets: ['@babel/typescript', '@babel/react'], + extensions: ['.tsx', '.ts', '.jsx', 'js'], + }), + // extensions({ + // // Supporting Typescript files + // // Uses ".mjs, .js" by default + // extensions: ['.tsx', '.ts', '.jsx', '.js'], + // // Resolves index dir files based on supplied extensions + // // This is enable by default + // resolveIndex: true, + // }), rollupTypescript({ lib: ['es2015', 'dom'], target: 'es6' }), terser({ output: { comments: false }, module: true }), ], diff --git a/yarn.lock b/yarn.lock index a0a61f067b90..2772dcab946c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8447,6 +8447,7 @@ __metadata: remark-lint: ^8.0.0 remark-preset-lint-recommended: ^5.0.0 rollup: ^2.60.2 + rollup-plugin-extensions: ^0.1.0 rollup-plugin-jsx: ^1.0.3 rollup-plugin-terser: ^7.0.2 serve-static: ^1.14.1 @@ -39842,6 +39843,15 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-extensions@npm:^0.1.0": + version: 0.1.0 + resolution: "rollup-plugin-extensions@npm:0.1.0" + peerDependencies: + rollup: ^1.4.1 + checksum: 1d08385299b3936074dacb837ba154b7ead4b5612c0fdc6b40e227987f5cdd1ea7652bfd81b867f7a77e5f100638862d1778b4bf6519027d573bf7dbad86f666 + languageName: node + linkType: hard + "rollup-plugin-jsx@npm:^1.0.3": version: 1.0.3 resolution: "rollup-plugin-jsx@npm:1.0.3" From b9bf49327e0089b3c5c4afe168ee26b153fbd18e Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Fri, 21 Jan 2022 17:00:47 +0100 Subject: [PATCH 03/17] wip --- .../src/blocks/ArgsTable/ArgJsDoc.tsx | 2 +- .../src/blocks/ArgsTable/ArgRow.tsx | 2 +- .../src/blocks/ArgsTable/ArgValue.tsx | 2 +- .../src/blocks/ArgsTable/ArgsTable.tsx | 2 +- lib/components/src/blocks/ColorPalette.tsx | 2 +- lib/components/src/blocks/Description.tsx | 2 +- lib/components/src/blocks/DocsPage.tsx | 2 +- lib/components/src/blocks/EmptyBlock.tsx | 2 +- lib/components/src/blocks/IconGallery.tsx | 2 +- lib/components/src/blocks/Typeset.tsx | 2 +- lib/components/src/controls/Object.tsx | 1 - .../components/JsonAddValue.tsx | 48 +- .../components/JsonArray.tsx | 88 ++-- .../components/JsonFunctionValue.tsx | 67 +-- .../components/JsonNode.tsx | 87 ++-- .../components/JsonObject.tsx | 93 ++-- .../components/JsonValue.tsx | 67 +-- .../react-editable-json-tree/index.tsx | 17 +- .../types/dataTypes.ts | 14 +- .../types/deltaTypes.ts | 4 +- .../types/inputUsageTypes.ts | 5 +- lib/components/src/index.ts | 2 +- .../src/typography/DocumentFormatting.tsx | 411 +----------------- .../src/typography/ResetWrapper.tsx | 12 + lib/components/src/typography/components.tsx | 53 +++ lib/components/src/typography/elements/A.tsx | 24 + .../src/typography/elements/Blockquote.tsx | 14 + .../src/typography/elements/Code.tsx | 54 +++ lib/components/src/typography/elements/DL.tsx | 37 ++ .../src/typography/elements/Div.tsx | 4 + lib/components/src/typography/elements/H1.tsx | 7 + lib/components/src/typography/elements/H2.tsx | 8 + lib/components/src/typography/elements/H3.tsx | 6 + lib/components/src/typography/elements/H4.tsx | 6 + lib/components/src/typography/elements/H5.tsx | 6 + lib/components/src/typography/elements/H6.tsx | 7 + lib/components/src/typography/elements/HR.tsx | 8 + .../src/typography/elements/Img.tsx | 5 + lib/components/src/typography/elements/LI.tsx | 16 + .../src/typography/elements/Link.tsx | 14 + lib/components/src/typography/elements/OL.tsx | 15 + lib/components/src/typography/elements/P.tsx | 9 + .../src/typography/elements/Pre.tsx | 41 ++ .../src/typography/elements/Span.tsx | 83 ++++ lib/components/src/typography/elements/TT.tsx | 4 + .../src/typography/elements/Table.tsx | 37 ++ lib/components/src/typography/elements/UL.tsx | 15 + .../typography/{shared.tsx => lib/common.tsx} | 2 +- .../src/typography/lib/isReactChildString.tsx | 3 + 49 files changed, 767 insertions(+), 647 deletions(-) create mode 100644 lib/components/src/typography/ResetWrapper.tsx create mode 100644 lib/components/src/typography/components.tsx create mode 100644 lib/components/src/typography/elements/A.tsx create mode 100644 lib/components/src/typography/elements/Blockquote.tsx create mode 100644 lib/components/src/typography/elements/Code.tsx create mode 100644 lib/components/src/typography/elements/DL.tsx create mode 100644 lib/components/src/typography/elements/Div.tsx create mode 100644 lib/components/src/typography/elements/H1.tsx create mode 100644 lib/components/src/typography/elements/H2.tsx create mode 100644 lib/components/src/typography/elements/H3.tsx create mode 100644 lib/components/src/typography/elements/H4.tsx create mode 100644 lib/components/src/typography/elements/H5.tsx create mode 100644 lib/components/src/typography/elements/H6.tsx create mode 100644 lib/components/src/typography/elements/HR.tsx create mode 100644 lib/components/src/typography/elements/Img.tsx create mode 100644 lib/components/src/typography/elements/LI.tsx create mode 100644 lib/components/src/typography/elements/Link.tsx create mode 100644 lib/components/src/typography/elements/OL.tsx create mode 100644 lib/components/src/typography/elements/P.tsx create mode 100644 lib/components/src/typography/elements/Pre.tsx create mode 100644 lib/components/src/typography/elements/Span.tsx create mode 100644 lib/components/src/typography/elements/TT.tsx create mode 100644 lib/components/src/typography/elements/Table.tsx create mode 100644 lib/components/src/typography/elements/UL.tsx rename lib/components/src/typography/{shared.tsx => lib/common.tsx} (95%) create mode 100644 lib/components/src/typography/lib/isReactChildString.tsx diff --git a/lib/components/src/blocks/ArgsTable/ArgJsDoc.tsx b/lib/components/src/blocks/ArgsTable/ArgJsDoc.tsx index 126181d71a4b..3148abeb457e 100644 --- a/lib/components/src/blocks/ArgsTable/ArgJsDoc.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgJsDoc.tsx @@ -1,7 +1,7 @@ import React, { FC } from 'react'; import { styled } from '@storybook/theming'; import { JsDocTags } from './types'; -import { codeCommon } from '../../typography/shared'; +import { codeCommon } from '../../typography/lib/common'; interface ArgJsDocArgs { tags: JsDocTags; diff --git a/lib/components/src/blocks/ArgsTable/ArgRow.tsx b/lib/components/src/blocks/ArgsTable/ArgRow.tsx index ab7c0800d357..0a29f7f858d8 100644 --- a/lib/components/src/blocks/ArgsTable/ArgRow.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgRow.tsx @@ -6,7 +6,7 @@ import { ArgType, Args, TableAnnotation } from './types'; import { ArgJsDoc } from './ArgJsDoc'; import { ArgValue } from './ArgValue'; import { ArgControl, ArgControlProps } from './ArgControl'; -import { codeCommon } from '../../typography/shared'; +import { codeCommon } from '../../typography/lib/common'; interface ArgRowProps { row: ArgType; diff --git a/lib/components/src/blocks/ArgsTable/ArgValue.tsx b/lib/components/src/blocks/ArgsTable/ArgValue.tsx index 5ef0d2923137..6d77e88954bb 100644 --- a/lib/components/src/blocks/ArgsTable/ArgValue.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgValue.tsx @@ -6,7 +6,7 @@ import { PropSummaryValue } from './types'; import { WithTooltipPure } from '../../tooltip/lazy-WithTooltip'; import { Icons } from '../../icon/icon'; import { SyntaxHighlighter } from '../../syntaxhighlighter/lazy-syntaxhighlighter'; -import { codeCommon } from '../../typography/shared'; +import { codeCommon } from '../../typography/lib/common'; interface ArgValueProps { value?: PropSummaryValue; diff --git a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx index abeeb84973c5..faabe0e11c24 100644 --- a/lib/components/src/blocks/ArgsTable/ArgsTable.tsx +++ b/lib/components/src/blocks/ArgsTable/ArgsTable.tsx @@ -8,7 +8,7 @@ import { SectionRow } from './SectionRow'; import { ArgType, ArgTypes, Args } from './types'; import { EmptyBlock } from '../EmptyBlock'; import { Link } from '../../typography/link/link'; -import { ResetWrapper } from '../../typography/DocumentFormatting'; +import { ResetWrapper } from '../../typography/ResetWrapper'; export const TableWrapper = styled.table<{ compact?: boolean; diff --git a/lib/components/src/blocks/ColorPalette.tsx b/lib/components/src/blocks/ColorPalette.tsx index 9898a44f168e..f726b7f40738 100644 --- a/lib/components/src/blocks/ColorPalette.tsx +++ b/lib/components/src/blocks/ColorPalette.tsx @@ -3,7 +3,7 @@ import { styled } from '@storybook/theming'; import { transparentize } from 'polished'; import { getBlockBackgroundStyle } from './BlockBackgroundStyles'; -import { ResetWrapper } from '../typography/DocumentFormatting'; +import { ResetWrapper } from '../typography/ResetWrapper'; const ItemTitle = styled.div(({ theme }) => ({ fontWeight: theme.typography.weight.bold, diff --git a/lib/components/src/blocks/Description.tsx b/lib/components/src/blocks/Description.tsx index d5b57f2b6d7d..7692118bc55a 100644 --- a/lib/components/src/blocks/Description.tsx +++ b/lib/components/src/blocks/Description.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent } from 'react'; import Markdown from 'markdown-to-jsx'; -import { ResetWrapper } from '../typography/DocumentFormatting'; +import { ResetWrapper } from '../typography/ResetWrapper'; import { components } from '..'; export interface DescriptionProps { diff --git a/lib/components/src/blocks/DocsPage.tsx b/lib/components/src/blocks/DocsPage.tsx index 2ef0df238460..24bf4503de20 100644 --- a/lib/components/src/blocks/DocsPage.tsx +++ b/lib/components/src/blocks/DocsPage.tsx @@ -2,7 +2,7 @@ import React, { FC } from 'react'; import { styled, Theme } from '@storybook/theming'; import { transparentize } from 'polished'; -import { withReset } from '../typography/shared'; +import { withReset } from '../typography/lib/common'; const breakpoint = 600; diff --git a/lib/components/src/blocks/EmptyBlock.tsx b/lib/components/src/blocks/EmptyBlock.tsx index 06f7ba536e9b..876a1fa41a70 100644 --- a/lib/components/src/blocks/EmptyBlock.tsx +++ b/lib/components/src/blocks/EmptyBlock.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { styled } from '@storybook/theming'; import { transparentize } from 'polished'; -import { withReset } from '../typography/shared'; +import { withReset } from '../typography/lib/common'; const Wrapper = styled.div<{}>(withReset, ({ theme }) => ({ backgroundColor: theme.base === 'light' ? 'rgba(0,0,0,.01)' : 'rgba(255,255,255,.01)', diff --git a/lib/components/src/blocks/IconGallery.tsx b/lib/components/src/blocks/IconGallery.tsx index 01d4f11f5f50..7424eb235f72 100644 --- a/lib/components/src/blocks/IconGallery.tsx +++ b/lib/components/src/blocks/IconGallery.tsx @@ -1,6 +1,6 @@ import React, { FunctionComponent } from 'react'; import { styled } from '@storybook/theming'; -import { ResetWrapper } from '../typography/DocumentFormatting'; +import { ResetWrapper } from '../typography/ResetWrapper'; import { getBlockBackgroundStyle } from './BlockBackgroundStyles'; diff --git a/lib/components/src/blocks/Typeset.tsx b/lib/components/src/blocks/Typeset.tsx index c4f3f26ab3df..cb95c6b7d956 100644 --- a/lib/components/src/blocks/Typeset.tsx +++ b/lib/components/src/blocks/Typeset.tsx @@ -2,7 +2,7 @@ import React, { FunctionComponent } from 'react'; import { styled } from '@storybook/theming'; import { transparentize } from 'polished'; -import { withReset } from '../typography/shared'; +import { withReset } from '../typography/lib/common'; import { getBlockBackgroundStyle } from './BlockBackgroundStyles'; const Label = styled.div<{}>(({ theme }) => ({ diff --git a/lib/components/src/controls/Object.tsx b/lib/components/src/controls/Object.tsx index 069a19306fe8..83670dd61f3d 100644 --- a/lib/components/src/controls/Object.tsx +++ b/lib/components/src/controls/Object.tsx @@ -11,7 +11,6 @@ import React, { } from 'react'; import { styled, useTheme, Theme } from '@storybook/theming'; -// @ts-ignore import { JsonTree, getObjectType } from './react-editable-json-tree'; import { getControlId, getControlSetterButtonId } from './helpers'; import type { ControlProps, ObjectValue, ObjectConfig } from './types'; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx index 55971aafb60f..4619577f7316 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx @@ -1,10 +1,15 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +/* eslint-disable react/button-has-type */ +import React, { Component, ReactElement } from 'react'; -import inputUsageTypes from '../types/inputUsageTypes'; +import * as inputUsageTypes from '../types/inputUsageTypes'; -class JsonAddValue extends Component { - constructor(props) { +interface JsonAddValueState { + inputRefKey: any; + inputRefValue: any; +} + +export class JsonAddValue extends Component { + constructor(props: JsonAddValueProps) { super(props); this.state = { inputRefKey: null, @@ -36,7 +41,7 @@ class JsonAddValue extends Component { document.removeEventListener('keydown', this.onKeydown); } - onKeydown(event) { + onKeydown(event: KeyboardEvent) { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; if (event.code === 'Enter' || event.key === 'Enter') { event.preventDefault(); @@ -51,7 +56,7 @@ class JsonAddValue extends Component { onSubmit() { const { handleAdd, onlyValue, onSubmitValueParser, keyPath, deep } = this.props; const { inputRefKey, inputRefValue } = this.state; - const result = {}; + const result: any = {}; // Check if we have the key if (!onlyValue) { // Check that there is a key @@ -66,11 +71,13 @@ class JsonAddValue extends Component { handleAdd(result); } - refInputKey(node) { + refInputKey(node: any) { + // @ts-ignore this.state.inputRefKey = node; } refInputValue(node) { + // @ts-ignore this.state.inputRefValue = node; } @@ -116,22 +123,21 @@ class JsonAddValue extends Component { } } -JsonAddValue.propTypes = { - handleAdd: PropTypes.func.isRequired, - handleCancel: PropTypes.func.isRequired, - onlyValue: PropTypes.bool, - addButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - inputElementGenerator: PropTypes.func.isRequired, - keyPath: PropTypes.array, - deep: PropTypes.number, - onSubmitValueParser: PropTypes.func.isRequired, -}; +interface JsonAddValueProps { + handleAdd: (...args: any) => any; + handleCancel: (...args: any) => any; + onlyValue?: boolean; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + keyPath?: string[]; + deep?: number; + onSubmitValueParser: (...args: any) => any; +} +// @ts-ignore JsonAddValue.defaultProps = { onlyValue: false, addButtonElement: , cancelButtonElement: , }; - -export default JsonAddValue; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx index 61bf8240aa2f..7ee72c93fa0a 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx @@ -1,12 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +/* eslint-disable react/no-array-index-key */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import React, { Component, ReactElement } from 'react'; -import JsonNode from './JsonNode'; -import JsonAddValue from './JsonAddValue'; +import { JsonNode } from './JsonNode'; +import { JsonAddValue } from './JsonAddValue'; import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from '../types/deltaTypes'; -class JsonArray extends Component { - constructor(props) { +interface JsonArrayState { + data: JsonArrayProps['data']; + name: JsonArrayProps['name']; + keyPath: string[]; + deep: JsonArrayProps['deep']; + nextDeep: JsonArrayProps['deep']; + collapsed: any; + addFormVisible: boolean; +} +export class JsonArray extends Component { + constructor(props: JsonArrayProps) { super(props); const keyPath = [...props.keyPath, props.name]; this.state = { @@ -31,13 +41,14 @@ class JsonArray extends Component { this.renderNotCollapsed = this.renderNotCollapsed.bind(this); } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonArrayProps, state: JsonArrayState) { return props.data !== state.data ? { data: props.data } : null; } - onChildUpdate(childKey, childData) { + onChildUpdate(childKey: string, childData: any) { const { data, keyPath } = this.state; // Update data + // @ts-ignore data[childKey] = childData; // Put new data this.setState({ @@ -61,7 +72,7 @@ class JsonArray extends Component { })); } - handleRemoveItem(index) { + handleRemoveItem(index: number) { return () => { const { beforeRemoveAction, logger } = this.props; const { data, keyPath, nextDeep: deep } = this.state; @@ -91,7 +102,7 @@ class JsonArray extends Component { }; } - handleAddValueAdd({ newValue }) { + handleAddValueAdd({ newValue }: any) { const { data, keyPath, nextDeep: deep } = this.state; const { beforeAddAction, logger } = this.props; @@ -125,7 +136,7 @@ class JsonArray extends Component { }); } - handleEditValue({ key, value }) { + handleEditValue({ key, value }: any) { return new Promise((resolve, reject) => { const { beforeUpdateAction } = this.props; const { data, keyPath, nextDeep: deep } = this.state; @@ -155,7 +166,7 @@ class JsonArray extends Component { oldValue, }); // Resolve - resolve(); + resolve(undefined); }) .catch(reject); }); @@ -306,37 +317,36 @@ class JsonArray extends Component { } } -JsonArray.propTypes = { - data: PropTypes.array.isRequired, - name: PropTypes.string.isRequired, - isCollapsed: PropTypes.func.isRequired, - keyPath: PropTypes.array, - deep: PropTypes.number, - handleRemove: PropTypes.func, - onUpdate: PropTypes.func.isRequired, - onDeltaUpdate: PropTypes.func.isRequired, - readOnly: PropTypes.func.isRequired, - dataType: PropTypes.string, - getStyle: PropTypes.func.isRequired, - addButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - editButtonElement: PropTypes.element, - inputElementGenerator: PropTypes.func.isRequired, - textareaElementGenerator: PropTypes.func.isRequired, - minusMenuElement: PropTypes.element, - plusMenuElement: PropTypes.element, - beforeRemoveAction: PropTypes.func, - beforeAddAction: PropTypes.func, - beforeUpdateAction: PropTypes.func, - logger: PropTypes.object.isRequired, - onSubmitValueParser: PropTypes.func.isRequired, -}; +interface JsonArrayProps { + data: any[]; + name: string; + isCollapsed: (...args: any) => any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: any; + onSubmitValueParser: (...args: any) => any; +} +// @ts-ignore JsonArray.defaultProps = { keyPath: [], deep: 0, minusMenuElement: - , plusMenuElement: + , }; - -export default JsonArray; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx index ae205834ee73..9f7df7a4b9a2 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx @@ -1,11 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +/* eslint-disable react/sort-comp */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/button-has-type */ +import React, { Component, ReactElement } from 'react'; import { isComponentWillChange } from '../utils/objectTypes'; -import inputUsageTypes from '../types/inputUsageTypes'; +import * as inputUsageTypes from '../types/inputUsageTypes'; + +interface JsonFunctionValueState { + value: JsonFunctionValueProps['value']; + name: JsonFunctionValueProps['name']; + keyPath: string[]; + deep: JsonFunctionValueProps['deep']; + editEnabled: boolean; + inputRef: any; +} -class JsonFunctionValue extends Component { - constructor(props) { +export class JsonFunctionValue extends Component { + constructor(props: JsonFunctionValueProps) { super(props); const keyPath = [...props.keyPath, props.name]; this.state = { @@ -25,7 +36,7 @@ class JsonFunctionValue extends Component { this.onKeydown = this.onKeydown.bind(this); } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonFunctionValueProps, state: JsonFunctionValueState) { return props.value !== state.value ? { value: props.value } : null; } @@ -47,7 +58,7 @@ class JsonFunctionValue extends Component { document.removeEventListener('keydown', this.onKeydown); } - onKeydown(event) { + onKeydown(event: KeyboardEvent) { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; if (event.code === 'Enter' || event.key === 'Enter') { event.preventDefault(); @@ -88,7 +99,8 @@ class JsonFunctionValue extends Component { }); } - refInput(node) { + refInput(node: any) { + // @ts-ignore this.state.inputRef = node; } @@ -178,25 +190,26 @@ class JsonFunctionValue extends Component { } } -JsonFunctionValue.propTypes = { - name: PropTypes.string.isRequired, - value: PropTypes.any.isRequired, - originalValue: PropTypes.any, - keyPath: PropTypes.array, - deep: PropTypes.number, - handleRemove: PropTypes.func, - handleUpdateValue: PropTypes.func, - readOnly: PropTypes.func.isRequired, - dataType: PropTypes.string, - getStyle: PropTypes.func.isRequired, - editButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - textareaElementGenerator: PropTypes.func.isRequired, - minusMenuElement: PropTypes.element, - logger: PropTypes.object.isRequired, - onSubmitValueParser: PropTypes.func.isRequired, -}; +interface JsonFunctionValueProps { + name: string; + value: any; + originalValue?: any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + editButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + logger: any; + onSubmitValueParser: (...args: any) => any; +} +// @ts-ignore JsonFunctionValue.defaultProps = { keyPath: [], deep: 0, @@ -205,5 +218,3 @@ JsonFunctionValue.defaultProps = { cancelButtonElement: , minusMenuElement: - , }; - -export default JsonFunctionValue; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx index ca46745edc6c..618a380f883b 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx @@ -1,15 +1,21 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component, ReactElement } from 'react'; -import JsonValue from './JsonValue'; -import JsonObject from './JsonObject'; -import JsonArray from './JsonArray'; -import JsonFunctionValue from './JsonFunctionValue'; +import { JsonValue } from './JsonValue'; +import { JsonObject } from './JsonObject'; +import { JsonArray } from './JsonArray'; +import { JsonFunctionValue } from './JsonFunctionValue'; import { getObjectType } from '../utils/objectTypes'; -import dataTypes from '../types/dataTypes'; +import * as dataTypes from '../types/dataTypes'; -class JsonNode extends Component { - constructor(props) { +interface JsonNodeState { + data: JsonNodeProps['data']; + name: JsonNodeProps['name']; + keyPath: JsonNodeProps['keyPath']; + deep: JsonNodeProps['deep']; +} + +export class JsonNode extends Component { + constructor(props: JsonNodeProps) { super(props); this.state = { data: props.data, @@ -19,7 +25,7 @@ class JsonNode extends Component { }; } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonNodeProps, state: JsonNodeState) { return props.data !== state.data ? { data: props.data } : null; } @@ -222,8 +228,8 @@ class JsonNode extends Component { return ( any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: object; + onSubmitValueParser: (...args: any) => any; +} +/// @ts-ignore JsonNode.defaultProps = { keyPath: [], deep: 0, }; - -export default JsonNode; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx index 7e3c298e97d8..c2c51c03a9dc 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx @@ -1,12 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +/* eslint-disable jsx-a11y/click-events-have-key-events */ +import React, { Component, ReactElement } from 'react'; -import JsonNode from './JsonNode'; -import JsonAddValue from './JsonAddValue'; +import { JsonNode } from './JsonNode'; +import { JsonAddValue } from './JsonAddValue'; import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from '../types/deltaTypes'; -class JsonObject extends Component { - constructor(props) { +interface JsonObjectState { + name: string; + collapsed: ReturnType; + data: JsonObjectProps['data']; + keyPath: JsonObjectProps['keyPath']; + deep: JsonObjectProps['deep']; + nextDeep: number; + addFormVisible: boolean; +} + +export class JsonObject extends Component { + constructor(props: JsonObjectProps) { super(props); const keyPath = props.deep === -1 ? [] : [...props.keyPath, props.name]; this.state = { @@ -31,13 +41,14 @@ class JsonObject extends Component { this.renderNotCollapsed = this.renderNotCollapsed.bind(this); } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonObjectProps, state: JsonObjectState) { return props.data !== state.data ? { data: props.data } : null; } - onChildUpdate(childKey, childData) { + onChildUpdate(childKey: string, childData: any) { const { data, keyPath } = this.state; // Update data + // @ts-ignore data[childKey] = childData; // Put new data this.setState({ @@ -61,13 +72,14 @@ class JsonObject extends Component { }); } - handleAddValueAdd({ key, newValue }) { + handleAddValueAdd({ key, newValue }: any) { const { data, keyPath, nextDeep: deep } = this.state; const { beforeAddAction, logger } = this.props; beforeAddAction(key, keyPath, deep, newValue) .then(() => { // Update data + // @ts-ignore data[key] = newValue; this.setState({ data, @@ -89,10 +101,11 @@ class JsonObject extends Component { .catch(logger.error); } - handleRemoveValue(key) { + handleRemoveValue(key: string) { return () => { const { beforeRemoveAction, logger } = this.props; const { data, keyPath, nextDeep: deep } = this.state; + // @ts-ignore const oldValue = data[key]; // Before Remove Action beforeRemoveAction(key, keyPath, deep, oldValue) @@ -105,6 +118,7 @@ class JsonObject extends Component { type: REMOVE_DELTA_TYPE, }; + // @ts-ignore delete data[key]; this.setState({ data }); @@ -124,18 +138,20 @@ class JsonObject extends Component { })); } - handleEditValue({ key, value }) { - return new Promise((resolve, reject) => { + handleEditValue({ key, value }: any) { + return new Promise((resolve, reject) => { const { beforeUpdateAction } = this.props; const { data, keyPath, nextDeep: deep } = this.state; // Old value + // @ts-ignore const oldValue = data[key]; // Before update action beforeUpdateAction(key, keyPath, deep, oldValue, value) .then(() => { // Update value + // @ts-ignore data[key] = value; // Set state this.setState({ @@ -310,37 +326,36 @@ class JsonObject extends Component { } } -JsonObject.propTypes = { - data: PropTypes.object.isRequired, - name: PropTypes.string.isRequired, - isCollapsed: PropTypes.func.isRequired, - keyPath: PropTypes.array, - deep: PropTypes.number, - handleRemove: PropTypes.func, - onUpdate: PropTypes.func.isRequired, - onDeltaUpdate: PropTypes.func.isRequired, - readOnly: PropTypes.func.isRequired, - dataType: PropTypes.string, - getStyle: PropTypes.func.isRequired, - addButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - editButtonElement: PropTypes.element, - inputElementGenerator: PropTypes.func.isRequired, - textareaElementGenerator: PropTypes.func.isRequired, - minusMenuElement: PropTypes.element, - plusMenuElement: PropTypes.element, - beforeRemoveAction: PropTypes.func, - beforeAddAction: PropTypes.func, - beforeUpdateAction: PropTypes.func, - logger: PropTypes.object.isRequired, - onSubmitValueParser: PropTypes.func.isRequired, -}; +interface JsonObjectProps { + data: Record; + name: string; + isCollapsed: (...args: any) => any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: any; + onSubmitValueParser: (...args: any) => any; +} +// @ts-ignore JsonObject.defaultProps = { keyPath: [], deep: 0, minusMenuElement: - , plusMenuElement: + , }; - -export default JsonObject; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx index f2bf4dcd056f..4f271b63b0a0 100644 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx +++ b/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx @@ -1,11 +1,22 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/sort-comp */ +/* eslint-disable react/button-has-type */ +import React, { Component, ReactElement } from 'react'; import { isComponentWillChange } from '../utils/objectTypes'; -import inputUsageTypes from '../types/inputUsageTypes'; +import * as inputUsageTypes from '../types/inputUsageTypes'; + +interface JsonValueState { + value: JsonValueProps['value']; + name: JsonValueProps['name']; + keyPath: string[]; + deep: JsonValueProps['deep']; + editEnabled: boolean; + inputRef: any; +} -class JsonValue extends Component { - constructor(props) { +export class JsonValue extends Component { + constructor(props: JsonValueProps) { super(props); const keyPath = [...props.keyPath, props.name]; this.state = { @@ -25,7 +36,7 @@ class JsonValue extends Component { this.onKeydown = this.onKeydown.bind(this); } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonValueProps, state: JsonValueState) { return props.value !== state.value ? { value: props.value } : null; } @@ -47,7 +58,7 @@ class JsonValue extends Component { document.removeEventListener('keydown', this.onKeydown); } - onKeydown(event) { + onKeydown(event: KeyboardEvent) { if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; if (event.code === 'Enter' || event.key === 'Enter') { event.preventDefault(); @@ -88,7 +99,8 @@ class JsonValue extends Component { }); } - refInput(node) { + refInput(node: any) { + // @ts-ignore this.state.inputRef = node; } @@ -167,25 +179,26 @@ class JsonValue extends Component { } } -JsonValue.propTypes = { - name: PropTypes.string.isRequired, - value: PropTypes.any.isRequired, - originalValue: PropTypes.any, - keyPath: PropTypes.array, - deep: PropTypes.number, - handleRemove: PropTypes.func, - handleUpdateValue: PropTypes.func, - readOnly: PropTypes.func.isRequired, - dataType: PropTypes.string, - getStyle: PropTypes.func.isRequired, - editButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - inputElementGenerator: PropTypes.func.isRequired, - minusMenuElement: PropTypes.element, - logger: PropTypes.object.isRequired, - onSubmitValueParser: PropTypes.func.isRequired, -}; +interface JsonValueProps { + name: string; + value: any; + originalValue?: any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + editButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + logger: any; + onSubmitValueParser: (...args: any) => any; +} +// @ts-ignore JsonValue.defaultProps = { keyPath: [], deep: 0, @@ -194,5 +207,3 @@ JsonValue.defaultProps = { cancelButtonElement: , minusMenuElement: - , }; - -export default JsonValue; diff --git a/lib/components/src/controls/react-editable-json-tree/index.tsx b/lib/components/src/controls/react-editable-json-tree/index.tsx index b8f2ee91c4be..6c6a43c6bd4d 100644 --- a/lib/components/src/controls/react-editable-json-tree/index.tsx +++ b/lib/components/src/controls/react-editable-json-tree/index.tsx @@ -9,7 +9,7 @@ import DATA_TYPES from './types/dataTypes'; import INPUT_USAGE_TYPES from './types/inputUsageTypes'; import { parse } from './utils/parse'; -class JsonTree extends Component { +export class JsonTree extends Component { constructor(props) { super(props); this.state = { @@ -165,10 +165,11 @@ JsonTree.defaultProps = { fallback: null, }; -export { JsonTree }; -export { getObjectType }; -export { ADD_DELTA_TYPE }; -export { REMOVE_DELTA_TYPE }; -export { UPDATE_DELTA_TYPE }; -export { DATA_TYPES }; -export { INPUT_USAGE_TYPES }; +export { + getObjectType, + ADD_DELTA_TYPE, + REMOVE_DELTA_TYPE, + UPDATE_DELTA_TYPE, + DATA_TYPES, + INPUT_USAGE_TYPES, +}; diff --git a/lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts b/lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts index dc68e5aa25c6..9bb7e1a7dc03 100644 --- a/lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts +++ b/lib/components/src/controls/react-editable-json-tree/types/dataTypes.ts @@ -10,16 +10,4 @@ const UNDEFINED = 'Undefined'; const FUNCTION = 'Function'; const SYMBOL = 'Symbol'; -export default { - ERROR, - OBJECT, - ARRAY, - STRING, - NUMBER, - BOOLEAN, - DATE, - NULL, - UNDEFINED, - FUNCTION, - SYMBOL, -}; +export { ERROR, OBJECT, ARRAY, STRING, NUMBER, BOOLEAN, DATE, NULL, UNDEFINED, FUNCTION, SYMBOL }; diff --git a/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts b/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts index 5926294bc87a..13dcd1aef53a 100644 --- a/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts +++ b/lib/components/src/controls/react-editable-json-tree/types/deltaTypes.ts @@ -2,6 +2,4 @@ const ADD_DELTA_TYPE = 'ADD_DELTA_TYPE'; const REMOVE_DELTA_TYPE = 'REMOVE_DELTA_TYPE'; const UPDATE_DELTA_TYPE = 'UPDATE_DELTA_TYPE'; -export { ADD_DELTA_TYPE }; -export { REMOVE_DELTA_TYPE }; -export { UPDATE_DELTA_TYPE }; +export { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE }; diff --git a/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts b/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts index db9afca8982c..8430b82e21c9 100644 --- a/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts +++ b/lib/components/src/controls/react-editable-json-tree/types/inputUsageTypes.ts @@ -1,7 +1,4 @@ const VALUE = 'value'; const KEY = 'key'; -export default { - KEY, - VALUE, -}; +export { KEY, VALUE }; diff --git a/lib/components/src/index.ts b/lib/components/src/index.ts index 54d8578b4aec..4e538a6ef770 100644 --- a/lib/components/src/index.ts +++ b/lib/components/src/index.ts @@ -2,7 +2,7 @@ /// import { createElement, forwardRef, ElementType } from 'react'; -import { components as rawComponents } from './typography/DocumentFormatting'; +import { components as rawComponents } from './typography/components'; export { Badge } from './Badge/Badge'; diff --git a/lib/components/src/typography/DocumentFormatting.tsx b/lib/components/src/typography/DocumentFormatting.tsx index 1bfb805e3b94..b6dd2892b343 100644 --- a/lib/components/src/typography/DocumentFormatting.tsx +++ b/lib/components/src/typography/DocumentFormatting.tsx @@ -1,384 +1,4 @@ -import React, { FunctionComponent, ReactNode } from 'react'; -import { styled, CSSObject } from '@storybook/theming'; -import { withReset, withMargin, headerCommon, codeCommon } from './shared'; -import { StyledSyntaxHighlighter } from '../blocks/Source'; - -export const H1 = styled.h1<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.l1}px`, - fontWeight: theme.typography.weight.black, -})); - -export const H2 = styled.h2<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.m2}px`, - paddingBottom: 4, - borderBottom: `1px solid ${theme.appBorderColor}`, -})); - -export const H3 = styled.h3<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.m1}px`, -})); - -export const H4 = styled.h4<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.s3}px`, -})); - -export const H5 = styled.h5<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.s2}px`, -})); - -export const H6 = styled.h6<{}>(withReset, headerCommon, ({ theme }) => ({ - fontSize: `${theme.typography.size.s2}px`, - color: theme.color.dark, -})); - -export const Pre = styled.pre<{}>(withReset, withMargin, ({ theme }) => ({ - // reset - fontFamily: theme.typography.fonts.mono, - WebkitFontSmoothing: 'antialiased', - MozOsxFontSmoothing: 'grayscale', - lineHeight: '18px', - padding: '11px 1rem', - whiteSpace: 'pre-wrap', - color: 'inherit', - borderRadius: 3, - margin: '1rem 0', - - '&:not(.prismjs)': { - background: 'transparent', - border: 'none', - borderRadius: 0, - padding: 0, - margin: 0, - }, - '& pre, &.prismjs': { - padding: 15, - margin: 0, - whiteSpace: 'pre-wrap', - color: 'inherit', - fontSize: '13px', - lineHeight: '19px', - code: { - color: 'inherit', - fontSize: 'inherit', - }, - }, - '& code': { - whiteSpace: 'pre', - }, - '& code, & tt': { - border: 'none', - }, -})); - -const Link: FunctionComponent = ({ href: input, children, ...props }) => { - const isStorybookPath = /^\//.test(input); - const isAnchorUrl = /^#.*/.test(input); - const href = isStorybookPath ? `?path=${input}` : input; - const target = isAnchorUrl ? '_self' : '_top'; - - return ( - - {children} - - ); -}; - -export const A = styled(Link)<{}>(withReset, ({ theme }) => ({ - fontSize: 'inherit', - lineHeight: '24px', - - color: theme.color.secondary, - textDecoration: 'none', - '&.absent': { - color: '#cc0000', - }, - '&.anchor': { - display: 'block', - paddingLeft: 30, - marginLeft: -30, - cursor: 'pointer', - position: 'absolute', - top: 0, - left: 0, - bottom: 0, - }, -})); - -export const HR = styled.hr<{}>(({ theme }) => ({ - border: '0 none', - borderTop: `1px solid ${theme.appBorderColor}`, - height: 4, - padding: 0, -})); - -export const DL = styled.dl<{}>(withReset, { - ...withMargin, - padding: 0, - '& dt': { - fontSize: '14px', - fontWeight: 'bold', - fontStyle: 'italic', - padding: 0, - margin: '16px 0 4px', - }, - '& dt:first-of-type': { - padding: 0, - }, - '& dt > :first-of-type': { - marginTop: 0, - }, - - '& dt > :last-child': { - marginBottom: 0, - }, - - '& dd': { - margin: '0 0 16px', - padding: '0 15px', - }, - - '& dd > :first-of-type': { - marginTop: 0, - }, - - '& dd > :last-child': { - marginBottom: 0, - }, -}); - -export const Blockquote = styled.blockquote<{}>(withReset, withMargin, ({ theme }) => ({ - borderLeft: `4px solid ${theme.color.medium}`, - padding: '0 15px', - color: theme.color.dark, - '& > :first-of-type': { - marginTop: 0, - }, - '& > :last-child': { - marginBottom: 0, - }, -})); - -export const Table = styled.table<{}>(withReset, withMargin, ({ theme }) => ({ - fontSize: theme.typography.size.s2, - lineHeight: '24px', - padding: 0, - borderCollapse: 'collapse', - '& tr': { - borderTop: `1px solid ${theme.appBorderColor}`, - backgroundColor: theme.appContentBg, - margin: 0, - padding: 0, - }, - '& tr:nth-of-type(2n)': { - backgroundColor: theme.base === 'dark' ? theme.color.darker : theme.color.lighter, - }, - '& tr th': { - fontWeight: 'bold', - color: theme.color.defaultText, - border: `1px solid ${theme.appBorderColor}`, - margin: 0, - padding: '6px 13px', - }, - '& tr td': { - border: `1px solid ${theme.appBorderColor}`, - color: theme.color.defaultText, - margin: 0, - padding: '6px 13px', - }, - '& tr th :first-of-type, & tr td :first-of-type': { - marginTop: 0, - }, - '& tr th :last-child, & tr td :last-child': { - marginBottom: 0, - }, -})); - -export const Img = styled.img<{}>({ - maxWidth: '100%', -}); - -export const Div = styled.div<{}>(withReset); - -export const Span = styled.span<{}>(withReset, ({ theme }) => ({ - '&.frame': { - display: 'block', - overflow: 'hidden', - - '& > span': { - border: `1px solid ${theme.color.medium}`, - display: 'block', - float: 'left', - overflow: 'hidden', - margin: '13px 0 0', - padding: 7, - width: 'auto', - }, - '& span img': { - display: 'block', - float: 'left', - }, - '& span span': { - clear: 'both', - color: theme.color.darkest, - display: 'block', - padding: '5px 0 0', - }, - }, - '&.align-center': { - display: 'block', - overflow: 'hidden', - clear: 'both', - - '& > span': { - display: 'block', - overflow: 'hidden', - margin: '13px auto 0', - textAlign: 'center', - }, - '& span img': { - margin: '0 auto', - textAlign: 'center', - }, - }, - '&.align-right': { - display: 'block', - overflow: 'hidden', - clear: 'both', - - '& > span': { - display: 'block', - overflow: 'hidden', - margin: '13px 0 0', - textAlign: 'right', - }, - '& span img': { - margin: 0, - textAlign: 'right', - }, - }, - '&.float-left': { - display: 'block', - marginRight: 13, - overflow: 'hidden', - float: 'left', - '& span': { - margin: '13px 0 0', - }, - }, - '&.float-right': { - display: 'block', - marginLeft: 13, - overflow: 'hidden', - float: 'right', - - '& > span': { - display: 'block', - overflow: 'hidden', - margin: '13px auto 0', - textAlign: 'right', - }, - }, -})); - -const listCommon: CSSObject = { - paddingLeft: 30, - '& :first-of-type': { - marginTop: 0, - }, - '& :last-child': { - marginBottom: 0, - }, -}; - -export const LI = styled.li<{}>(withReset, ({ theme }) => ({ - fontSize: theme.typography.size.s2, - color: theme.color.defaultText, - lineHeight: '24px', - '& + li': { - marginTop: '.25em', - }, - '& ul, & ol': { - marginTop: '.25em', - marginBottom: 0, - }, - '& code': codeCommon({ theme }), -})); - -export const UL = styled.ul<{}>(withReset, withMargin, { ...listCommon, listStyle: 'disc' }); - -export const OL = styled.ol<{}>(withReset, withMargin, { ...listCommon, listStyle: 'decimal' }); - -export const P = styled.p<{}>(withReset, withMargin, ({ theme }) => ({ - fontSize: theme.typography.size.s2, - lineHeight: '24px', - color: theme.color.defaultText, - '& code': codeCommon({ theme }), -})); - -const DefaultCodeBlock = styled.code<{}>( - ({ theme }) => ({ - // from reset - fontFamily: theme.typography.fonts.mono, - WebkitFontSmoothing: 'antialiased', - MozOsxFontSmoothing: 'grayscale', - display: 'inline-block', - paddingLeft: 2, - paddingRight: 2, - verticalAlign: 'baseline', - color: 'inherit', - }), - codeCommon -); - -const isInlineCodeRegex = /[\n\r]/g; - -const isReactChildString = (child: ReactNode): child is string => typeof child === 'string'; - -export const Code = ({ - className, - children, - ...props -}: React.ComponentProps) => { - const language = (className || '').match(/lang-(\S+)/); - const childrenArray = React.Children.toArray(children); - const isInlineCode = !childrenArray - .filter(isReactChildString) - .some((child) => child.match(isInlineCodeRegex)); - - if (isInlineCode) { - return ( - - {childrenArray} - - ); - } - - return ( - - {children} - - ); -}; - -export const TT = styled.title<{}>(codeCommon); - -/** - * This is a "local" reset to style subtrees with Storybook styles - * - * We can't style individual elements (e.g. h1, h2, etc.) in here - * because the CSS specificity is too high, so those styles can too - * easily override child elements that are not expecting it. - */ - -export const ResetWrapper = styled.div<{}>(withReset); - -const nameSpaceClassNames = ({ ...props }, key: string) => { +export const nameSpaceClassNames = ({ ...props }, key: string) => { const classes = [props.class, props.className]; // eslint-disable-next-line no-param-reassign delete props.class; @@ -388,32 +8,3 @@ const nameSpaceClassNames = ({ ...props }, key: string) => { return props; }; - -export const components = { - h1: ((props) =>

) as typeof H1, - h2: ((props) =>

) as typeof H2, - h3: ((props) =>

) as typeof H3, - h4: ((props) =>

) as typeof H4, - h5: ((props) =>

) as typeof H5, - h6: ((props) =>
) as typeof H6, - pre: ((props) =>
) as typeof Pre,
-  a: ((props) => ) as typeof A,
-  hr: ((props) => 
) as typeof HR, - dl: ((props) =>
) as typeof DL, - blockquote: ((props) => ( -
- )) as typeof Blockquote, - table: ((props) => ) as typeof Table, - img: ((props) => ) as typeof Img, - div: ((props) =>
) as typeof Div, - span: ((props) => ) as typeof Span, - li: ((props) =>
  • ) as typeof LI, - ul: ((props) =>
      ) as typeof UL, - ol: ((props) =>
        ) as typeof OL, - p: ((props) =>

        ) as typeof P, - code: ((props) => ) as typeof Code, - tt: ((props) => ) as typeof TT, - resetwrapper: ((props) => ( - - )) as typeof ResetWrapper, -}; diff --git a/lib/components/src/typography/ResetWrapper.tsx b/lib/components/src/typography/ResetWrapper.tsx new file mode 100644 index 000000000000..84432f4f4534 --- /dev/null +++ b/lib/components/src/typography/ResetWrapper.tsx @@ -0,0 +1,12 @@ +import { styled } from '@storybook/theming'; +import { withReset } from './lib/common'; + +/** + * This is a "local" reset to style subtrees with Storybook styles + * + * We can't style individual elements (e.g. h1, h2, etc.) in here + * because the CSS specificity is too high, so those styles can too + * easily override child elements that are not expecting it. + */ + +export const ResetWrapper = styled.div<{}>(withReset); diff --git a/lib/components/src/typography/components.tsx b/lib/components/src/typography/components.tsx new file mode 100644 index 000000000000..78c289e82cda --- /dev/null +++ b/lib/components/src/typography/components.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { nameSpaceClassNames } from './DocumentFormatting'; +import { A } from './elements/A'; +import { Blockquote } from './elements/Blockquote'; +import { Code } from './elements/Code'; +import { Div } from './elements/Div'; +import { DL } from './elements/DL'; +import { H1 } from './elements/H1'; +import { H2 } from './elements/H2'; +import { H3 } from './elements/H3'; +import { H4 } from './elements/H4'; +import { H5 } from './elements/H5'; +import { H6 } from './elements/H6'; +import { HR } from './elements/HR'; +import { Img } from './elements/Img'; +import { LI } from './elements/LI'; +import { OL } from './elements/OL'; +import { P } from './elements/P'; +import { Pre } from './elements/Pre'; +import { Span } from './elements/Span'; +import { Table } from './elements/Table'; +import { TT } from './elements/TT'; +import { UL } from './elements/UL'; +import { ResetWrapper } from './ResetWrapper'; + +export const components = { + h1: ((props) =>

        ) as typeof H1, + h2: ((props) =>

        ) as typeof H2, + h3: ((props) =>

        ) as typeof H3, + h4: ((props) =>

        ) as typeof H4, + h5: ((props) =>

        ) as typeof H5, + h6: ((props) =>
        ) as typeof H6, + pre: ((props) =>
        ) as typeof Pre,
        +  a: ((props) => ) as typeof A,
        +  hr: ((props) => 
        ) as typeof HR, + dl: ((props) =>
        ) as typeof DL, + blockquote: ((props) => ( +
        + )) as typeof Blockquote, + table: ((props) =>

  • ) as typeof Table, + img: ((props) => ) as typeof Img, + div: ((props) =>
    ) as typeof Div, + span: ((props) => ) as typeof Span, + li: ((props) =>
  • ) as typeof LI, + ul: ((props) =>
      ) as typeof UL, + ol: ((props) =>
        ) as typeof OL, + p: ((props) =>

        ) as typeof P, + code: ((props) => ) as typeof Code, + tt: ((props) => ) as typeof TT, + resetwrapper: ((props) => ( + + )) as typeof ResetWrapper, +}; diff --git a/lib/components/src/typography/elements/A.tsx b/lib/components/src/typography/elements/A.tsx new file mode 100644 index 000000000000..2d3d73e4088f --- /dev/null +++ b/lib/components/src/typography/elements/A.tsx @@ -0,0 +1,24 @@ +import { styled } from '@storybook/theming'; +import { withReset } from '../lib/common'; +import { Link } from './Link'; + +export const A = styled(Link)<{}>(withReset, ({ theme }) => ({ + fontSize: 'inherit', + lineHeight: '24px', + + color: theme.color.secondary, + textDecoration: 'none', + '&.absent': { + color: '#cc0000', + }, + '&.anchor': { + display: 'block', + paddingLeft: 30, + marginLeft: -30, + cursor: 'pointer', + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + }, +})); diff --git a/lib/components/src/typography/elements/Blockquote.tsx b/lib/components/src/typography/elements/Blockquote.tsx new file mode 100644 index 000000000000..4dbc744f30ea --- /dev/null +++ b/lib/components/src/typography/elements/Blockquote.tsx @@ -0,0 +1,14 @@ +import { styled } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +export const Blockquote = styled.blockquote<{}>(withReset, withMargin, ({ theme }) => ({ + borderLeft: `4px solid ${theme.color.medium}`, + padding: '0 15px', + color: theme.color.dark, + '& > :first-of-type': { + marginTop: 0, + }, + '& > :last-child': { + marginBottom: 0, + }, +})); diff --git a/lib/components/src/typography/elements/Code.tsx b/lib/components/src/typography/elements/Code.tsx new file mode 100644 index 000000000000..c1007c58728f --- /dev/null +++ b/lib/components/src/typography/elements/Code.tsx @@ -0,0 +1,54 @@ +import { styled } from '@storybook/theming'; +import React from 'react'; +import { StyledSyntaxHighlighter } from '../../blocks/Source'; +import { isReactChildString } from '../lib/isReactChildString'; +import { codeCommon } from '../lib/common'; + +const isInlineCodeRegex = /[\n\r]/g; + +const DefaultCodeBlock = styled.code<{}>( + ({ theme }) => ({ + // from reset + fontFamily: theme.typography.fonts.mono, + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + display: 'inline-block', + paddingLeft: 2, + paddingRight: 2, + verticalAlign: 'baseline', + color: 'inherit', + }), + codeCommon +); + +export const Code = ({ + className, + children, + ...props +}: React.ComponentProps) => { + const language = (className || '').match(/lang-(\S+)/); + const childrenArray = React.Children.toArray(children); + const isInlineCode = !childrenArray + .filter(isReactChildString) + .some((child) => child.match(isInlineCodeRegex)); + + if (isInlineCode) { + return ( + + {childrenArray} + + ); + } + + return ( + + {children} + + ); +}; diff --git a/lib/components/src/typography/elements/DL.tsx b/lib/components/src/typography/elements/DL.tsx new file mode 100644 index 000000000000..b204c6556087 --- /dev/null +++ b/lib/components/src/typography/elements/DL.tsx @@ -0,0 +1,37 @@ +import { styled } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +export const DL = styled.dl<{}>(withReset, { + ...withMargin, + padding: 0, + '& dt': { + fontSize: '14px', + fontWeight: 'bold', + fontStyle: 'italic', + padding: 0, + margin: '16px 0 4px', + }, + '& dt:first-of-type': { + padding: 0, + }, + '& dt > :first-of-type': { + marginTop: 0, + }, + + '& dt > :last-child': { + marginBottom: 0, + }, + + '& dd': { + margin: '0 0 16px', + padding: '0 15px', + }, + + '& dd > :first-of-type': { + marginTop: 0, + }, + + '& dd > :last-child': { + marginBottom: 0, + }, +}); diff --git a/lib/components/src/typography/elements/Div.tsx b/lib/components/src/typography/elements/Div.tsx new file mode 100644 index 000000000000..297b1672155c --- /dev/null +++ b/lib/components/src/typography/elements/Div.tsx @@ -0,0 +1,4 @@ +import { styled } from '@storybook/theming'; +import { withReset } from '../lib/common'; + +export const Div = styled.div<{}>(withReset); diff --git a/lib/components/src/typography/elements/H1.tsx b/lib/components/src/typography/elements/H1.tsx new file mode 100644 index 000000000000..0f46695ae4d3 --- /dev/null +++ b/lib/components/src/typography/elements/H1.tsx @@ -0,0 +1,7 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H1 = styled.h1<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.l1}px`, + fontWeight: theme.typography.weight.black, +})); diff --git a/lib/components/src/typography/elements/H2.tsx b/lib/components/src/typography/elements/H2.tsx new file mode 100644 index 000000000000..a7ec745bcaf9 --- /dev/null +++ b/lib/components/src/typography/elements/H2.tsx @@ -0,0 +1,8 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H2 = styled.h2<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.m2}px`, + paddingBottom: 4, + borderBottom: `1px solid ${theme.appBorderColor}`, +})); diff --git a/lib/components/src/typography/elements/H3.tsx b/lib/components/src/typography/elements/H3.tsx new file mode 100644 index 000000000000..fc93638614a2 --- /dev/null +++ b/lib/components/src/typography/elements/H3.tsx @@ -0,0 +1,6 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H3 = styled.h3<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.m1}px`, +})); diff --git a/lib/components/src/typography/elements/H4.tsx b/lib/components/src/typography/elements/H4.tsx new file mode 100644 index 000000000000..88e32a13e38e --- /dev/null +++ b/lib/components/src/typography/elements/H4.tsx @@ -0,0 +1,6 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H4 = styled.h4<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.s3}px`, +})); diff --git a/lib/components/src/typography/elements/H5.tsx b/lib/components/src/typography/elements/H5.tsx new file mode 100644 index 000000000000..b1d55198bced --- /dev/null +++ b/lib/components/src/typography/elements/H5.tsx @@ -0,0 +1,6 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H5 = styled.h5<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.s2}px`, +})); diff --git a/lib/components/src/typography/elements/H6.tsx b/lib/components/src/typography/elements/H6.tsx new file mode 100644 index 000000000000..dd1804f84263 --- /dev/null +++ b/lib/components/src/typography/elements/H6.tsx @@ -0,0 +1,7 @@ +import { styled } from '@storybook/theming'; +import { withReset, headerCommon } from '../lib/common'; + +export const H6 = styled.h6<{}>(withReset, headerCommon, ({ theme }) => ({ + fontSize: `${theme.typography.size.s2}px`, + color: theme.color.dark, +})); diff --git a/lib/components/src/typography/elements/HR.tsx b/lib/components/src/typography/elements/HR.tsx new file mode 100644 index 000000000000..eab7d4c1ff82 --- /dev/null +++ b/lib/components/src/typography/elements/HR.tsx @@ -0,0 +1,8 @@ +import { styled } from '@storybook/theming'; + +export const HR = styled.hr<{}>(({ theme }) => ({ + border: '0 none', + borderTop: `1px solid ${theme.appBorderColor}`, + height: 4, + padding: 0, +})); diff --git a/lib/components/src/typography/elements/Img.tsx b/lib/components/src/typography/elements/Img.tsx new file mode 100644 index 000000000000..5dafc283c64a --- /dev/null +++ b/lib/components/src/typography/elements/Img.tsx @@ -0,0 +1,5 @@ +import { styled } from '@storybook/theming'; + +export const Img = styled.img<{}>({ + maxWidth: '100%', +}); diff --git a/lib/components/src/typography/elements/LI.tsx b/lib/components/src/typography/elements/LI.tsx new file mode 100644 index 000000000000..159da5f188ee --- /dev/null +++ b/lib/components/src/typography/elements/LI.tsx @@ -0,0 +1,16 @@ +import { styled } from '@storybook/theming'; +import { withReset, codeCommon } from '../lib/common'; + +export const LI = styled.li<{}>(withReset, ({ theme }) => ({ + fontSize: theme.typography.size.s2, + color: theme.color.defaultText, + lineHeight: '24px', + '& + li': { + marginTop: '.25em', + }, + '& ul, & ol': { + marginTop: '.25em', + marginBottom: 0, + }, + '& code': codeCommon({ theme }), +})); diff --git a/lib/components/src/typography/elements/Link.tsx b/lib/components/src/typography/elements/Link.tsx new file mode 100644 index 000000000000..eb5ee9e0de5b --- /dev/null +++ b/lib/components/src/typography/elements/Link.tsx @@ -0,0 +1,14 @@ +import React, { FunctionComponent } from 'react'; + +export const Link: FunctionComponent = ({ href: input, children, ...props }) => { + const isStorybookPath = /^\//.test(input); + const isAnchorUrl = /^#.*/.test(input); + const href = isStorybookPath ? `?path=${input}` : input; + const target = isAnchorUrl ? '_self' : '_top'; + + return ( + + {children} + + ); +}; diff --git a/lib/components/src/typography/elements/OL.tsx b/lib/components/src/typography/elements/OL.tsx new file mode 100644 index 000000000000..19584f0f3b8c --- /dev/null +++ b/lib/components/src/typography/elements/OL.tsx @@ -0,0 +1,15 @@ +import { styled } from '@storybook/theming'; +import type { CSSObject } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +const listCommon: CSSObject = { + paddingLeft: 30, + '& :first-of-type': { + marginTop: 0, + }, + '& :last-child': { + marginBottom: 0, + }, +}; + +export const OL = styled.ol<{}>(withReset, withMargin, { ...listCommon, listStyle: 'decimal' }); diff --git a/lib/components/src/typography/elements/P.tsx b/lib/components/src/typography/elements/P.tsx new file mode 100644 index 000000000000..aad4b7b4bb36 --- /dev/null +++ b/lib/components/src/typography/elements/P.tsx @@ -0,0 +1,9 @@ +import { styled } from '@storybook/theming'; +import { withReset, withMargin, codeCommon } from '../lib/common'; + +export const P = styled.p<{}>(withReset, withMargin, ({ theme }) => ({ + fontSize: theme.typography.size.s2, + lineHeight: '24px', + color: theme.color.defaultText, + '& code': codeCommon({ theme }), +})); diff --git a/lib/components/src/typography/elements/Pre.tsx b/lib/components/src/typography/elements/Pre.tsx new file mode 100644 index 000000000000..9476699d6cca --- /dev/null +++ b/lib/components/src/typography/elements/Pre.tsx @@ -0,0 +1,41 @@ +import { styled } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +export const Pre = styled.pre<{}>(withReset, withMargin, ({ theme }) => ({ + // reset + fontFamily: theme.typography.fonts.mono, + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + lineHeight: '18px', + padding: '11px 1rem', + whiteSpace: 'pre-wrap', + color: 'inherit', + borderRadius: 3, + margin: '1rem 0', + + '&:not(.prismjs)': { + background: 'transparent', + border: 'none', + borderRadius: 0, + padding: 0, + margin: 0, + }, + '& pre, &.prismjs': { + padding: 15, + margin: 0, + whiteSpace: 'pre-wrap', + color: 'inherit', + fontSize: '13px', + lineHeight: '19px', + code: { + color: 'inherit', + fontSize: 'inherit', + }, + }, + '& code': { + whiteSpace: 'pre', + }, + '& code, & tt': { + border: 'none', + }, +})); diff --git a/lib/components/src/typography/elements/Span.tsx b/lib/components/src/typography/elements/Span.tsx new file mode 100644 index 000000000000..7dbac454d171 --- /dev/null +++ b/lib/components/src/typography/elements/Span.tsx @@ -0,0 +1,83 @@ +import { styled } from '@storybook/theming'; +import { withReset } from '../lib/common'; + +export const Span = styled.span<{}>(withReset, ({ theme }) => ({ + '&.frame': { + display: 'block', + overflow: 'hidden', + + '& > span': { + border: `1px solid ${theme.color.medium}`, + display: 'block', + float: 'left', + overflow: 'hidden', + margin: '13px 0 0', + padding: 7, + width: 'auto', + }, + '& span img': { + display: 'block', + float: 'left', + }, + '& span span': { + clear: 'both', + color: theme.color.darkest, + display: 'block', + padding: '5px 0 0', + }, + }, + '&.align-center': { + display: 'block', + overflow: 'hidden', + clear: 'both', + + '& > span': { + display: 'block', + overflow: 'hidden', + margin: '13px auto 0', + textAlign: 'center', + }, + '& span img': { + margin: '0 auto', + textAlign: 'center', + }, + }, + '&.align-right': { + display: 'block', + overflow: 'hidden', + clear: 'both', + + '& > span': { + display: 'block', + overflow: 'hidden', + margin: '13px 0 0', + textAlign: 'right', + }, + '& span img': { + margin: 0, + textAlign: 'right', + }, + }, + '&.float-left': { + display: 'block', + marginRight: 13, + overflow: 'hidden', + float: 'left', + '& span': { + margin: '13px 0 0', + }, + }, + '&.float-right': { + display: 'block', + marginLeft: 13, + overflow: 'hidden', + float: 'right', + + '& > span': { + display: 'block', + overflow: 'hidden', + margin: '13px auto 0', + textAlign: 'right', + }, + }, +})); diff --git a/lib/components/src/typography/elements/TT.tsx b/lib/components/src/typography/elements/TT.tsx new file mode 100644 index 000000000000..8d2e320eac9d --- /dev/null +++ b/lib/components/src/typography/elements/TT.tsx @@ -0,0 +1,4 @@ +import { styled } from '@storybook/theming'; +import { codeCommon } from '../lib/common'; + +export const TT = styled.title<{}>(codeCommon); diff --git a/lib/components/src/typography/elements/Table.tsx b/lib/components/src/typography/elements/Table.tsx new file mode 100644 index 000000000000..cd0b82f4f356 --- /dev/null +++ b/lib/components/src/typography/elements/Table.tsx @@ -0,0 +1,37 @@ +import { styled } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +export const Table = styled.table<{}>(withReset, withMargin, ({ theme }) => ({ + fontSize: theme.typography.size.s2, + lineHeight: '24px', + padding: 0, + borderCollapse: 'collapse', + '& tr': { + borderTop: `1px solid ${theme.appBorderColor}`, + backgroundColor: theme.appContentBg, + margin: 0, + padding: 0, + }, + '& tr:nth-of-type(2n)': { + backgroundColor: theme.base === 'dark' ? theme.color.darker : theme.color.lighter, + }, + '& tr th': { + fontWeight: 'bold', + color: theme.color.defaultText, + border: `1px solid ${theme.appBorderColor}`, + margin: 0, + padding: '6px 13px', + }, + '& tr td': { + border: `1px solid ${theme.appBorderColor}`, + color: theme.color.defaultText, + margin: 0, + padding: '6px 13px', + }, + '& tr th :first-of-type, & tr td :first-of-type': { + marginTop: 0, + }, + '& tr th :last-child, & tr td :last-child': { + marginBottom: 0, + }, +})); diff --git a/lib/components/src/typography/elements/UL.tsx b/lib/components/src/typography/elements/UL.tsx new file mode 100644 index 000000000000..2c2f4ee7ee8f --- /dev/null +++ b/lib/components/src/typography/elements/UL.tsx @@ -0,0 +1,15 @@ +import { styled } from '@storybook/theming'; +import type { CSSObject } from '@storybook/theming'; +import { withReset, withMargin } from '../lib/common'; + +const listCommon: CSSObject = { + paddingLeft: 30, + '& :first-of-type': { + marginTop: 0, + }, + '& :last-child': { + marginBottom: 0, + }, +}; + +export const UL = styled.ul<{}>(withReset, withMargin, { ...listCommon, listStyle: 'disc' }); diff --git a/lib/components/src/typography/shared.tsx b/lib/components/src/typography/lib/common.tsx similarity index 95% rename from lib/components/src/typography/shared.tsx rename to lib/components/src/typography/lib/common.tsx index 4ff39c0bf949..dcd6f9adc573 100644 --- a/lib/components/src/typography/shared.tsx +++ b/lib/components/src/typography/lib/common.tsx @@ -1,5 +1,5 @@ import { transparentize } from 'polished'; -import { CSSObject, Theme } from '@storybook/theming'; +import type { CSSObject, Theme } from '@storybook/theming'; export const headerCommon = ({ theme }: { theme: Theme }): CSSObject => ({ margin: '20px 0 8px', diff --git a/lib/components/src/typography/lib/isReactChildString.tsx b/lib/components/src/typography/lib/isReactChildString.tsx new file mode 100644 index 000000000000..ef03ec347ac3 --- /dev/null +++ b/lib/components/src/typography/lib/isReactChildString.tsx @@ -0,0 +1,3 @@ +import { ReactNode } from 'react'; + +export const isReactChildString = (child: ReactNode): child is string => typeof child === 'string'; From 67b70ab2f95b839a6cd4c29556976bb8258817b5 Mon Sep 17 00:00:00 2001 From: Norbert de Langen Date: Thu, 27 Jan 2022 21:54:00 +0100 Subject: [PATCH 04/17] progress --- lib/components/src/blocks/Description.tsx | 2 +- lib/components/src/blocks/Preview.tsx | 2 +- lib/components/src/blocks/Story.tsx | 2 +- .../react-editable-json-tree/JsonNodes.tsx | 1597 +++++++++++++++++ .../components/JsonAddValue.tsx | 143 -- .../components/JsonArray.tsx | 352 ---- .../components/JsonFunctionValue.tsx | 220 --- .../components/JsonNode.tsx | 347 ---- .../components/JsonObject.tsx | 361 ---- .../components/JsonValue.tsx | 209 --- .../react-editable-json-tree/index.tsx | 97 +- scripts/bundle-package.ts | 17 +- scripts/dts-localize.ts | 7 +- 13 files changed, 1666 insertions(+), 1690 deletions(-) create mode 100644 lib/components/src/controls/react-editable-json-tree/JsonNodes.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx delete mode 100644 lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx diff --git a/lib/components/src/blocks/Description.tsx b/lib/components/src/blocks/Description.tsx index 7692118bc55a..b8e47de00aa0 100644 --- a/lib/components/src/blocks/Description.tsx +++ b/lib/components/src/blocks/Description.tsx @@ -1,7 +1,7 @@ import React, { FunctionComponent } from 'react'; import Markdown from 'markdown-to-jsx'; import { ResetWrapper } from '../typography/ResetWrapper'; -import { components } from '..'; +import { components } from '../typography/components'; export interface DescriptionProps { markdown: string; diff --git a/lib/components/src/blocks/Preview.tsx b/lib/components/src/blocks/Preview.tsx index 1d142a66f4f9..6750b49e0bda 100644 --- a/lib/components/src/blocks/Preview.tsx +++ b/lib/components/src/blocks/Preview.tsx @@ -17,7 +17,7 @@ import { ActionBar, ActionItem } from '../ActionBar/ActionBar'; import { Toolbar } from './Toolbar'; import { ZoomContext } from './ZoomContext'; import { Zoom } from '../Zoom/Zoom'; -import { StorySkeleton } from '.'; +import { StorySkeleton } from './Story'; export interface PreviewProps { isLoading?: true; diff --git a/lib/components/src/blocks/Story.tsx b/lib/components/src/blocks/Story.tsx index 0af364d08e27..bc466a54dd23 100644 --- a/lib/components/src/blocks/Story.tsx +++ b/lib/components/src/blocks/Story.tsx @@ -6,7 +6,7 @@ import type { Parameters } from '@storybook/csf'; import { IFrame } from './IFrame'; import { EmptyBlock } from './EmptyBlock'; import { ZoomContext } from './ZoomContext'; -import { Loader } from '..'; +import { Loader } from '../Loader/Loader'; import { getStoryHref } from '../utils/getStoryHref'; const { PREVIEW_URL } = global; diff --git a/lib/components/src/controls/react-editable-json-tree/JsonNodes.tsx b/lib/components/src/controls/react-editable-json-tree/JsonNodes.tsx new file mode 100644 index 000000000000..8b5e512b1ecc --- /dev/null +++ b/lib/components/src/controls/react-editable-json-tree/JsonNodes.tsx @@ -0,0 +1,1597 @@ +/* eslint-disable jsx-a11y/no-static-element-interactions */ +/* eslint-disable react/sort-comp */ +/* eslint-disable react/no-array-index-key */ + +/* eslint-disable no-undef */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable react/button-has-type */ + +import React, { Component, ReactElement } from 'react'; +import * as inputUsageTypes from './types/inputUsageTypes'; + +import * as dataTypes from './types/dataTypes'; +import * as deltaTypes from './types/deltaTypes'; +import { getObjectType, isComponentWillChange } from './utils/objectTypes'; + +interface JsonAddValueState { + inputRefKey: any; + inputRefValue: any; +} + +export class JsonAddValue extends Component { + constructor(props: JsonAddValueProps) { + super(props); + this.state = { + inputRefKey: null, + inputRefValue: null, + }; + // Bind + this.refInputValue = this.refInputValue.bind(this); + this.refInputKey = this.refInputKey.bind(this); + this.onKeydown = this.onKeydown.bind(this); + this.onSubmit = this.onSubmit.bind(this); + } + + componentDidMount() { + const { inputRefKey, inputRefValue } = this.state; + const { onlyValue } = this.props; + + if (inputRefKey && typeof inputRefKey.focus === 'function') { + inputRefKey.focus(); + } + + if (onlyValue && inputRefValue && typeof inputRefValue.focus === 'function') { + inputRefValue.focus(); + } + + document.addEventListener('keydown', this.onKeydown); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeydown); + } + + onKeydown(event: KeyboardEvent) { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; + if (event.code === 'Enter' || event.key === 'Enter') { + event.preventDefault(); + this.onSubmit(); + } + if (event.code === 'Escape' || event.key === 'Escape') { + event.preventDefault(); + this.props.handleCancel(); + } + } + + onSubmit() { + const { handleAdd, onlyValue, onSubmitValueParser, keyPath, deep } = this.props; + const { inputRefKey, inputRefValue } = this.state; + const result: any = {}; + // Check if we have the key + if (!onlyValue) { + // Check that there is a key + if (!inputRefKey.value) { + // Empty key => Not authorized + return; + } + + result.key = inputRefKey.value; + } + result.newValue = onSubmitValueParser(false, keyPath, deep, result.key, inputRefValue.value); + handleAdd(result); + } + + refInputKey(node: any) { + // @ts-ignore + this.state.inputRefKey = node; + } + + refInputValue(node: any) { + // @ts-ignore + this.state.inputRefValue = node; + } + + render() { + const { + handleCancel, + onlyValue, + addButtonElement, + cancelButtonElement, + inputElementGenerator, + keyPath, + deep, + } = this.props; + const addButtonElementLayout = React.cloneElement(addButtonElement, { + onClick: this.onSubmit, + }); + const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { + onClick: handleCancel, + }); + const inputElementValue = inputElementGenerator(inputUsageTypes.VALUE, keyPath, deep); + const inputElementValueLayout = React.cloneElement(inputElementValue, { + placeholder: 'Value', + ref: this.refInputValue, + }); + let inputElementKeyLayout = null; + + if (!onlyValue) { + const inputElementKey = inputElementGenerator(inputUsageTypes.KEY, keyPath, deep); + inputElementKeyLayout = React.cloneElement(inputElementKey, { + placeholder: 'Key', + ref: this.refInputKey, + }); + } + + return ( + + {inputElementKeyLayout} + {inputElementValueLayout} + {cancelButtonElementLayout} + {addButtonElementLayout} + + ); + } +} + +interface JsonAddValueProps { + handleAdd: (...args: any) => any; + handleCancel: (...args: any) => any; + onlyValue?: boolean; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + keyPath?: string[]; + deep?: number; + onSubmitValueParser: (...args: any) => any; +} + +// @ts-ignore +JsonAddValue.defaultProps = { + onlyValue: false, + addButtonElement: , + cancelButtonElement: , +}; + +interface JsonArrayState { + data: JsonArrayProps['data']; + name: JsonArrayProps['name']; + keyPath: string[]; + deep: JsonArrayProps['deep']; + nextDeep: JsonArrayProps['deep']; + collapsed: any; + addFormVisible: boolean; +} +export class JsonArray extends Component { + constructor(props: JsonArrayProps) { + super(props); + const keyPath = [...props.keyPath, props.name]; + this.state = { + data: props.data, + name: props.name, + keyPath, + deep: props.deep, + nextDeep: props.deep + 1, + collapsed: props.isCollapsed(keyPath, props.deep, props.data), + addFormVisible: false, + }; + + // Bind + this.handleCollapseMode = this.handleCollapseMode.bind(this); + this.handleRemoveItem = this.handleRemoveItem.bind(this); + this.handleAddMode = this.handleAddMode.bind(this); + this.handleAddValueAdd = this.handleAddValueAdd.bind(this); + this.handleAddValueCancel = this.handleAddValueCancel.bind(this); + this.handleEditValue = this.handleEditValue.bind(this); + this.onChildUpdate = this.onChildUpdate.bind(this); + this.renderCollapsed = this.renderCollapsed.bind(this); + this.renderNotCollapsed = this.renderNotCollapsed.bind(this); + } + + static getDerivedStateFromProps(props: JsonArrayProps, state: JsonArrayState) { + return props.data !== state.data ? { data: props.data } : null; + } + + onChildUpdate(childKey: string, childData: any) { + const { data, keyPath } = this.state; + // Update data + // @ts-ignore + data[childKey] = childData; + // Put new data + this.setState({ + data, + }); + // Spread + const { onUpdate } = this.props; + const size = keyPath.length; + onUpdate(keyPath[size - 1], data); + } + + handleAddMode() { + this.setState({ + addFormVisible: true, + }); + } + + handleCollapseMode() { + this.setState((state) => ({ + collapsed: !state.collapsed, + })); + } + + handleRemoveItem(index: number) { + return () => { + const { beforeRemoveAction, logger } = this.props; + const { data, keyPath, nextDeep: deep } = this.state; + const oldValue = data[index]; + + // Before Remove Action + beforeRemoveAction(index, keyPath, deep, oldValue) + .then(() => { + const deltaUpdateResult = { + keyPath, + deep, + key: index, + oldValue, + type: deltaTypes.REMOVE_DELTA_TYPE, + }; + + data.splice(index, 1); + this.setState({ data }); + + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], data); + // Spread delta update + onDeltaUpdate(deltaUpdateResult); + }) + .catch(logger.error); + }; + } + + handleAddValueAdd({ newValue }: any) { + const { data, keyPath, nextDeep: deep } = this.state; + const { beforeAddAction, logger } = this.props; + + beforeAddAction(data.length, keyPath, deep, newValue) + .then(() => { + // Update data + const newData = [...data, newValue]; + this.setState({ + data: newData, + }); + // Cancel add to close + this.handleAddValueCancel(); + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], newData); + // Spread delta update + onDeltaUpdate({ + type: deltaTypes.ADD_DELTA_TYPE, + keyPath, + deep, + key: newData.length - 1, + newValue, + }); + }) + .catch(logger.error); + } + + handleAddValueCancel() { + this.setState({ + addFormVisible: false, + }); + } + + handleEditValue({ key, value }: any) { + return new Promise((resolve, reject) => { + const { beforeUpdateAction } = this.props; + const { data, keyPath, nextDeep: deep } = this.state; + + // Old value + const oldValue = data[key]; + + // Before update action + beforeUpdateAction(key, keyPath, deep, oldValue, value) + .then(() => { + // Update value + data[key] = value; + // Set state + this.setState({ + data, + }); + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], data); + // Spread delta update + onDeltaUpdate({ + type: deltaTypes.UPDATE_DELTA_TYPE, + keyPath, + deep, + key, + newValue: value, + oldValue, + }); + // Resolve + resolve(undefined); + }) + .catch(reject); + }); + } + + renderCollapsed() { + const { name, data, keyPath, deep } = this.state; + const { handleRemove, readOnly, getStyle, dataType, minusMenuElement } = this.props; + const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType); + + const isReadOnly = readOnly(name, data, keyPath, deep, dataType); + + const removeItemButton = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: minus, + }); + + return ( + + + [...] {data.length} {data.length === 1 ? 'item' : 'items'} + + {!isReadOnly && removeItemButton} + + ); + } + + renderNotCollapsed() { + const { name, data, keyPath, deep, addFormVisible, nextDeep } = this.state; + const { + isCollapsed, + handleRemove, + onDeltaUpdate, + readOnly, + getStyle, + dataType, + addButtonElement, + cancelButtonElement, + editButtonElement, + inputElementGenerator, + textareaElementGenerator, + minusMenuElement, + plusMenuElement, + beforeRemoveAction, + beforeAddAction, + beforeUpdateAction, + logger, + onSubmitValueParser, + } = this.props; + const { minus, plus, delimiter, ul, addForm } = getStyle(name, data, keyPath, deep, dataType); + + const isReadOnly = readOnly(name, data, keyPath, deep, dataType); + + const addItemButton = React.cloneElement(plusMenuElement, { + onClick: this.handleAddMode, + className: 'rejt-plus-menu', + style: plus, + }); + const removeItemButton = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: minus, + }); + + const onlyValue = true; + const startObject = '['; + const endObject = ']'; + return ( + + + {startObject} + + {!addFormVisible && addItemButton} +

          + {data.map((item, index) => ( + + ))} +
        + {!isReadOnly && addFormVisible && ( +
        + +
        + )} + + {endObject} + + {!isReadOnly && removeItemButton} + + ); + } + + render() { + const { name, collapsed, data, keyPath, deep } = this.state; + const { dataType, getStyle } = this.props; + const value = collapsed ? this.renderCollapsed() : this.renderNotCollapsed(); + const style = getStyle(name, data, keyPath, deep, dataType); + + return ( +
        + + + {name} :{' '} + + + {value} +
        + ); + } +} + +interface JsonArrayProps { + data: any[]; + name: string; + isCollapsed: (...args: any) => any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: any; + onSubmitValueParser: (...args: any) => any; +} + +// @ts-ignore +JsonArray.defaultProps = { + keyPath: [], + deep: 0, + minusMenuElement: - , + plusMenuElement: + , +}; + +interface JsonFunctionValueState { + value: JsonFunctionValueProps['value']; + name: JsonFunctionValueProps['name']; + keyPath: string[]; + deep: JsonFunctionValueProps['deep']; + editEnabled: boolean; + inputRef: any; +} + +export class JsonFunctionValue extends Component { + constructor(props: JsonFunctionValueProps) { + super(props); + const keyPath = [...props.keyPath, props.name]; + this.state = { + value: props.value, + name: props.name, + keyPath, + deep: props.deep, + editEnabled: false, + inputRef: null, + }; + + // Bind + this.handleEditMode = this.handleEditMode.bind(this); + this.refInput = this.refInput.bind(this); + this.handleCancelEdit = this.handleCancelEdit.bind(this); + this.handleEdit = this.handleEdit.bind(this); + this.onKeydown = this.onKeydown.bind(this); + } + + static getDerivedStateFromProps(props: JsonFunctionValueProps, state: JsonFunctionValueState) { + return props.value !== state.value ? { value: props.value } : null; + } + + componentDidUpdate() { + const { editEnabled, inputRef, name, value, keyPath, deep } = this.state; + const { readOnly, dataType } = this.props; + const readOnlyResult = readOnly(name, value, keyPath, deep, dataType); + + if (editEnabled && !readOnlyResult && typeof inputRef.focus === 'function') { + inputRef.focus(); + } + } + + componentDidMount() { + document.addEventListener('keydown', this.onKeydown); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeydown); + } + + onKeydown(event: KeyboardEvent) { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; + if (event.code === 'Enter' || event.key === 'Enter') { + event.preventDefault(); + this.handleEdit(); + } + if (event.code === 'Escape' || event.key === 'Escape') { + event.preventDefault(); + this.handleCancelEdit(); + } + } + + handleEdit() { + const { handleUpdateValue, originalValue, logger, onSubmitValueParser, keyPath } = this.props; + const { inputRef, name, deep } = this.state; + if (!inputRef) return; + + const newValue = onSubmitValueParser(true, keyPath, deep, name, inputRef.value); + + const result = { + value: newValue, + key: name, + }; + + // Run update + handleUpdateValue(result) + .then(() => { + // Cancel edit mode if necessary + if (!isComponentWillChange(originalValue, newValue)) { + this.handleCancelEdit(); + } + }) + .catch(logger.error); + } + + handleEditMode() { + this.setState({ + editEnabled: true, + }); + } + + refInput(node: any) { + // @ts-ignore + this.state.inputRef = node; + } + + handleCancelEdit() { + this.setState({ + editEnabled: false, + }); + } + + render() { + const { name, value, editEnabled, keyPath, deep } = this.state; + const { + handleRemove, + originalValue, + readOnly, + dataType, + getStyle, + editButtonElement, + cancelButtonElement, + textareaElementGenerator, + minusMenuElement, + keyPath: comeFromKeyPath, + } = this.props; + + const style = getStyle(name, originalValue, keyPath, deep, dataType); + let result = null; + let minusElement = null; + const resultOnlyResult = readOnly(name, originalValue, keyPath, deep, dataType); + + if (editEnabled && !resultOnlyResult) { + const textareaElement = textareaElementGenerator( + inputUsageTypes.VALUE, + comeFromKeyPath, + deep, + name, + originalValue, + dataType + ); + + const editButtonElementLayout = React.cloneElement(editButtonElement, { + onClick: this.handleEdit, + }); + const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { + onClick: this.handleCancelEdit, + }); + const textareaElementLayout = React.cloneElement(textareaElement, { + ref: this.refInput, + defaultValue: originalValue, + }); + + result = ( + + {textareaElementLayout} {cancelButtonElementLayout} + {editButtonElementLayout} + + ); + minusElement = null; + } else { + result = ( + + {value} + + ); + const minusMenuLayout = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: style.minus, + }); + minusElement = resultOnlyResult ? null : minusMenuLayout; + } + + return ( +
      1. + + {name} :{' '} + + {result} + {minusElement} +
      2. + ); + } +} + +interface JsonFunctionValueProps { + name: string; + value: any; + originalValue?: any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + editButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + logger: any; + onSubmitValueParser: (...args: any) => any; +} + +// @ts-ignore +JsonFunctionValue.defaultProps = { + keyPath: [], + deep: 0, + handleUpdateValue: () => {}, + editButtonElement: , + cancelButtonElement: , + minusMenuElement: - , +}; + +interface JsonNodeState { + data: JsonNodeProps['data']; + name: JsonNodeProps['name']; + keyPath: JsonNodeProps['keyPath']; + deep: JsonNodeProps['deep']; +} + +export class JsonNode extends Component { + constructor(props: JsonNodeProps) { + super(props); + this.state = { + data: props.data, + name: props.name, + keyPath: props.keyPath, + deep: props.deep, + }; + } + + static getDerivedStateFromProps(props: JsonNodeProps, state: JsonNodeState) { + return props.data !== state.data ? { data: props.data } : null; + } + + render() { + const { data, name, keyPath, deep } = this.state; + const { + isCollapsed, + handleRemove, + handleUpdateValue, + onUpdate, + onDeltaUpdate, + readOnly, + getStyle, + addButtonElement, + cancelButtonElement, + editButtonElement, + inputElementGenerator, + textareaElementGenerator, + minusMenuElement, + plusMenuElement, + beforeRemoveAction, + beforeAddAction, + beforeUpdateAction, + logger, + onSubmitValueParser, + } = this.props; + const readOnlyTrue = () => true; + + const dataType = getObjectType(data); + switch (dataType) { + case dataTypes.ERROR: + return ( + + ); + case dataTypes.OBJECT: + return ( + + ); + case dataTypes.ARRAY: + return ( + + ); + case dataTypes.STRING: + return ( + + ); + case dataTypes.NUMBER: + return ( + + ); + case dataTypes.BOOLEAN: + return ( + + ); + case dataTypes.DATE: + return ( + + ); + case dataTypes.NULL: + return ( + + ); + case dataTypes.UNDEFINED: + return ( + + ); + case dataTypes.FUNCTION: + return ( + + ); + case dataTypes.SYMBOL: + return ( + + ); + default: + return null; + } + } +} + +interface JsonNodeProps { + name: string; + data?: any; + isCollapsed: (...args: any) => any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: object; + onSubmitValueParser: (...args: any) => any; +} + +/// @ts-ignore +JsonNode.defaultProps = { + keyPath: [], + deep: 0, +}; + +interface JsonObjectState { + name: string; + collapsed: ReturnType; + data: JsonObjectProps['data']; + keyPath: JsonObjectProps['keyPath']; + deep: JsonObjectProps['deep']; + nextDeep: number; + addFormVisible: boolean; +} + +export class JsonObject extends Component { + constructor(props: JsonObjectProps) { + super(props); + const keyPath = props.deep === -1 ? [] : [...props.keyPath, props.name]; + this.state = { + name: props.name, + data: props.data, + keyPath, + deep: props.deep, + nextDeep: props.deep + 1, + collapsed: props.isCollapsed(keyPath, props.deep, props.data), + addFormVisible: false, + }; + + // Bind + this.handleCollapseMode = this.handleCollapseMode.bind(this); + this.handleRemoveValue = this.handleRemoveValue.bind(this); + this.handleAddMode = this.handleAddMode.bind(this); + this.handleAddValueAdd = this.handleAddValueAdd.bind(this); + this.handleAddValueCancel = this.handleAddValueCancel.bind(this); + this.handleEditValue = this.handleEditValue.bind(this); + this.onChildUpdate = this.onChildUpdate.bind(this); + this.renderCollapsed = this.renderCollapsed.bind(this); + this.renderNotCollapsed = this.renderNotCollapsed.bind(this); + } + + static getDerivedStateFromProps(props: JsonObjectProps, state: JsonObjectState) { + return props.data !== state.data ? { data: props.data } : null; + } + + onChildUpdate(childKey: string, childData: any) { + const { data, keyPath } = this.state; + // Update data + // @ts-ignore + data[childKey] = childData; + // Put new data + this.setState({ + data, + }); + // Spread + const { onUpdate } = this.props; + const size = keyPath.length; + onUpdate(keyPath[size - 1], data); + } + + handleAddMode() { + this.setState({ + addFormVisible: true, + }); + } + + handleAddValueCancel() { + this.setState({ + addFormVisible: false, + }); + } + + handleAddValueAdd({ key, newValue }: any) { + const { data, keyPath, nextDeep: deep } = this.state; + const { beforeAddAction, logger } = this.props; + + beforeAddAction(key, keyPath, deep, newValue) + .then(() => { + // Update data + // @ts-ignore + data[key] = newValue; + this.setState({ + data, + }); + // Cancel add to close + this.handleAddValueCancel(); + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], data); + // Spread delta update + onDeltaUpdate({ + type: deltaTypes.ADD_DELTA_TYPE, + keyPath, + deep, + key, + newValue, + }); + }) + .catch(logger.error); + } + + handleRemoveValue(key: string) { + return () => { + const { beforeRemoveAction, logger } = this.props; + const { data, keyPath, nextDeep: deep } = this.state; + // @ts-ignore + const oldValue = data[key]; + // Before Remove Action + beforeRemoveAction(key, keyPath, deep, oldValue) + .then(() => { + const deltaUpdateResult = { + keyPath, + deep, + key, + oldValue, + type: deltaTypes.REMOVE_DELTA_TYPE, + }; + + // @ts-ignore + delete data[key]; + this.setState({ data }); + + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], data); + // Spread delta update + onDeltaUpdate(deltaUpdateResult); + }) + .catch(logger.error); + }; + } + + handleCollapseMode() { + this.setState((state) => ({ + collapsed: !state.collapsed, + })); + } + + handleEditValue({ key, value }: any) { + return new Promise((resolve, reject) => { + const { beforeUpdateAction } = this.props; + const { data, keyPath, nextDeep: deep } = this.state; + + // Old value + // @ts-ignore + const oldValue = data[key]; + + // Before update action + beforeUpdateAction(key, keyPath, deep, oldValue, value) + .then(() => { + // Update value + // @ts-ignore + data[key] = value; + // Set state + this.setState({ + data, + }); + // Spread new update + const { onUpdate, onDeltaUpdate } = this.props; + onUpdate(keyPath[keyPath.length - 1], data); + // Spread delta update + onDeltaUpdate({ + type: deltaTypes.UPDATE_DELTA_TYPE, + keyPath, + deep, + key, + newValue: value, + oldValue, + }); + // Resolve + resolve(); + }) + .catch(reject); + }); + } + + renderCollapsed() { + const { name, keyPath, deep, data } = this.state; + const { handleRemove, readOnly, dataType, getStyle, minusMenuElement } = this.props; + + const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType); + const keyList = Object.getOwnPropertyNames(data); + + const isReadOnly = readOnly(name, data, keyPath, deep, dataType); + + const removeItemButton = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: minus, + }); + + return ( + + + {'{...}'} {keyList.length} {keyList.length === 1 ? 'key' : 'keys'} + + {!isReadOnly && removeItemButton} + + ); + } + + renderNotCollapsed() { + const { name, data, keyPath, deep, nextDeep, addFormVisible } = this.state; + const { + isCollapsed, + handleRemove, + onDeltaUpdate, + readOnly, + getStyle, + dataType, + addButtonElement, + cancelButtonElement, + editButtonElement, + inputElementGenerator, + textareaElementGenerator, + minusMenuElement, + plusMenuElement, + beforeRemoveAction, + beforeAddAction, + beforeUpdateAction, + logger, + onSubmitValueParser, + } = this.props; + + const { minus, plus, addForm, ul, delimiter } = getStyle(name, data, keyPath, deep, dataType); + const keyList = Object.getOwnPropertyNames(data); + + const isReadOnly = readOnly(name, data, keyPath, deep, dataType); + + const addItemButton = React.cloneElement(plusMenuElement, { + onClick: this.handleAddMode, + className: 'rejt-plus-menu', + style: plus, + }); + const removeItemButton = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: minus, + }); + + const list = keyList.map((key) => ( + + )); + + const startObject = '{'; + const endObject = '}'; + + return ( + + + {startObject} + + {!isReadOnly && addItemButton} +
          + {list} +
        + {!isReadOnly && addFormVisible && ( +
        + +
        + )} + + {endObject} + + {!isReadOnly && removeItemButton} +
        + ); + } + + render() { + const { name, collapsed, data, keyPath, deep } = this.state; + const { getStyle, dataType } = this.props; + const value = collapsed ? this.renderCollapsed() : this.renderNotCollapsed(); + const style = getStyle(name, data, keyPath, deep, dataType); + + return ( +
        + + + {name} :{' '} + + + {value} +
        + ); + } +} + +interface JsonObjectProps { + data: Record; + name: string; + isCollapsed: (...args: any) => any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + onUpdate: (...args: any) => any; + onDeltaUpdate: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + textareaElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + beforeRemoveAction?: (...args: any) => any; + beforeAddAction?: (...args: any) => any; + beforeUpdateAction?: (...args: any) => any; + logger: any; + onSubmitValueParser: (...args: any) => any; +} + +// @ts-ignore +JsonObject.defaultProps = { + keyPath: [], + deep: 0, + minusMenuElement: - , + plusMenuElement: + , +}; + +interface JsonValueState { + value: JsonValueProps['value']; + name: JsonValueProps['name']; + keyPath: string[]; + deep: JsonValueProps['deep']; + editEnabled: boolean; + inputRef: any; +} + +export class JsonValue extends Component { + constructor(props: JsonValueProps) { + super(props); + const keyPath = [...props.keyPath, props.name]; + this.state = { + value: props.value, + name: props.name, + keyPath, + deep: props.deep, + editEnabled: false, + inputRef: null, + }; + + // Bind + this.handleEditMode = this.handleEditMode.bind(this); + this.refInput = this.refInput.bind(this); + this.handleCancelEdit = this.handleCancelEdit.bind(this); + this.handleEdit = this.handleEdit.bind(this); + this.onKeydown = this.onKeydown.bind(this); + } + + static getDerivedStateFromProps(props: JsonValueProps, state: JsonValueState) { + return props.value !== state.value ? { value: props.value } : null; + } + + componentDidUpdate() { + const { editEnabled, inputRef, name, value, keyPath, deep } = this.state; + const { readOnly, dataType } = this.props; + const isReadOnly = readOnly(name, value, keyPath, deep, dataType); + + if (editEnabled && !isReadOnly && typeof inputRef.focus === 'function') { + inputRef.focus(); + } + } + + componentDidMount() { + document.addEventListener('keydown', this.onKeydown); + } + + componentWillUnmount() { + document.removeEventListener('keydown', this.onKeydown); + } + + onKeydown(event: KeyboardEvent) { + if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; + if (event.code === 'Enter' || event.key === 'Enter') { + event.preventDefault(); + this.handleEdit(); + } + if (event.code === 'Escape' || event.key === 'Escape') { + event.preventDefault(); + this.handleCancelEdit(); + } + } + + handleEdit() { + const { handleUpdateValue, originalValue, logger, onSubmitValueParser, keyPath } = this.props; + const { inputRef, name, deep } = this.state; + if (!inputRef) return; + + const newValue = onSubmitValueParser(true, keyPath, deep, name, inputRef.value); + + const result = { + value: newValue, + key: name, + }; + + // Run update + handleUpdateValue(result) + .then(() => { + // Cancel edit mode if necessary + if (!isComponentWillChange(originalValue, newValue)) { + this.handleCancelEdit(); + } + }) + .catch(logger.error); + } + + handleEditMode() { + this.setState({ + editEnabled: true, + }); + } + + refInput(node: any) { + // @ts-ignore + this.state.inputRef = node; + } + + handleCancelEdit() { + this.setState({ + editEnabled: false, + }); + } + + render() { + const { name, value, editEnabled, keyPath, deep } = this.state; + const { + handleRemove, + originalValue, + readOnly, + dataType, + getStyle, + editButtonElement, + cancelButtonElement, + inputElementGenerator, + minusMenuElement, + keyPath: comeFromKeyPath, + } = this.props; + + const style = getStyle(name, originalValue, keyPath, deep, dataType); + const isReadOnly = readOnly(name, originalValue, keyPath, deep, dataType); + const isEditing = editEnabled && !isReadOnly; + const inputElement = inputElementGenerator( + inputUsageTypes.VALUE, + comeFromKeyPath, + deep, + name, + originalValue, + dataType + ); + + const editButtonElementLayout = React.cloneElement(editButtonElement, { + onClick: this.handleEdit, + }); + const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { + onClick: this.handleCancelEdit, + }); + const inputElementLayout = React.cloneElement(inputElement, { + ref: this.refInput, + defaultValue: JSON.stringify(originalValue), + }); + const minusMenuLayout = React.cloneElement(minusMenuElement, { + onClick: handleRemove, + className: 'rejt-minus-menu', + style: style.minus, + }); + + return ( +
      3. + + {name} + {' : '} + + {isEditing ? ( + + {inputElementLayout} {cancelButtonElementLayout} + {editButtonElementLayout} + + ) : ( + + {String(value)} + + )} + {!isReadOnly && !isEditing && minusMenuLayout} +
      4. + ); + } +} + +interface JsonValueProps { + name: string; + value: any; + originalValue?: any; + keyPath?: string[]; + deep?: number; + handleRemove?: (...args: any) => any; + handleUpdateValue?: (...args: any) => any; + readOnly: (...args: any) => any; + dataType?: string; + getStyle: (...args: any) => any; + editButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + inputElementGenerator: (...args: any) => any; + minusMenuElement?: ReactElement; + logger: any; + onSubmitValueParser: (...args: any) => any; +} + +// @ts-ignore +JsonValue.defaultProps = { + keyPath: [], + deep: 0, + handleUpdateValue: () => Promise.resolve(), + editButtonElement: , + cancelButtonElement: , + minusMenuElement: - , +}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx deleted file mode 100644 index 4619577f7316..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonAddValue.tsx +++ /dev/null @@ -1,143 +0,0 @@ -/* eslint-disable react/button-has-type */ -import React, { Component, ReactElement } from 'react'; - -import * as inputUsageTypes from '../types/inputUsageTypes'; - -interface JsonAddValueState { - inputRefKey: any; - inputRefValue: any; -} - -export class JsonAddValue extends Component { - constructor(props: JsonAddValueProps) { - super(props); - this.state = { - inputRefKey: null, - inputRefValue: null, - }; - // Bind - this.refInputValue = this.refInputValue.bind(this); - this.refInputKey = this.refInputKey.bind(this); - this.onKeydown = this.onKeydown.bind(this); - this.onSubmit = this.onSubmit.bind(this); - } - - componentDidMount() { - const { inputRefKey, inputRefValue } = this.state; - const { onlyValue } = this.props; - - if (inputRefKey && typeof inputRefKey.focus === 'function') { - inputRefKey.focus(); - } - - if (onlyValue && inputRefValue && typeof inputRefValue.focus === 'function') { - inputRefValue.focus(); - } - - document.addEventListener('keydown', this.onKeydown); - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeydown); - } - - onKeydown(event: KeyboardEvent) { - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; - if (event.code === 'Enter' || event.key === 'Enter') { - event.preventDefault(); - this.onSubmit(); - } - if (event.code === 'Escape' || event.key === 'Escape') { - event.preventDefault(); - this.props.handleCancel(); - } - } - - onSubmit() { - const { handleAdd, onlyValue, onSubmitValueParser, keyPath, deep } = this.props; - const { inputRefKey, inputRefValue } = this.state; - const result: any = {}; - // Check if we have the key - if (!onlyValue) { - // Check that there is a key - if (!inputRefKey.value) { - // Empty key => Not authorized - return; - } - - result.key = inputRefKey.value; - } - result.newValue = onSubmitValueParser(false, keyPath, deep, result.key, inputRefValue.value); - handleAdd(result); - } - - refInputKey(node: any) { - // @ts-ignore - this.state.inputRefKey = node; - } - - refInputValue(node) { - // @ts-ignore - this.state.inputRefValue = node; - } - - render() { - const { - handleCancel, - onlyValue, - addButtonElement, - cancelButtonElement, - inputElementGenerator, - keyPath, - deep, - } = this.props; - const addButtonElementLayout = React.cloneElement(addButtonElement, { - onClick: this.onSubmit, - }); - const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { - onClick: handleCancel, - }); - const inputElementValue = inputElementGenerator(inputUsageTypes.VALUE, keyPath, deep); - const inputElementValueLayout = React.cloneElement(inputElementValue, { - placeholder: 'Value', - ref: this.refInputValue, - }); - let inputElementKeyLayout = null; - - if (!onlyValue) { - const inputElementKey = inputElementGenerator(inputUsageTypes.KEY, keyPath, deep); - inputElementKeyLayout = React.cloneElement(inputElementKey, { - placeholder: 'Key', - ref: this.refInputKey, - }); - } - - return ( - - {inputElementKeyLayout} - {inputElementValueLayout} - {cancelButtonElementLayout} - {addButtonElementLayout} - - ); - } -} - -interface JsonAddValueProps { - handleAdd: (...args: any) => any; - handleCancel: (...args: any) => any; - onlyValue?: boolean; - addButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - inputElementGenerator: (...args: any) => any; - keyPath?: string[]; - deep?: number; - onSubmitValueParser: (...args: any) => any; -} - -// @ts-ignore -JsonAddValue.defaultProps = { - onlyValue: false, - addButtonElement: , - cancelButtonElement: , -}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx deleted file mode 100644 index 7ee72c93fa0a..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonArray.tsx +++ /dev/null @@ -1,352 +0,0 @@ -/* eslint-disable react/no-array-index-key */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -import React, { Component, ReactElement } from 'react'; - -import { JsonNode } from './JsonNode'; -import { JsonAddValue } from './JsonAddValue'; -import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from '../types/deltaTypes'; - -interface JsonArrayState { - data: JsonArrayProps['data']; - name: JsonArrayProps['name']; - keyPath: string[]; - deep: JsonArrayProps['deep']; - nextDeep: JsonArrayProps['deep']; - collapsed: any; - addFormVisible: boolean; -} -export class JsonArray extends Component { - constructor(props: JsonArrayProps) { - super(props); - const keyPath = [...props.keyPath, props.name]; - this.state = { - data: props.data, - name: props.name, - keyPath, - deep: props.deep, - nextDeep: props.deep + 1, - collapsed: props.isCollapsed(keyPath, props.deep, props.data), - addFormVisible: false, - }; - - // Bind - this.handleCollapseMode = this.handleCollapseMode.bind(this); - this.handleRemoveItem = this.handleRemoveItem.bind(this); - this.handleAddMode = this.handleAddMode.bind(this); - this.handleAddValueAdd = this.handleAddValueAdd.bind(this); - this.handleAddValueCancel = this.handleAddValueCancel.bind(this); - this.handleEditValue = this.handleEditValue.bind(this); - this.onChildUpdate = this.onChildUpdate.bind(this); - this.renderCollapsed = this.renderCollapsed.bind(this); - this.renderNotCollapsed = this.renderNotCollapsed.bind(this); - } - - static getDerivedStateFromProps(props: JsonArrayProps, state: JsonArrayState) { - return props.data !== state.data ? { data: props.data } : null; - } - - onChildUpdate(childKey: string, childData: any) { - const { data, keyPath } = this.state; - // Update data - // @ts-ignore - data[childKey] = childData; - // Put new data - this.setState({ - data, - }); - // Spread - const { onUpdate } = this.props; - const size = keyPath.length; - onUpdate(keyPath[size - 1], data); - } - - handleAddMode() { - this.setState({ - addFormVisible: true, - }); - } - - handleCollapseMode() { - this.setState((state) => ({ - collapsed: !state.collapsed, - })); - } - - handleRemoveItem(index: number) { - return () => { - const { beforeRemoveAction, logger } = this.props; - const { data, keyPath, nextDeep: deep } = this.state; - const oldValue = data[index]; - - // Before Remove Action - beforeRemoveAction(index, keyPath, deep, oldValue) - .then(() => { - const deltaUpdateResult = { - keyPath, - deep, - key: index, - oldValue, - type: REMOVE_DELTA_TYPE, - }; - - data.splice(index, 1); - this.setState({ data }); - - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], data); - // Spread delta update - onDeltaUpdate(deltaUpdateResult); - }) - .catch(logger.error); - }; - } - - handleAddValueAdd({ newValue }: any) { - const { data, keyPath, nextDeep: deep } = this.state; - const { beforeAddAction, logger } = this.props; - - beforeAddAction(data.length, keyPath, deep, newValue) - .then(() => { - // Update data - const newData = [...data, newValue]; - this.setState({ - data: newData, - }); - // Cancel add to close - this.handleAddValueCancel(); - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], newData); - // Spread delta update - onDeltaUpdate({ - type: ADD_DELTA_TYPE, - keyPath, - deep, - key: newData.length - 1, - newValue, - }); - }) - .catch(logger.error); - } - - handleAddValueCancel() { - this.setState({ - addFormVisible: false, - }); - } - - handleEditValue({ key, value }: any) { - return new Promise((resolve, reject) => { - const { beforeUpdateAction } = this.props; - const { data, keyPath, nextDeep: deep } = this.state; - - // Old value - const oldValue = data[key]; - - // Before update action - beforeUpdateAction(key, keyPath, deep, oldValue, value) - .then(() => { - // Update value - data[key] = value; - // Set state - this.setState({ - data, - }); - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], data); - // Spread delta update - onDeltaUpdate({ - type: UPDATE_DELTA_TYPE, - keyPath, - deep, - key, - newValue: value, - oldValue, - }); - // Resolve - resolve(undefined); - }) - .catch(reject); - }); - } - - renderCollapsed() { - const { name, data, keyPath, deep } = this.state; - const { handleRemove, readOnly, getStyle, dataType, minusMenuElement } = this.props; - const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType); - - const isReadOnly = readOnly(name, data, keyPath, deep, dataType); - - const removeItemButton = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: minus, - }); - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - - - [...] {data.length} {data.length === 1 ? 'item' : 'items'} - - {!isReadOnly && removeItemButton} - - ); - /* eslint-enable */ - } - - renderNotCollapsed() { - const { name, data, keyPath, deep, addFormVisible, nextDeep } = this.state; - const { - isCollapsed, - handleRemove, - onDeltaUpdate, - readOnly, - getStyle, - dataType, - addButtonElement, - cancelButtonElement, - editButtonElement, - inputElementGenerator, - textareaElementGenerator, - minusMenuElement, - plusMenuElement, - beforeRemoveAction, - beforeAddAction, - beforeUpdateAction, - logger, - onSubmitValueParser, - } = this.props; - const { minus, plus, delimiter, ul, addForm } = getStyle(name, data, keyPath, deep, dataType); - - const isReadOnly = readOnly(name, data, keyPath, deep, dataType); - - const addItemButton = React.cloneElement(plusMenuElement, { - onClick: this.handleAddMode, - className: 'rejt-plus-menu', - style: plus, - }); - const removeItemButton = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: minus, - }); - - const onlyValue = true; - const startObject = '['; - const endObject = ']'; - return ( - - - {startObject} - - {!addFormVisible && addItemButton} -
          - {data.map((item, index) => ( - - ))} -
        - {!isReadOnly && addFormVisible && ( -
        - -
        - )} - - {endObject} - - {!isReadOnly && removeItemButton} -
        - ); - } - - render() { - const { name, collapsed, data, keyPath, deep } = this.state; - const { dataType, getStyle } = this.props; - const value = collapsed ? this.renderCollapsed() : this.renderNotCollapsed(); - const style = getStyle(name, data, keyPath, deep, dataType); - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( -
        - - - {name} :{' '} - - - {value} -
        - ); - /* eslint-enable */ - } -} - -interface JsonArrayProps { - data: any[]; - name: string; - isCollapsed: (...args: any) => any; - keyPath?: string[]; - deep?: number; - handleRemove?: (...args: any) => any; - onUpdate: (...args: any) => any; - onDeltaUpdate: (...args: any) => any; - readOnly: (...args: any) => any; - dataType?: string; - getStyle: (...args: any) => any; - addButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - editButtonElement?: ReactElement; - inputElementGenerator: (...args: any) => any; - textareaElementGenerator: (...args: any) => any; - minusMenuElement?: ReactElement; - plusMenuElement?: ReactElement; - beforeRemoveAction?: (...args: any) => any; - beforeAddAction?: (...args: any) => any; - beforeUpdateAction?: (...args: any) => any; - logger: any; - onSubmitValueParser: (...args: any) => any; -} - -// @ts-ignore -JsonArray.defaultProps = { - keyPath: [], - deep: 0, - minusMenuElement: - , - plusMenuElement: + , -}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx deleted file mode 100644 index 9f7df7a4b9a2..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonFunctionValue.tsx +++ /dev/null @@ -1,220 +0,0 @@ -/* eslint-disable react/sort-comp */ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable react/button-has-type */ -import React, { Component, ReactElement } from 'react'; - -import { isComponentWillChange } from '../utils/objectTypes'; -import * as inputUsageTypes from '../types/inputUsageTypes'; - -interface JsonFunctionValueState { - value: JsonFunctionValueProps['value']; - name: JsonFunctionValueProps['name']; - keyPath: string[]; - deep: JsonFunctionValueProps['deep']; - editEnabled: boolean; - inputRef: any; -} - -export class JsonFunctionValue extends Component { - constructor(props: JsonFunctionValueProps) { - super(props); - const keyPath = [...props.keyPath, props.name]; - this.state = { - value: props.value, - name: props.name, - keyPath, - deep: props.deep, - editEnabled: false, - inputRef: null, - }; - - // Bind - this.handleEditMode = this.handleEditMode.bind(this); - this.refInput = this.refInput.bind(this); - this.handleCancelEdit = this.handleCancelEdit.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.onKeydown = this.onKeydown.bind(this); - } - - static getDerivedStateFromProps(props: JsonFunctionValueProps, state: JsonFunctionValueState) { - return props.value !== state.value ? { value: props.value } : null; - } - - componentDidUpdate() { - const { editEnabled, inputRef, name, value, keyPath, deep } = this.state; - const { readOnly, dataType } = this.props; - const readOnlyResult = readOnly(name, value, keyPath, deep, dataType); - - if (editEnabled && !readOnlyResult && typeof inputRef.focus === 'function') { - inputRef.focus(); - } - } - - componentDidMount() { - document.addEventListener('keydown', this.onKeydown); - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeydown); - } - - onKeydown(event: KeyboardEvent) { - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; - if (event.code === 'Enter' || event.key === 'Enter') { - event.preventDefault(); - this.handleEdit(); - } - if (event.code === 'Escape' || event.key === 'Escape') { - event.preventDefault(); - this.handleCancelEdit(); - } - } - - handleEdit() { - const { handleUpdateValue, originalValue, logger, onSubmitValueParser, keyPath } = this.props; - const { inputRef, name, deep } = this.state; - if (!inputRef) return; - - const newValue = onSubmitValueParser(true, keyPath, deep, name, inputRef.value); - - const result = { - value: newValue, - key: name, - }; - - // Run update - handleUpdateValue(result) - .then(() => { - // Cancel edit mode if necessary - if (!isComponentWillChange(originalValue, newValue)) { - this.handleCancelEdit(); - } - }) - .catch(logger.error); - } - - handleEditMode() { - this.setState({ - editEnabled: true, - }); - } - - refInput(node: any) { - // @ts-ignore - this.state.inputRef = node; - } - - handleCancelEdit() { - this.setState({ - editEnabled: false, - }); - } - - render() { - const { name, value, editEnabled, keyPath, deep } = this.state; - const { - handleRemove, - originalValue, - readOnly, - dataType, - getStyle, - editButtonElement, - cancelButtonElement, - textareaElementGenerator, - minusMenuElement, - keyPath: comeFromKeyPath, - } = this.props; - - const style = getStyle(name, originalValue, keyPath, deep, dataType); - let result = null; - let minusElement = null; - const resultOnlyResult = readOnly(name, originalValue, keyPath, deep, dataType); - - if (editEnabled && !resultOnlyResult) { - const textareaElement = textareaElementGenerator( - inputUsageTypes.VALUE, - comeFromKeyPath, - deep, - name, - originalValue, - dataType - ); - - const editButtonElementLayout = React.cloneElement(editButtonElement, { - onClick: this.handleEdit, - }); - const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { - onClick: this.handleCancelEdit, - }); - const textareaElementLayout = React.cloneElement(textareaElement, { - ref: this.refInput, - defaultValue: originalValue, - }); - - result = ( - - {textareaElementLayout} {cancelButtonElementLayout} - {editButtonElementLayout} - - ); - minusElement = null; - } else { - /* eslint-disable jsx-a11y/no-static-element-interactions */ - result = ( - - {value} - - ); - /* eslint-enable */ - const minusMenuLayout = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: style.minus, - }); - minusElement = resultOnlyResult ? null : minusMenuLayout; - } - - return ( -
      5. - - {name} :{' '} - - {result} - {minusElement} -
      6. - ); - } -} - -interface JsonFunctionValueProps { - name: string; - value: any; - originalValue?: any; - keyPath?: string[]; - deep?: number; - handleRemove?: (...args: any) => any; - handleUpdateValue?: (...args: any) => any; - readOnly: (...args: any) => any; - dataType?: string; - getStyle: (...args: any) => any; - editButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - textareaElementGenerator: (...args: any) => any; - minusMenuElement?: ReactElement; - logger: any; - onSubmitValueParser: (...args: any) => any; -} - -// @ts-ignore -JsonFunctionValue.defaultProps = { - keyPath: [], - deep: 0, - handleUpdateValue: () => {}, - editButtonElement: , - cancelButtonElement: , - minusMenuElement: - , -}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx deleted file mode 100644 index 618a380f883b..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonNode.tsx +++ /dev/null @@ -1,347 +0,0 @@ -import React, { Component, ReactElement } from 'react'; - -import { JsonValue } from './JsonValue'; -import { JsonObject } from './JsonObject'; -import { JsonArray } from './JsonArray'; -import { JsonFunctionValue } from './JsonFunctionValue'; -import { getObjectType } from '../utils/objectTypes'; -import * as dataTypes from '../types/dataTypes'; - -interface JsonNodeState { - data: JsonNodeProps['data']; - name: JsonNodeProps['name']; - keyPath: JsonNodeProps['keyPath']; - deep: JsonNodeProps['deep']; -} - -export class JsonNode extends Component { - constructor(props: JsonNodeProps) { - super(props); - this.state = { - data: props.data, - name: props.name, - keyPath: props.keyPath, - deep: props.deep, - }; - } - - static getDerivedStateFromProps(props: JsonNodeProps, state: JsonNodeState) { - return props.data !== state.data ? { data: props.data } : null; - } - - render() { - const { data, name, keyPath, deep } = this.state; - const { - isCollapsed, - handleRemove, - handleUpdateValue, - onUpdate, - onDeltaUpdate, - readOnly, - getStyle, - addButtonElement, - cancelButtonElement, - editButtonElement, - inputElementGenerator, - textareaElementGenerator, - minusMenuElement, - plusMenuElement, - beforeRemoveAction, - beforeAddAction, - beforeUpdateAction, - logger, - onSubmitValueParser, - } = this.props; - const readOnlyTrue = () => true; - - const dataType = getObjectType(data); - switch (dataType) { - case dataTypes.ERROR: - return ( - - ); - case dataTypes.OBJECT: - return ( - - ); - case dataTypes.ARRAY: - return ( - - ); - case dataTypes.STRING: - return ( - - ); - case dataTypes.NUMBER: - return ( - - ); - case dataTypes.BOOLEAN: - return ( - - ); - case dataTypes.DATE: - return ( - - ); - case dataTypes.NULL: - return ( - - ); - case dataTypes.UNDEFINED: - return ( - - ); - case dataTypes.FUNCTION: - return ( - - ); - case dataTypes.SYMBOL: - return ( - - ); - default: - return null; - } - } -} - -interface JsonNodeProps { - name: string; - data?: any; - isCollapsed: (...args: any) => any; - keyPath?: string[]; - deep?: number; - handleRemove?: (...args: any) => any; - handleUpdateValue?: (...args: any) => any; - onUpdate: (...args: any) => any; - onDeltaUpdate: (...args: any) => any; - readOnly: (...args: any) => any; - getStyle: (...args: any) => any; - addButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - editButtonElement?: ReactElement; - inputElementGenerator: (...args: any) => any; - textareaElementGenerator: (...args: any) => any; - minusMenuElement?: ReactElement; - plusMenuElement?: ReactElement; - beforeRemoveAction?: (...args: any) => any; - beforeAddAction?: (...args: any) => any; - beforeUpdateAction?: (...args: any) => any; - logger: object; - onSubmitValueParser: (...args: any) => any; -} - -/// @ts-ignore -JsonNode.defaultProps = { - keyPath: [], - deep: 0, -}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx deleted file mode 100644 index c2c51c03a9dc..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonObject.tsx +++ /dev/null @@ -1,361 +0,0 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -import React, { Component, ReactElement } from 'react'; - -import { JsonNode } from './JsonNode'; -import { JsonAddValue } from './JsonAddValue'; -import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from '../types/deltaTypes'; - -interface JsonObjectState { - name: string; - collapsed: ReturnType; - data: JsonObjectProps['data']; - keyPath: JsonObjectProps['keyPath']; - deep: JsonObjectProps['deep']; - nextDeep: number; - addFormVisible: boolean; -} - -export class JsonObject extends Component { - constructor(props: JsonObjectProps) { - super(props); - const keyPath = props.deep === -1 ? [] : [...props.keyPath, props.name]; - this.state = { - name: props.name, - data: props.data, - keyPath, - deep: props.deep, - nextDeep: props.deep + 1, - collapsed: props.isCollapsed(keyPath, props.deep, props.data), - addFormVisible: false, - }; - - // Bind - this.handleCollapseMode = this.handleCollapseMode.bind(this); - this.handleRemoveValue = this.handleRemoveValue.bind(this); - this.handleAddMode = this.handleAddMode.bind(this); - this.handleAddValueAdd = this.handleAddValueAdd.bind(this); - this.handleAddValueCancel = this.handleAddValueCancel.bind(this); - this.handleEditValue = this.handleEditValue.bind(this); - this.onChildUpdate = this.onChildUpdate.bind(this); - this.renderCollapsed = this.renderCollapsed.bind(this); - this.renderNotCollapsed = this.renderNotCollapsed.bind(this); - } - - static getDerivedStateFromProps(props: JsonObjectProps, state: JsonObjectState) { - return props.data !== state.data ? { data: props.data } : null; - } - - onChildUpdate(childKey: string, childData: any) { - const { data, keyPath } = this.state; - // Update data - // @ts-ignore - data[childKey] = childData; - // Put new data - this.setState({ - data, - }); - // Spread - const { onUpdate } = this.props; - const size = keyPath.length; - onUpdate(keyPath[size - 1], data); - } - - handleAddMode() { - this.setState({ - addFormVisible: true, - }); - } - - handleAddValueCancel() { - this.setState({ - addFormVisible: false, - }); - } - - handleAddValueAdd({ key, newValue }: any) { - const { data, keyPath, nextDeep: deep } = this.state; - const { beforeAddAction, logger } = this.props; - - beforeAddAction(key, keyPath, deep, newValue) - .then(() => { - // Update data - // @ts-ignore - data[key] = newValue; - this.setState({ - data, - }); - // Cancel add to close - this.handleAddValueCancel(); - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], data); - // Spread delta update - onDeltaUpdate({ - type: ADD_DELTA_TYPE, - keyPath, - deep, - key, - newValue, - }); - }) - .catch(logger.error); - } - - handleRemoveValue(key: string) { - return () => { - const { beforeRemoveAction, logger } = this.props; - const { data, keyPath, nextDeep: deep } = this.state; - // @ts-ignore - const oldValue = data[key]; - // Before Remove Action - beforeRemoveAction(key, keyPath, deep, oldValue) - .then(() => { - const deltaUpdateResult = { - keyPath, - deep, - key, - oldValue, - type: REMOVE_DELTA_TYPE, - }; - - // @ts-ignore - delete data[key]; - this.setState({ data }); - - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], data); - // Spread delta update - onDeltaUpdate(deltaUpdateResult); - }) - .catch(logger.error); - }; - } - - handleCollapseMode() { - this.setState((state) => ({ - collapsed: !state.collapsed, - })); - } - - handleEditValue({ key, value }: any) { - return new Promise((resolve, reject) => { - const { beforeUpdateAction } = this.props; - const { data, keyPath, nextDeep: deep } = this.state; - - // Old value - // @ts-ignore - const oldValue = data[key]; - - // Before update action - beforeUpdateAction(key, keyPath, deep, oldValue, value) - .then(() => { - // Update value - // @ts-ignore - data[key] = value; - // Set state - this.setState({ - data, - }); - // Spread new update - const { onUpdate, onDeltaUpdate } = this.props; - onUpdate(keyPath[keyPath.length - 1], data); - // Spread delta update - onDeltaUpdate({ - type: UPDATE_DELTA_TYPE, - keyPath, - deep, - key, - newValue: value, - oldValue, - }); - // Resolve - resolve(); - }) - .catch(reject); - }); - } - - renderCollapsed() { - const { name, keyPath, deep, data } = this.state; - const { handleRemove, readOnly, dataType, getStyle, minusMenuElement } = this.props; - - const { minus, collapsed } = getStyle(name, data, keyPath, deep, dataType); - const keyList = Object.getOwnPropertyNames(data); - - const isReadOnly = readOnly(name, data, keyPath, deep, dataType); - - const removeItemButton = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: minus, - }); - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( - - - {'{...}'} {keyList.length} {keyList.length === 1 ? 'key' : 'keys'} - - {!isReadOnly && removeItemButton} - - ); - /* eslint-enable */ - } - - renderNotCollapsed() { - const { name, data, keyPath, deep, nextDeep, addFormVisible } = this.state; - const { - isCollapsed, - handleRemove, - onDeltaUpdate, - readOnly, - getStyle, - dataType, - addButtonElement, - cancelButtonElement, - editButtonElement, - inputElementGenerator, - textareaElementGenerator, - minusMenuElement, - plusMenuElement, - beforeRemoveAction, - beforeAddAction, - beforeUpdateAction, - logger, - onSubmitValueParser, - } = this.props; - - const { minus, plus, addForm, ul, delimiter } = getStyle(name, data, keyPath, deep, dataType); - const keyList = Object.getOwnPropertyNames(data); - - const isReadOnly = readOnly(name, data, keyPath, deep, dataType); - - const addItemButton = React.cloneElement(plusMenuElement, { - onClick: this.handleAddMode, - className: 'rejt-plus-menu', - style: plus, - }); - const removeItemButton = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: minus, - }); - - const list = keyList.map((key) => ( - - )); - - const startObject = '{'; - const endObject = '}'; - - return ( - - - {startObject} - - {!isReadOnly && addItemButton} -
          - {list} -
        - {!isReadOnly && addFormVisible && ( -
        - -
        - )} - - {endObject} - - {!isReadOnly && removeItemButton} -
        - ); - } - - render() { - const { name, collapsed, data, keyPath, deep } = this.state; - const { getStyle, dataType } = this.props; - const value = collapsed ? this.renderCollapsed() : this.renderNotCollapsed(); - const style = getStyle(name, data, keyPath, deep, dataType); - - /* eslint-disable jsx-a11y/no-static-element-interactions */ - return ( -
        - - - {name} :{' '} - - - {value} -
        - ); - /* eslint-enable */ - } -} - -interface JsonObjectProps { - data: Record; - name: string; - isCollapsed: (...args: any) => any; - keyPath?: string[]; - deep?: number; - handleRemove?: (...args: any) => any; - onUpdate: (...args: any) => any; - onDeltaUpdate: (...args: any) => any; - readOnly: (...args: any) => any; - dataType?: string; - getStyle: (...args: any) => any; - addButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - editButtonElement?: ReactElement; - inputElementGenerator: (...args: any) => any; - textareaElementGenerator: (...args: any) => any; - minusMenuElement?: ReactElement; - plusMenuElement?: ReactElement; - beforeRemoveAction?: (...args: any) => any; - beforeAddAction?: (...args: any) => any; - beforeUpdateAction?: (...args: any) => any; - logger: any; - onSubmitValueParser: (...args: any) => any; -} - -// @ts-ignore -JsonObject.defaultProps = { - keyPath: [], - deep: 0, - minusMenuElement: - , - plusMenuElement: + , -}; diff --git a/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx b/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx deleted file mode 100644 index 4f271b63b0a0..000000000000 --- a/lib/components/src/controls/react-editable-json-tree/components/JsonValue.tsx +++ /dev/null @@ -1,209 +0,0 @@ -/* eslint-disable jsx-a11y/click-events-have-key-events */ -/* eslint-disable react/sort-comp */ -/* eslint-disable react/button-has-type */ -import React, { Component, ReactElement } from 'react'; - -import { isComponentWillChange } from '../utils/objectTypes'; -import * as inputUsageTypes from '../types/inputUsageTypes'; - -interface JsonValueState { - value: JsonValueProps['value']; - name: JsonValueProps['name']; - keyPath: string[]; - deep: JsonValueProps['deep']; - editEnabled: boolean; - inputRef: any; -} - -export class JsonValue extends Component { - constructor(props: JsonValueProps) { - super(props); - const keyPath = [...props.keyPath, props.name]; - this.state = { - value: props.value, - name: props.name, - keyPath, - deep: props.deep, - editEnabled: false, - inputRef: null, - }; - - // Bind - this.handleEditMode = this.handleEditMode.bind(this); - this.refInput = this.refInput.bind(this); - this.handleCancelEdit = this.handleCancelEdit.bind(this); - this.handleEdit = this.handleEdit.bind(this); - this.onKeydown = this.onKeydown.bind(this); - } - - static getDerivedStateFromProps(props: JsonValueProps, state: JsonValueState) { - return props.value !== state.value ? { value: props.value } : null; - } - - componentDidUpdate() { - const { editEnabled, inputRef, name, value, keyPath, deep } = this.state; - const { readOnly, dataType } = this.props; - const isReadOnly = readOnly(name, value, keyPath, deep, dataType); - - if (editEnabled && !isReadOnly && typeof inputRef.focus === 'function') { - inputRef.focus(); - } - } - - componentDidMount() { - document.addEventListener('keydown', this.onKeydown); - } - - componentWillUnmount() { - document.removeEventListener('keydown', this.onKeydown); - } - - onKeydown(event: KeyboardEvent) { - if (event.altKey || event.ctrlKey || event.metaKey || event.shiftKey || event.repeat) return; - if (event.code === 'Enter' || event.key === 'Enter') { - event.preventDefault(); - this.handleEdit(); - } - if (event.code === 'Escape' || event.key === 'Escape') { - event.preventDefault(); - this.handleCancelEdit(); - } - } - - handleEdit() { - const { handleUpdateValue, originalValue, logger, onSubmitValueParser, keyPath } = this.props; - const { inputRef, name, deep } = this.state; - if (!inputRef) return; - - const newValue = onSubmitValueParser(true, keyPath, deep, name, inputRef.value); - - const result = { - value: newValue, - key: name, - }; - - // Run update - handleUpdateValue(result) - .then(() => { - // Cancel edit mode if necessary - if (!isComponentWillChange(originalValue, newValue)) { - this.handleCancelEdit(); - } - }) - .catch(logger.error); - } - - handleEditMode() { - this.setState({ - editEnabled: true, - }); - } - - refInput(node: any) { - // @ts-ignore - this.state.inputRef = node; - } - - handleCancelEdit() { - this.setState({ - editEnabled: false, - }); - } - - render() { - const { name, value, editEnabled, keyPath, deep } = this.state; - const { - handleRemove, - originalValue, - readOnly, - dataType, - getStyle, - editButtonElement, - cancelButtonElement, - inputElementGenerator, - minusMenuElement, - keyPath: comeFromKeyPath, - } = this.props; - - const style = getStyle(name, originalValue, keyPath, deep, dataType); - const isReadOnly = readOnly(name, originalValue, keyPath, deep, dataType); - const isEditing = editEnabled && !isReadOnly; - const inputElement = inputElementGenerator( - inputUsageTypes.VALUE, - comeFromKeyPath, - deep, - name, - originalValue, - dataType - ); - - const editButtonElementLayout = React.cloneElement(editButtonElement, { - onClick: this.handleEdit, - }); - const cancelButtonElementLayout = React.cloneElement(cancelButtonElement, { - onClick: this.handleCancelEdit, - }); - const inputElementLayout = React.cloneElement(inputElement, { - ref: this.refInput, - defaultValue: JSON.stringify(originalValue), - }); - const minusMenuLayout = React.cloneElement(minusMenuElement, { - onClick: handleRemove, - className: 'rejt-minus-menu', - style: style.minus, - }); - - return ( -
      7. - - {name} - {' : '} - - {isEditing ? ( - - {inputElementLayout} {cancelButtonElementLayout} - {editButtonElementLayout} - - ) : ( - - {String(value)} - - )} - {!isReadOnly && !isEditing && minusMenuLayout} -
      8. - ); - } -} - -interface JsonValueProps { - name: string; - value: any; - originalValue?: any; - keyPath?: string[]; - deep?: number; - handleRemove?: (...args: any) => any; - handleUpdateValue?: (...args: any) => any; - readOnly: (...args: any) => any; - dataType?: string; - getStyle: (...args: any) => any; - editButtonElement?: ReactElement; - cancelButtonElement?: ReactElement; - inputElementGenerator: (...args: any) => any; - minusMenuElement?: ReactElement; - logger: any; - onSubmitValueParser: (...args: any) => any; -} - -// @ts-ignore -JsonValue.defaultProps = { - keyPath: [], - deep: 0, - handleUpdateValue: () => Promise.resolve(), - editButtonElement: , - cancelButtonElement: , - minusMenuElement: - , -}; diff --git a/lib/components/src/controls/react-editable-json-tree/index.tsx b/lib/components/src/controls/react-editable-json-tree/index.tsx index 6c6a43c6bd4d..71df7597010e 100644 --- a/lib/components/src/controls/react-editable-json-tree/index.tsx +++ b/lib/components/src/controls/react-editable-json-tree/index.tsx @@ -1,16 +1,19 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; +import React, { Component, ReactElement } from 'react'; -import JsonNode from './components/JsonNode'; +import { JsonNode } from './JsonNodes'; import { value, object, array } from './utils/styles'; import { ADD_DELTA_TYPE, REMOVE_DELTA_TYPE, UPDATE_DELTA_TYPE } from './types/deltaTypes'; import { getObjectType } from './utils/objectTypes'; -import DATA_TYPES from './types/dataTypes'; -import INPUT_USAGE_TYPES from './types/inputUsageTypes'; +import * as DATA_TYPES from './types/dataTypes'; +import * as INPUT_USAGE_TYPES from './types/inputUsageTypes'; import { parse } from './utils/parse'; -export class JsonTree extends Component { - constructor(props) { +interface JsonTreeState { + data: JsonTreeProps['data']; + rootName: JsonTreeProps['rootName']; +} +export class JsonTree extends Component { + constructor(props: JsonTreeProps) { super(props); this.state = { data: props.data, @@ -21,7 +24,7 @@ export class JsonTree extends Component { this.removeRoot = this.removeRoot.bind(this); } - static getDerivedStateFromProps(props, state) { + static getDerivedStateFromProps(props: JsonTreeProps, state: JsonTreeState) { if (props.data !== state.data || props.rootName !== state.rootName) { return { data: props.data, @@ -31,7 +34,7 @@ export class JsonTree extends Component { return null; } - onUpdate(key, data) { + onUpdate(key: string, data: any) { this.setState({ data }); this.props.onFullyUpdate(data); } @@ -59,7 +62,7 @@ export class JsonTree extends Component { beforeUpdateAction, logger, onSubmitValueParser, - fallback, + fallback = null, } = this.props; // Node type @@ -71,10 +74,12 @@ export class JsonTree extends Component { } let inputElementFunction = inputElement; if (inputElement && getObjectType(inputElement) !== 'Function') { + // @ts-ignore inputElementFunction = () => inputElement; } let textareaElementFunction = textareaElement; if (textareaElement && getObjectType(textareaElement) !== 'Function') { + // @ts-ignore textareaElementFunction = () => textareaElement; } @@ -84,18 +89,17 @@ export class JsonTree extends Component { any} getStyle={getStyle} addButtonElement={addButtonElement} cancelButtonElement={cancelButtonElement} editButtonElement={editButtonElement} - inputElementGenerator={inputElementFunction} - textareaElementGenerator={textareaElementFunction} + inputElementGenerator={inputElementFunction as (...args: any) => any} + textareaElementGenerator={textareaElementFunction as (...args: any) => any} minusMenuElement={minusMenuElement} plusMenuElement={plusMenuElement} handleRemove={this.removeRoot} @@ -113,28 +117,30 @@ export class JsonTree extends Component { } } -JsonTree.propTypes = { - data: PropTypes.any.isRequired, - rootName: PropTypes.string, - isCollapsed: PropTypes.func, - onFullyUpdate: PropTypes.func, - onDeltaUpdate: PropTypes.func, - readOnly: PropTypes.oneOfType([PropTypes.bool, PropTypes.func]), - getStyle: PropTypes.func, - addButtonElement: PropTypes.element, - cancelButtonElement: PropTypes.element, - editButtonElement: PropTypes.element, - inputElement: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), - textareaElement: PropTypes.oneOfType([PropTypes.element, PropTypes.func]), - minusMenuElement: PropTypes.element, - plusMenuElement: PropTypes.element, - beforeRemoveAction: PropTypes.func, - beforeAddAction: PropTypes.func, - beforeUpdateAction: PropTypes.func, - logger: PropTypes.object, - onSubmitValueParser: PropTypes.func, -}; +interface JsonTreeProps { + data: any; + rootName?: string; + isCollapsed?: (...args: any) => any; + onFullyUpdate?: (...args: any) => any; + onDeltaUpdate?: (...args: any) => any; + readOnly?: boolean | ((...args: any) => any); + getStyle?: (...args: any) => any; + addButtonElement?: ReactElement; + cancelButtonElement?: ReactElement; + editButtonElement?: ReactElement; + inputElement?: ReactElement | ((...args: any) => ReactElement); + textareaElement?: ReactElement | ((...args: any) => ReactElement); + minusMenuElement?: ReactElement; + plusMenuElement?: ReactElement; + fallback?: ReactElement; + beforeRemoveAction?: (...args: any) => Promise; + beforeAddAction?: (...args: any) => Promise; + beforeUpdateAction?: (...args: any) => any; + logger?: object; + onSubmitValueParser?: (...args: any) => any; +} +// @ts-ignore JsonTree.defaultProps = { rootName: 'root', isCollapsed: (keyPath, deep) => deep !== -1, @@ -149,21 +155,18 @@ JsonTree.defaultProps = { return value; } }, - /* eslint-disable no-unused-vars */ - readOnly: (keyName, data, keyPath, deep, dataType) => false, - onFullyUpdate: (data) => {}, - onDeltaUpdate: ({ type, keyPath, deep, key, newValue, oldValue }) => {}, - beforeRemoveAction: (key, keyPath, deep, oldValue) => new Promise((resolve) => resolve()), - beforeAddAction: (key, keyPath, deep, newValue) => new Promise((resolve) => resolve()), - beforeUpdateAction: (key, keyPath, deep, oldValue, newValue) => - new Promise((resolve) => resolve()), + readOnly: () => false, + onFullyUpdate: () => {}, + onDeltaUpdate: () => {}, + beforeRemoveAction: () => Promise.resolve(), + beforeAddAction: () => Promise.resolve(), + beforeUpdateAction: () => Promise.resolve(), logger: { error: () => {} }, onSubmitValueParser: (isEditMode, keyPath, deep, name, rawValue) => parse(rawValue), - inputElement: (usage, keyPath, deep, keyName, data, dataType) => , - textareaElement: (usage, keyPath, deep, keyName, data, dataType) =>