diff --git a/src/categories/fixtures.ts b/src/categories/fixtures.ts index ddc277a83..aabbc37e9 100644 --- a/src/categories/fixtures.ts +++ b/src/categories/fixtures.ts @@ -1,8 +1,10 @@ import { CategoryFragment } from "@saleor/fragments/types/CategoryFragment"; -import { content } from "../storybook/stories/components/RichTextEditor"; +import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json"; import { CategoryDetails_category } from "./types/CategoryDetails"; +const content = richTextEditorFixtures.richTextEditor; + export const categories: CategoryFragment[] = [ { __typename: "Category", diff --git a/src/collections/fixtures.ts b/src/collections/fixtures.ts index f604aa199..4e515428f 100644 --- a/src/collections/fixtures.ts +++ b/src/collections/fixtures.ts @@ -1,7 +1,9 @@ -import { content } from "../storybook/stories/components/RichTextEditor"; +import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json"; import { CollectionDetails_collection } from "./types/CollectionDetails"; import { CollectionList_collections_edges_node } from "./types/CollectionList"; +const content = richTextEditorFixtures.richTextEditor; + export const collections: CollectionList_collections_edges_node[] = [ { __typename: "Collection", diff --git a/src/components/RichTextEditor/ImageEntity.tsx b/src/components/RichTextEditor/ImageEntity.tsx deleted file mode 100644 index 2bc502f3f..000000000 --- a/src/components/RichTextEditor/ImageEntity.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import Button from "@material-ui/core/Button"; -import ClickAwayListener from "@material-ui/core/ClickAwayListener"; -import Grow from "@material-ui/core/Grow"; -import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import Popper from "@material-ui/core/Popper"; -import { makeStyles } from "@material-ui/core/styles"; -import DeleteIcon from "@material-ui/icons/Delete"; -import { ContentState } from "draft-js"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -interface ImageEntityProps { - children: React.ReactNode; - contentState: ContentState; - entityKey: string; - onEdit: (entityKey: string) => void; - onRemove: (entityKey: string) => void; -} - -const useStyles = makeStyles( - theme => ({ - anchor: { - display: "inline-block" - }, - container: { - alignItems: "center", - display: "flex" - }, - image: { maxWidth: "100%" }, - inline: { - display: "inline-block" - }, - root: { - alignItems: "center", - display: "flex", - minHeight: 72, - padding: theme.spacing(1.5) - } - }), - { name: "ImageEntity" } -); - -const ImageEntity: React.FC = props => { - const { contentState, entityKey, onEdit, onRemove } = props; - const classes = useStyles(props); - - const [isOpened, setOpenStatus] = React.useState(false); - const anchor = React.useRef(); - - const disable = () => setOpenStatus(false); - const toggle = () => setOpenStatus(!isOpened); - - return ( - <> -
- - {({ TransitionProps, placement }) => ( - - - -
- - onRemove(entityKey)}> - - -
-
-
-
- )} -
-
- - - ); -}; -export default ImageEntity; diff --git a/src/components/RichTextEditor/ImageSource.tsx b/src/components/RichTextEditor/ImageSource.tsx deleted file mode 100644 index d3ae28ff9..000000000 --- a/src/components/RichTextEditor/ImageSource.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import TextField from "@material-ui/core/TextField"; -import { buttonMessages } from "@saleor/intl"; -import { AtomicBlockUtils, EditorState, EntityInstance } from "draft-js"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import Form from "../Form"; - -interface ImageSourceProps { - editorState: EditorState; - entity?: EntityInstance; - entityKey?: string; - entityType: { - type: string; - }; - onComplete: (updateState: EditorState) => void; - onClose: () => void; -} - -const ImageSource: React.FC = ({ - editorState, - entity, - entityKey, - entityType, - onComplete, - onClose -}) => { - const intl = useIntl(); - - const initial = entity ? entity.getData().href : ""; - - const handleSubmit = (href: string) => { - if (href) { - const content = editorState.getCurrentContent(); - if (entity) { - const nextContent = content.mergeEntityData(entityKey, { href }); - const nextState = EditorState.push( - editorState, - nextContent, - "apply-entity" - ); - onComplete(nextState); - } else { - const contentWithEntity = content.createEntity( - entityType.type, - "IMMUTABLE", - { href } - ); - const nextState = AtomicBlockUtils.insertAtomicBlock( - editorState, - contentWithEntity.getLastCreatedEntityKey(), - " " - ); - - onComplete(nextState); - } - } else { - onComplete(editorState); - } - }; - - return ( - -
handleSubmit(href)} - > - {({ data, change, submit }) => ( - <> - - - - - - - - - - - - )} -
-
- ); -}; - -export default ImageSource; diff --git a/src/components/RichTextEditor/LinkEntity.tsx b/src/components/RichTextEditor/LinkEntity.tsx deleted file mode 100644 index 86166fe2e..000000000 --- a/src/components/RichTextEditor/LinkEntity.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import Button from "@material-ui/core/Button"; -import ClickAwayListener from "@material-ui/core/ClickAwayListener"; -import Grow from "@material-ui/core/Grow"; -import IconButton from "@material-ui/core/IconButton"; -import Paper from "@material-ui/core/Paper"; -import Popper from "@material-ui/core/Popper"; -import { makeStyles } from "@material-ui/core/styles"; -import Typography from "@material-ui/core/Typography"; -import DeleteIcon from "@material-ui/icons/Delete"; -import { buttonMessages } from "@saleor/intl"; -import { ContentState } from "draft-js"; -import React from "react"; -import { FormattedMessage } from "react-intl"; - -import Link from "../Link"; - -interface LinkEntityProps { - children: React.ReactNode; - contentState: ContentState; - entityKey: string; - onEdit: (entityKey: string) => void; - onRemove: (entityKey: string) => void; -} - -const useStyles = makeStyles( - theme => ({ - anchor: { - display: "inline-block" - }, - container: { - alignItems: "center", - display: "flex" - }, - inline: { - display: "inline-block" - }, - popover: { - zIndex: 1 - }, - root: { - alignItems: "center", - display: "flex", - minHeight: 72, - padding: theme.spacing(1.5, 1.5, 1.5, 3) - }, - separator: { - backgroundColor: theme.palette.grey[300], - display: "inline-block", - height: 30, - marginLeft: theme.spacing(2), - marginRight: theme.spacing(), - width: 1 - } - }), - { name: "LinkEntity" } -); - -const LinkEntity: React.FC = props => { - const { children, contentState, entityKey, onEdit, onRemove } = props; - const classes = useStyles(props); - - const [isOpened, setOpenStatus] = React.useState(false); - const anchor = React.useRef(); - - const disable = () => setOpenStatus(false); - const toggle = () => setOpenStatus(!isOpened); - - return ( - <> -
- - {({ TransitionProps, placement }) => ( - - - -
- - {contentState.getEntity(entityKey).getData().url} - - - - onRemove(entityKey)}> - - -
-
-
-
- )} -
-
- - {children} - - - ); -}; -export default LinkEntity; diff --git a/src/components/RichTextEditor/LinkSource.tsx b/src/components/RichTextEditor/LinkSource.tsx deleted file mode 100644 index b6c55e7a5..000000000 --- a/src/components/RichTextEditor/LinkSource.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogTitle from "@material-ui/core/DialogTitle"; -import TextField from "@material-ui/core/TextField"; -import { buttonMessages } from "@saleor/intl"; -import { EditorState, EntityInstance, RichUtils } from "draft-js"; -import React from "react"; -import { FormattedMessage, useIntl } from "react-intl"; - -import Form from "../Form"; - -interface LinkSourceProps { - editorState: EditorState; - entity?: EntityInstance; - entityType: { - type: string; - }; - onComplete: (updateState: EditorState) => void; - onClose: () => void; -} - -const LinkSource: React.FC = ({ - editorState, - entity, - entityType, - onComplete, - onClose -}) => { - const intl = useIntl(); - const initial = entity ? entity.getData().url : ""; - - const handleSubmit = (url: string) => { - if (url) { - const content = editorState.getCurrentContent(); - const contentWithEntity = content.createEntity( - entityType.type, - "MUTABLE", - { url } - ); - const entityKey = contentWithEntity.getLastCreatedEntityKey(); - const newEditorState = EditorState.set(editorState, { - currentContent: contentWithEntity - }); - const nextState = RichUtils.toggleLink( - newEditorState, - newEditorState.getSelection(), - entityKey - ); - - onComplete(nextState); - } else { - onComplete(editorState); - } - }; - - return ( - -
handleSubmit(url)} - > - {({ data, change, submit }) => ( - <> - - - - - - - - - - - - )} -
-
- ); -}; - -export default LinkSource; diff --git a/src/components/RichTextEditor/RichTextEditor.stories.tsx b/src/components/RichTextEditor/RichTextEditor.stories.tsx new file mode 100644 index 000000000..e250f5496 --- /dev/null +++ b/src/components/RichTextEditor/RichTextEditor.stories.tsx @@ -0,0 +1,28 @@ +import { OutputData } from "@editorjs/editorjs"; +import RichTextEditor from "@saleor/components/RichTextEditor"; +import CardDecorator from "@saleor/storybook/CardDecorator"; +import Decorator from "@saleor/storybook/Decorator"; +import { storiesOf } from "@storybook/react"; +import React from "react"; + +import * as fixtures from "./fixtures.json"; +import { RichTextEditorProps } from "./RichTextEditor"; + +export const data: OutputData = fixtures.richTextEditor; + +const props: RichTextEditorProps = { + data, + disabled: false, + error: false, + helperText: "Lorem ipsum dolor sit amet, consectetur adipiscing elit", + label: "Content", + name: "content", + onChange: () => undefined +}; + +storiesOf("Generics / Rich text editor", module) + .addDecorator(CardDecorator) + .addDecorator(Decorator) + .add("default", () => ) + .add("disabled", () => ) + .add("error", () => ); diff --git a/src/components/RichTextEditor/RichTextEditor.tsx b/src/components/RichTextEditor/RichTextEditor.tsx index 1703bbcd6..62a616327 100644 --- a/src/components/RichTextEditor/RichTextEditor.tsx +++ b/src/components/RichTextEditor/RichTextEditor.tsx @@ -2,6 +2,10 @@ import EditorJS, { OutputData } from "@editorjs/editorjs"; import Header from "@editorjs/header"; import List from "@editorjs/list"; import Quote from "@editorjs/quote"; +import FormControl from "@material-ui/core/FormControl"; +import FormHelperText from "@material-ui/core/FormHelperText"; +import InputLabel from "@material-ui/core/InputLabel"; +import OutlinedInput from "@material-ui/core/OutlinedInput"; import { makeStyles } from "@material-ui/core/styles"; import { fade } from "@material-ui/core/styles/colorManipulator"; import Typography from "@material-ui/core/Typography"; @@ -32,21 +36,6 @@ const useStyles = makeStyles( }; return { - error: { - color: theme.palette.error.main - }, - helperText: { - marginTop: theme.spacing(0.75) - }, - label: { - color: theme.palette.text.secondary, - position: "absolute", - top: theme.spacing(1), - transition: theme.transitions.duration.short + "ms" - }, - labelActive: { - color: theme.palette.primary.main - }, root: { "& .cdx-quote__text": { minHeight: 24 @@ -99,13 +88,14 @@ const useStyles = makeStyles( "& a": { color: theme.palette.primary.light }, - "&:hover": { + "&:not($rootDisabled):hover": { borderColor: theme.palette.primary.main }, border: `1px solid ${fade(theme.palette.text.secondary, 0.4)}`, borderRadius: 4, boxShadow: `inset 0 0 0 0 ${theme.palette.primary.main}`, fontSize: theme.typography.body1.fontSize, + minHeight: 56, padding: theme.spacing(3, 2), paddingBottom: theme.spacing(), paddingLeft: 10, @@ -114,6 +104,12 @@ const useStyles = makeStyles( }, rootActive: { boxShadow: `inset 0px 0px 0 2px ${theme.palette.primary.main}` + }, + rootDisabled: { + ...theme.overrides.MuiOutlinedInput.root["&$disabled"]["& fieldset"] + }, + rootError: { + borderColor: theme.palette.error.main } }; }, @@ -122,9 +118,11 @@ const useStyles = makeStyles( const RichTextEditor: React.FC = ({ data, + disabled, error, helperText, label, + name, onChange, onReady }) => { @@ -144,6 +142,7 @@ const RichTextEditor: React.FC = ({ onChange(savedData); }, onReady, + readOnly: disabled, tools: { header: { class: Header, @@ -172,39 +171,36 @@ const RichTextEditor: React.FC = ({ [data === undefined] ); React.useEffect(() => editor.current?.destroy, []); + React.useEffect(() => { + if (editor.current?.readOnly) { + editor.current.readOnly.toggle(disabled); + } + }, [disabled]); return ( -
+ + + {label} +
setFocus(true)} onBlur={() => setFocus(false)} - > - - {label} - -
- {helperText && ( - - {helperText} - - )} -
+ /> + {helperText} + ); }; diff --git a/src/storybook/stories/components/fixtures.json b/src/components/RichTextEditor/fixtures.json similarity index 100% rename from src/storybook/stories/components/fixtures.json rename to src/components/RichTextEditor/fixtures.json diff --git a/src/pages/fixtures.ts b/src/pages/fixtures.ts index 6aba5a7ad..d912d5c09 100644 --- a/src/pages/fixtures.ts +++ b/src/pages/fixtures.ts @@ -1,7 +1,9 @@ -import { content } from "../storybook/stories/components/RichTextEditor"; +import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json"; import { PageDetails_page } from "./types/PageDetails"; import { PageList_pages_edges_node } from "./types/PageList"; +const content = richTextEditorFixtures.richTextEditor; + export const pageList: PageList_pages_edges_node[] = [ { __typename: "Page", diff --git a/src/products/fixtures.ts b/src/products/fixtures.ts index 928217fe1..734843360 100644 --- a/src/products/fixtures.ts +++ b/src/products/fixtures.ts @@ -5,11 +5,13 @@ import { } from "@saleor/types/globalTypes"; import { warehouseList } from "@saleor/warehouses/fixtures"; -import { content } from "../storybook/stories/components/RichTextEditor"; +import * as richTextEditorFixtures from "../components/RichTextEditor/fixtures.json"; import { ProductDetails_product } from "./types/ProductDetails"; import { ProductList_products_edges_node } from "./types/ProductList"; import { ProductVariantCreateData_product } from "./types/ProductVariantCreateData"; +const content = richTextEditorFixtures.richTextEditor; + export const product: ( placeholderImage: string ) => ProductDetails_product & diff --git a/src/storybook/config.js b/src/storybook/config.js index 28b2fc2ef..d088e83e8 100644 --- a/src/storybook/config.js +++ b/src/storybook/config.js @@ -35,7 +35,6 @@ function loadStories() { require("./stories/components/Percent"); require("./stories/components/PhoneField"); require("./stories/components/PriceField"); - require("./stories/components/RichTextEditor"); require("./stories/components/SaveButtonBar"); require("./stories/components/SaveFilterTabDialog"); require("./stories/components/SingleSelectField"); diff --git a/src/storybook/stories/components/RichTextEditor.tsx b/src/storybook/stories/components/RichTextEditor.tsx deleted file mode 100644 index 9cfd5580c..000000000 --- a/src/storybook/stories/components/RichTextEditor.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { OutputData } from "@editorjs/editorjs"; -import RichTextEditor from "@saleor/components/RichTextEditor"; -import { storiesOf } from "@storybook/react"; -import React from "react"; - -import CardDecorator from "../../CardDecorator"; -import Decorator from "../../Decorator"; -import * as fixtures from "./fixtures.json"; - -export const content: OutputData = fixtures.richTextEditor; - -storiesOf("Generics / Rich text editor", module) - .addDecorator(CardDecorator) - .addDecorator(Decorator) - .add("default", () => ( - undefined} - /> - ));