Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(admin-ui): add CodeEditor component #4467

Open
wants to merge 1 commit into
base: feat/new-admin-ui
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
79 changes: 79 additions & 0 deletions packages/admin-ui/src/CodeEditor/CodeEditor.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Meta, StoryObj } from "@storybook/react";
import { CodeEditor } from "./CodeEditor";

const meta: Meta<typeof CodeEditor> = {
title: "Components/Form/CodeEditor",
component: CodeEditor,
tags: ["autodocs"],
argTypes: {
onChange: { action: "onChange" }
},
parameters: {
layout: "padded"
}
};

export default meta;
type Story = StoryObj<typeof CodeEditor>;

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."
}
}
};
38 changes: 38 additions & 0 deletions packages/admin-ui/src/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={"w-full"}>
<FormComponentLabel text={label} required={required} disabled={disabled} />
<FormComponentDescription text={description} />
<CodeEditorPrimitive {...props} disabled={disabled} />
<FormComponentErrorMessage text={validationMessage} invalid={invalid} />
<FormComponentNote text={note} />
</div>
);
};
const CodeEditor = makeDecoratable("CodeEditor", DecoratableCodeEditor);

export { CodeEditor, type CodeEditorProps };
19 changes: 19 additions & 0 deletions packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Meta, StoryObj } from "@storybook/react";
import { CodeEditorPrimitive } from "./CodeEditorPrimitive";

const meta: Meta<typeof CodeEditorPrimitive> = {
title: "Components/Form Primitives/CodeEditor",
component: CodeEditorPrimitive,
tags: ["autodocs"],
argTypes: {
onChange: { action: "onChange" }
},
parameters: {
layout: "padded"
}
};

export default meta;
type Story = StoryObj<typeof CodeEditorPrimitive>;

export const Default: Story = {};
39 changes: 39 additions & 0 deletions packages/admin-ui/src/CodeEditor/CodeEditorPrimitive.tsx
Original file line number Diff line number Diff line change
@@ -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<typeof AceEditor> {
mode: string;
disabled?: boolean;
}

const CodeEditorPrimitive = ({
value,
theme = "github",
disabled,
...rest
}: CodeEditorPrimitiveProps) => {
return (
<AceEditor
value={value ? String(value) : ""}
theme={theme}
readOnly={disabled}
{...rest}
width="100%"
editorProps={{
$blockScrolling: Infinity // Suppresses scrolling warning in console.
}}
/>
);
};

export { CodeEditorPrimitive, type CodeEditorPrimitiveProps };
2 changes: 2 additions & 0 deletions packages/admin-ui/src/CodeEditor/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./CodeEditorPrimitive";
export * from "./CodeEditor";
1 change: 1 addition & 0 deletions packages/admin-ui/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 0 additions & 2 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
"@svgr/webpack": "^6.1.1",
"@tanstack/react-table": "^8.5.22",
"@webiny/admin-ui": "0.0.0",
"brace": "^0.11.1",
"classnames": "^2.5.1",
"cropperjs": "^1.4.3",
"dot-prop-immutable": "^2.1.1",
Expand All @@ -42,7 +41,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",
Expand Down
74 changes: 6 additions & 68 deletions packages/ui/src/CodeEditor/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Props> {
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 (
<React.Fragment>
<AceEditor
value={value ? String(value) : ""}
theme={theme}
onChange={this.onChange}
{...rest}
width="100%"
className={"mdc-text-field"}
editorProps={{
$blockScrolling: Infinity // Suppresses scrolling warning in console.
}}
/>

{validationIsValid === false && (
<FormElementMessage error className={webinyCheckboxHelperText}>
{validationMessage}
</FormElementMessage>
)}

{validationIsValid !== false && description && (
<FormElementMessage className={webinyCheckboxHelperText}>
{description}
</FormElementMessage>
)}
</React.Fragment>
);
}
}

export { CodeEditor };
export const CodeEditor = ({ readOnly, ...props }: Props) => {
return <AdminCodeEditor disabled={readOnly} {...props} />;
};
21 changes: 0 additions & 21 deletions packages/ui/src/CodeEditor/README.md

This file was deleted.

Loading
Loading