diff --git a/packages/admin-ui/package.json b/packages/admin-ui/package.json index 5ee694dc99e..7064f37dc0f 100644 --- a/packages/admin-ui/package.json +++ b/packages/admin-ui/package.json @@ -27,10 +27,12 @@ "@webiny/react-composition": "0.0.0", "@webiny/react-router": "0.0.0", "@webiny/utils": "0.0.0", + "ace-builds": "^1.37.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "mobx": "^6.9.0", "react": "18.2.0", + "react-ace": "^13.0.0", "tailwind-merge": "^2.4.0", "tailwindcss": "^3.4.6", "tailwindcss-animate": "^1.0.7" diff --git a/packages/admin-ui/src/CodeEditor/CodeEditor.stories.tsx b/packages/admin-ui/src/CodeEditor/CodeEditor.stories.tsx new file mode 100644 index 00000000000..eaf1537e495 --- /dev/null +++ b/packages/admin-ui/src/CodeEditor/CodeEditor.stories.tsx @@ -0,0 +1,79 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { CodeEditor } from "./CodeEditor"; + +const meta: Meta = { + title: "Components/Form/CodeEditor", + component: CodeEditor, + tags: ["autodocs"], + argTypes: { + onChange: { action: "onChange" } + }, + parameters: { + layout: "padded" + } +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const WithLabel: Story = { + args: { + label: "Any field label" + } +}; + +export const WithLabelRequired: Story = { + args: { + ...Default.args, + label: "Any field label", + required: true + } +}; + +export const WithDescription: Story = { + args: { + ...Default.args, + description: "Provide the required information for processing your request." + } +}; + +export const WithNotes: Story = { + args: { + ...Default.args, + note: "Note: Ensure your selection or input is accurate before proceeding." + } +}; + +export const WithErrors: Story = { + args: { + ...Default.args, + validation: { + isValid: false, + message: "This field is required." + } + } +}; + +export const Disabled: Story = { + args: { + ...Default.args, + label: "Any field label", + disabled: true + } +}; + +export const FullExample: Story = { + args: { + ...Default.args, + label: "Any field label", + required: true, + description: "Provide the required information for processing your request.", + note: "Note: Ensure your selection or input is accurate before proceeding.", + validation: { + isValid: false, + message: "This field is required." + } + } +}; diff --git a/packages/admin-ui/src/CodeEditor/CodeEditor.tsx b/packages/admin-ui/src/CodeEditor/CodeEditor.tsx new file mode 100644 index 00000000000..66c4df96e8f --- /dev/null +++ b/packages/admin-ui/src/CodeEditor/CodeEditor.tsx @@ -0,0 +1,38 @@ +import React, { useMemo } from "react"; +import { makeDecoratable } from "~/utils"; +import { CodeEditorPrimitive, CodeEditorPrimitiveProps } from "./CodeEditorPrimitive"; +import { + FormComponentDescription, + FormComponentErrorMessage, + FormComponentLabel, + FormComponentNote, + FormComponentProps +} from "~/FormComponent"; + +type CodeEditorProps = CodeEditorPrimitiveProps & FormComponentProps; + +const DecoratableCodeEditor = ({ + label, + description, + note, + required, + disabled, + validation, + ...props +}: CodeEditorProps) => { + const { isValid: validationIsValid, message: validationMessage } = validation || {}; + const invalid = useMemo(() => validationIsValid === false, [validationIsValid]); + + return ( +
+ + + + + +
+ ); +}; +const CodeEditor = makeDecoratable("CodeEditor", DecoratableCodeEditor); + +export { CodeEditor, type CodeEditorProps }; diff --git a/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.stories.tsx b/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.stories.tsx new file mode 100644 index 00000000000..c3b36dd21ad --- /dev/null +++ b/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.stories.tsx @@ -0,0 +1,19 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { CodeEditorPrimitive } from "./CodeEditorPrimitive"; + +const meta: Meta = { + title: "Components/Form Primitives/CodeEditor", + component: CodeEditorPrimitive, + tags: ["autodocs"], + argTypes: { + onChange: { action: "onChange" } + }, + parameters: { + layout: "padded" + } +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.tsx b/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.tsx new file mode 100644 index 00000000000..801e89dea40 --- /dev/null +++ b/packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import AceEditor from "react-ace"; +import "ace-builds/webpack-resolver"; +// Modes +import "ace-builds/src-noconflict/mode-html"; +import "ace-builds/src-noconflict/mode-json"; +// Extensions +import "ace-builds/src-noconflict/ext-searchbox"; +// Themes +import "ace-builds/src-noconflict/theme-github"; +import "ace-builds/src-noconflict/theme-twilight"; +import "ace-builds/src-noconflict/theme-chrome"; + +interface CodeEditorPrimitiveProps extends React.ComponentProps { + mode: string; + disabled?: boolean; +} + +const CodeEditorPrimitive = ({ + value, + theme = "github", + disabled, + ...rest +}: CodeEditorPrimitiveProps) => { + return ( + + ); +}; + +export { CodeEditorPrimitive, type CodeEditorPrimitiveProps }; diff --git a/packages/admin-ui/src/CodeEditor/index.ts b/packages/admin-ui/src/CodeEditor/index.ts new file mode 100644 index 00000000000..21f71d801ad --- /dev/null +++ b/packages/admin-ui/src/CodeEditor/index.ts @@ -0,0 +1,2 @@ +export * from "./CodeEditorPrimitive"; +export * from "./CodeEditor"; diff --git a/packages/admin-ui/src/index.ts b/packages/admin-ui/src/index.ts index 4ccdcd0fe7f..e776720146a 100644 --- a/packages/admin-ui/src/index.ts +++ b/packages/admin-ui/src/index.ts @@ -3,6 +3,7 @@ export * from "./Avatar"; export * from "./Button"; export * from "./Card"; export * from "./Checkbox"; +export * from "./CodeEditor"; export * from "./FormComponent"; export * from "./Grid"; export * from "./Heading"; diff --git a/packages/ui/package.json b/packages/ui/package.json index ff5876fe3bc..9a796989204 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -31,7 +31,6 @@ "@tanstack/react-table": "^8.5.22", "@webiny/admin-ui": "0.0.0", "@webiny/utils": "0.0.0", - "brace": "^0.11.1", "classnames": "^2.5.1", "cropperjs": "^1.4.3", "dot-prop-immutable": "^2.1.1", @@ -43,7 +42,6 @@ "material-components-web": "^14.0.0", "nprogress": "^0.2.0", "nuka-carousel": "4.7.1", - "react-ace": "^6.1.4", "react-butterfiles": "^1.3.3", "react-color": "^2.19.3", "react-columned": "^1.1.3", diff --git a/packages/ui/src/CodeEditor/CodeEditor.tsx b/packages/ui/src/CodeEditor/CodeEditor.tsx index 00f21480cd9..1fd9eae1556 100644 --- a/packages/ui/src/CodeEditor/CodeEditor.tsx +++ b/packages/ui/src/CodeEditor/CodeEditor.tsx @@ -1,31 +1,6 @@ import React from "react"; +import { CodeEditor as AdminCodeEditor } from "@webiny/admin-ui"; import { FormComponentProps } from "~/types"; -import { css } from "emotion"; - -import AceEditor from "react-ace"; -// Modes -import "brace/mode/html"; -import "brace/mode/json"; -// Extensions -import "brace/ext/searchbox"; -// Themes -import "brace/theme/github"; -import "brace/theme/twilight"; -import "brace/theme/chrome"; -import { FormElementMessage } from "~/FormElementMessage"; - -/** - * Controls the helper text below the checkbox. - * @type {string} - */ -const webinyCheckboxHelperText = css( - {}, - { - "&.mdc-text-field-helper-text": { - paddingTop: 5 - } - } -); interface Props extends FormComponentProps { mode: string; @@ -39,46 +14,9 @@ interface Props extends FormComponentProps { } /** - * CodeEditor component can be used to store simple boolean values. + * @deprecated This component is deprecated and will be removed in future releases. + * Please find out the new `CodeEditor` component props from the `@webiny/admin-ui` package instead. */ -class CodeEditor extends React.Component { - onChange = (value: string) => { - this.props.onChange && this.props.onChange(value); - }; - - public override render() { - const { value, description, validation, theme = "github", ...rest } = this.props; - - const { isValid: validationIsValid, message: validationMessage } = validation || {}; - - return ( - - - - {validationIsValid === false && ( - - {validationMessage} - - )} - - {validationIsValid !== false && description && ( - - {description} - - )} - - ); - } -} - -export { CodeEditor }; +export const CodeEditor = ({ readOnly, ...props }: Props) => { + return ; +}; diff --git a/packages/ui/src/CodeEditor/README.md b/packages/ui/src/CodeEditor/README.md deleted file mode 100644 index 4902697b07a..00000000000 --- a/packages/ui/src/CodeEditor/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# CodeEditor - -### Additional information - -https://github.com/securingsincity/react-ace - -### Description - -`CodeEditor` is a wrapper around one of the most popular code editors called `Ace` editor. Have in mind that you still -have to import `brace` as a dependency (after importing `CodeEditor`) for different code formats and editor types. - -### Import - -```js -import brace from "brace"; -import { CodeEditor } from "@webiny/ui/CodeEditor"; - -// Make sure to import this after CodeEditor. Check official docs for available modes and themes. -import "brace/mode/json"; -import "brace/theme/github"; -``` diff --git a/yarn.lock b/yarn.lock index 21d446ec208..580c33f00a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14886,6 +14886,7 @@ __metadata: "@webiny/react-composition": "npm:0.0.0" "@webiny/react-router": "npm:0.0.0" "@webiny/utils": "npm:0.0.0" + ace-builds: "npm:^1.37.1" chalk: "npm:^4.1.0" class-variance-authority: "npm:^0.7.0" clsx: "npm:^2.1.1" @@ -14895,6 +14896,7 @@ __metadata: postcss-loader: "npm:^6.2.1" prettier: "npm:^2.8.3" react: "npm:18.2.0" + react-ace: "npm:^13.0.0" rimraf: "npm:^5.0.5" sass: "npm:1.44.0" storybook: "npm:7.6.20" @@ -19170,7 +19172,6 @@ __metadata: "@webiny/utils": "npm:0.0.0" "@webiny/validation": "npm:0.0.0" babel-loader: "npm:^9.2.1" - brace: "npm:^0.11.1" classnames: "npm:^2.5.1" cropperjs: "npm:^1.4.3" dot-prop-immutable: "npm:^2.1.1" @@ -19185,7 +19186,6 @@ __metadata: nprogress: "npm:^0.2.0" nuka-carousel: "npm:4.7.1" raw-loader: "npm:^4.0.2" - react-ace: "npm:^6.1.4" react-butterfiles: "npm:^1.3.3" react-color: "npm:^2.19.3" react-columned: "npm:^1.1.3" @@ -19450,6 +19450,13 @@ __metadata: languageName: node linkType: hard +"ace-builds@npm:^1.36.3, ace-builds@npm:^1.37.1": + version: 1.37.1 + resolution: "ace-builds@npm:1.37.1" + checksum: 10/ae6a4fcac289a5edf4c1eb43492f6bbf65e146eb9542df31943593badcf77f6f9cfeff8904aec0c14fe332e5fbc6bf4a8173aa4101cab708e466cdb4b56839db + languageName: node + linkType: hard + "acorn-globals@npm:^7.0.0": version: 7.0.1 resolution: "acorn-globals@npm:7.0.1" @@ -24879,7 +24886,7 @@ __metadata: languageName: node linkType: hard -"diff-match-patch@npm:^1.0.4": +"diff-match-patch@npm:^1.0.4, diff-match-patch@npm:^1.0.5": version: 1.0.5 resolution: "diff-match-patch@npm:1.0.5" checksum: 10/fd1ab417eba9559bda752a4dfc9a8ac73fa2ca8b146d29d153964b437168e301c09d8a688fae0cd81d32dc6508a4918a94614213c85df760793f44e245173bb6 @@ -37789,6 +37796,22 @@ __metadata: languageName: node linkType: hard +"react-ace@npm:^13.0.0": + version: 13.0.0 + resolution: "react-ace@npm:13.0.0" + dependencies: + ace-builds: "npm:^1.36.3" + diff-match-patch: "npm:^1.0.5" + lodash.get: "npm:^4.4.2" + lodash.isequal: "npm:^4.5.0" + prop-types: "npm:^15.8.1" + peerDependencies: + react: ^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 10/93bf67725641d6533d25e930c309e86ea13f91299c6f3fa7ae90c35d647a8309beca2f031c31bf7fe2f3bc9d88831430befa3340eb6c1ed8d651529613363240 + languageName: node + linkType: hard + "react-ace@npm:^6.1.4": version: 6.6.0 resolution: "react-ace@npm:6.6.0"