Merge pull request #395 from mirumee/fix/rich-text-error
Handle rich text editor content error
This commit is contained in:
commit
4c8cce4f23
3 changed files with 213 additions and 192 deletions
|
@ -36,6 +36,7 @@ All notable, unreleased changes to this project will be documented in this file.
|
|||
- Remove PO files from repo and update translations #409 by @dominik-zeglen
|
||||
- Add optional chaining and explicitely return "Not found" page - #408 by @dominik-zeglen
|
||||
- Do not store errors in form component - #410 by @dominik-zeglen
|
||||
- Handle rich text editor content error - #395 by @dominik-zeglen
|
||||
|
||||
## 2.0.0
|
||||
|
||||
|
|
|
@ -1267,6 +1267,10 @@
|
|||
"context": "button",
|
||||
"string": "Add or Edit Link"
|
||||
},
|
||||
"src_dot_components_dot_RichTextEditor_dot_286109898": {
|
||||
"context": "rich text error",
|
||||
"string": "Invalid content"
|
||||
},
|
||||
"src_dot_components_dot_RichTextEditor_dot_2925475978": {
|
||||
"string": "URL Linked"
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import { fade } from "@material-ui/core/styles/colorManipulator";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import classNames from "classnames";
|
||||
import { RawDraftContentState } from "draft-js";
|
||||
import {
|
||||
|
@ -13,6 +14,8 @@ import isEqual from "lodash-es/isEqual";
|
|||
import React from "react";
|
||||
|
||||
import { ChangeEvent } from "@saleor/hooks/useForm";
|
||||
import ErrorBoundary from "react-error-boundary";
|
||||
import { CreateCSSProperties } from "@material-ui/styles/withStyles";
|
||||
import BoldIcon from "../../icons/BoldIcon";
|
||||
import HeaderTwo from "../../icons/HeaderTwo";
|
||||
import HeaderThree from "../../icons/HeaderThree";
|
||||
|
@ -38,158 +41,165 @@ export interface RichTextEditorProps {
|
|||
}
|
||||
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
error: {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
helperText: {
|
||||
marginTop: theme.spacing(0.75)
|
||||
},
|
||||
input: {
|
||||
position: "relative"
|
||||
},
|
||||
label: {
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
left: 12,
|
||||
position: "absolute",
|
||||
top: 9
|
||||
},
|
||||
linkIcon: {
|
||||
marginTop: 2
|
||||
},
|
||||
root: {
|
||||
"& .DraftEditor": {
|
||||
"&-editorContainer": {
|
||||
"& .public-DraftEditor-content": {
|
||||
lineHeight: 1.62
|
||||
},
|
||||
"& a": {
|
||||
color: theme.palette.primary.light
|
||||
},
|
||||
"&:after": {
|
||||
background: theme.palette.getContrastText(
|
||||
theme.palette.background.default
|
||||
),
|
||||
bottom: -11,
|
||||
content: "''",
|
||||
display: "block",
|
||||
height: 2,
|
||||
left: -12,
|
||||
position: "absolute",
|
||||
transform: "scaleX(0) scaleY(0)",
|
||||
width: "calc(100% + 24px)"
|
||||
},
|
||||
position: "relative"
|
||||
},
|
||||
"&-root": {
|
||||
...theme.typography.body1
|
||||
}
|
||||
theme => {
|
||||
const editorContainer: CreateCSSProperties = {
|
||||
border: `1px ${theme.palette.divider} solid`,
|
||||
borderRadius: 4,
|
||||
padding: "27px 12px 10px",
|
||||
position: "relative",
|
||||
transition: theme.transitions.duration.shortest + "ms"
|
||||
};
|
||||
|
||||
return {
|
||||
editorContainer,
|
||||
error: {
|
||||
color: theme.palette.error.main
|
||||
},
|
||||
"& .Draftail": {
|
||||
"&-Editor": {
|
||||
"&--focus": {
|
||||
boxShadow: `inset 0px 0px 0px 2px ${theme.palette.primary.main}`
|
||||
helperText: {
|
||||
marginTop: theme.spacing(0.75)
|
||||
},
|
||||
input: {
|
||||
position: "relative"
|
||||
},
|
||||
label: {
|
||||
fontSize: theme.typography.caption.fontSize,
|
||||
left: 12,
|
||||
position: "absolute",
|
||||
top: 9
|
||||
},
|
||||
linkIcon: {
|
||||
marginTop: 2
|
||||
},
|
||||
root: {
|
||||
"& .DraftEditor": {
|
||||
"&-editorContainer": {
|
||||
"& .public-DraftEditor-content": {
|
||||
lineHeight: 1.62
|
||||
},
|
||||
"& a": {
|
||||
color: theme.palette.primary.light
|
||||
},
|
||||
"&:after": {
|
||||
background: theme.palette.getContrastText(
|
||||
theme.palette.background.default
|
||||
),
|
||||
bottom: -11,
|
||||
content: "''",
|
||||
display: "block",
|
||||
height: 2,
|
||||
left: -12,
|
||||
position: "absolute",
|
||||
transform: "scaleX(0) scaleY(0)",
|
||||
width: "calc(100% + 24px)"
|
||||
},
|
||||
position: "relative"
|
||||
},
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.primary.main
|
||||
},
|
||||
border: `1px ${theme.palette.divider} solid`,
|
||||
borderRadius: 4,
|
||||
padding: "27px 12px 10px",
|
||||
position: "relative",
|
||||
transition: theme.transitions.duration.shortest + "ms"
|
||||
},
|
||||
"&-Toolbar": {
|
||||
"&Button": {
|
||||
"& svg": {
|
||||
padding: 2
|
||||
},
|
||||
"&--active": {
|
||||
"&:hover": {
|
||||
background: theme.palette.primary.main
|
||||
},
|
||||
"&:not(:hover)": {
|
||||
borderRightColor: theme.palette.primary.main
|
||||
},
|
||||
background: theme.palette.primary.main
|
||||
},
|
||||
"&:focus": {
|
||||
"&:active": {
|
||||
"&:after": {
|
||||
background: fade(theme.palette.primary.main, 0.3),
|
||||
borderRadius: "100%",
|
||||
content: "''",
|
||||
display: "block",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"&:hover": {
|
||||
background: fade(theme.palette.primary.main, 0.3)
|
||||
},
|
||||
alignItems: "center",
|
||||
background: "none",
|
||||
border: "none",
|
||||
borderRight: `1px ${theme.palette.divider} solid`,
|
||||
color: theme.typography.body1.color,
|
||||
cursor: "pointer",
|
||||
display: "inline-flex",
|
||||
height: 36,
|
||||
justifyContent: "center",
|
||||
padding: theme.spacing(1) + 2,
|
||||
transition: theme.transitions.duration.short + "ms",
|
||||
width: 36
|
||||
},
|
||||
"&Group": {
|
||||
"&:last-of-type": {
|
||||
"& .Draftail-ToolbarButton": {
|
||||
"&:last-of-type": {
|
||||
border: "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
display: "flex"
|
||||
},
|
||||
background: theme.palette.background.default,
|
||||
border: `1px ${theme.palette.divider} solid`,
|
||||
display: "inline-flex",
|
||||
flexWrap: "wrap",
|
||||
marginBottom: theme.spacing(),
|
||||
marginTop: 10,
|
||||
[theme.breakpoints.down(460)]: {
|
||||
width: "min-content"
|
||||
"&-root": {
|
||||
...theme.typography.body1
|
||||
}
|
||||
},
|
||||
"&-block": {
|
||||
"&--blockquote": {
|
||||
borderLeft: `2px solid ${theme.palette.divider}`,
|
||||
margin: 0,
|
||||
padding: theme.spacing(1, 2)
|
||||
}
|
||||
}
|
||||
},
|
||||
"&$error": {
|
||||
"& .Draftail": {
|
||||
"&-Editor": {
|
||||
borderColor: theme.palette.error.main
|
||||
"&--focus": {
|
||||
boxShadow: `inset 0px 0px 0px 2px ${theme.palette.primary.main}`
|
||||
},
|
||||
"&:hover": {
|
||||
borderColor: theme.palette.primary.main
|
||||
},
|
||||
...editorContainer
|
||||
},
|
||||
"&-Toolbar": {
|
||||
"&Button": {
|
||||
"& svg": {
|
||||
padding: 2
|
||||
},
|
||||
"&--active": {
|
||||
"&:hover": {
|
||||
background: theme.palette.primary.main
|
||||
},
|
||||
"&:not(:hover)": {
|
||||
borderRightColor: theme.palette.primary.main
|
||||
},
|
||||
background: theme.palette.primary.main
|
||||
},
|
||||
"&:focus": {
|
||||
"&:active": {
|
||||
"&:after": {
|
||||
background: fade(theme.palette.primary.main, 0.3),
|
||||
borderRadius: "100%",
|
||||
content: "''",
|
||||
display: "block",
|
||||
height: "100%",
|
||||
width: "100%"
|
||||
}
|
||||
}
|
||||
},
|
||||
"&:hover": {
|
||||
background: fade(theme.palette.primary.main, 0.3)
|
||||
},
|
||||
alignItems: "center",
|
||||
background: "none",
|
||||
border: "none",
|
||||
borderRight: `1px ${theme.palette.divider} solid`,
|
||||
color: theme.typography.body1.color,
|
||||
cursor: "pointer",
|
||||
display: "inline-flex",
|
||||
height: 36,
|
||||
justifyContent: "center",
|
||||
padding: theme.spacing(1) + 2,
|
||||
transition: theme.transitions.duration.short + "ms",
|
||||
width: 36
|
||||
},
|
||||
"&Group": {
|
||||
"&:last-of-type": {
|
||||
"& .Draftail-ToolbarButton": {
|
||||
"&:last-of-type": {
|
||||
border: "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
display: "flex"
|
||||
},
|
||||
background: theme.palette.background.default,
|
||||
border: `1px ${theme.palette.divider} solid`,
|
||||
display: "inline-flex",
|
||||
flexWrap: "wrap",
|
||||
marginBottom: theme.spacing(),
|
||||
marginTop: 10,
|
||||
[theme.breakpoints.down(460)]: {
|
||||
width: "min-content"
|
||||
}
|
||||
},
|
||||
"&-block": {
|
||||
"&--blockquote": {
|
||||
borderLeft: `2px solid ${theme.palette.divider}`,
|
||||
margin: 0,
|
||||
padding: theme.spacing(1, 2)
|
||||
}
|
||||
}
|
||||
},
|
||||
"&$error": {
|
||||
"& .Draftail": {
|
||||
"&-Editor": {
|
||||
borderColor: theme.palette.error.main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scroll: {
|
||||
"& .DraftEditor": {
|
||||
"&-editorContainer": {
|
||||
"& .public-DraftEditor-content": {
|
||||
lineHeight: 1.62
|
||||
},
|
||||
scroll: {
|
||||
"& .DraftEditor": {
|
||||
"&-editorContainer": {
|
||||
"& .public-DraftEditor-content": {
|
||||
lineHeight: 1.62
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
smallIcon: {
|
||||
marginLeft: 10
|
||||
}
|
||||
},
|
||||
smallIcon: {
|
||||
marginLeft: 10
|
||||
}
|
||||
}),
|
||||
};
|
||||
},
|
||||
{ name: "RichTextEditor" }
|
||||
);
|
||||
|
||||
|
@ -226,58 +236,64 @@ const RichTextEditor: React.FC<RichTextEditorProps> = props => {
|
|||
<Typography className={classes.label} variant="caption" color="primary">
|
||||
{label}
|
||||
</Typography>
|
||||
<DraftailEditor
|
||||
key={JSON.stringify(initial)}
|
||||
rawContentState={
|
||||
initial && Object.keys(initial).length > 0 ? initial : null
|
||||
}
|
||||
onSave={value => handleSave(value, initial, name, onChange)}
|
||||
blockTypes={[
|
||||
{
|
||||
icon: <HeaderOne />,
|
||||
type: BLOCK_TYPE.HEADER_ONE
|
||||
},
|
||||
{ icon: <HeaderTwo />, type: BLOCK_TYPE.HEADER_TWO },
|
||||
{ icon: <HeaderThree />, type: BLOCK_TYPE.HEADER_THREE },
|
||||
{ icon: <QuotationIcon />, type: BLOCK_TYPE.BLOCKQUOTE },
|
||||
{
|
||||
icon: <UnorderedListIcon />,
|
||||
type: BLOCK_TYPE.UNORDERED_LIST_ITEM
|
||||
},
|
||||
{ icon: <OrderedListIcon />, type: BLOCK_TYPE.ORDERED_LIST_ITEM }
|
||||
]}
|
||||
inlineStyles={[
|
||||
{
|
||||
icon: <BoldIcon className={classes.smallIcon} />,
|
||||
type: INLINE_STYLE.BOLD
|
||||
},
|
||||
{
|
||||
icon: <ItalicIcon className={classes.smallIcon} />,
|
||||
type: INLINE_STYLE.ITALIC
|
||||
},
|
||||
{
|
||||
icon: <StrikethroughIcon />,
|
||||
type: INLINE_STYLE.STRIKETHROUGH
|
||||
<ErrorBoundary
|
||||
FallbackComponent={() => (
|
||||
<div className={classes.editorContainer}>
|
||||
<Typography color="error">
|
||||
<FormattedMessage
|
||||
defaultMessage="Invalid content"
|
||||
description="rich text error"
|
||||
/>
|
||||
</Typography>
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<DraftailEditor
|
||||
key={JSON.stringify(initial)}
|
||||
rawContentState={
|
||||
initial && Object.keys(initial).length > 0 ? initial : null
|
||||
}
|
||||
]}
|
||||
enableLineBreak
|
||||
entityTypes={[
|
||||
{
|
||||
attributes: ["url"],
|
||||
decorator: LinkEntity,
|
||||
icon: <LinkIcon className={classes.linkIcon} />,
|
||||
source: LinkSource,
|
||||
type: ENTITY_TYPE.LINK
|
||||
}
|
||||
// {
|
||||
// attributes: ["href"],
|
||||
// decorator: ImageEntity,
|
||||
// icon: <ImageIcon />,
|
||||
// source: ImageSource,
|
||||
// type: ENTITY_TYPE.IMAGE
|
||||
// }
|
||||
]}
|
||||
/>
|
||||
onSave={value => handleSave(value, initial, name, onChange)}
|
||||
blockTypes={[
|
||||
{
|
||||
icon: <HeaderOne />,
|
||||
type: BLOCK_TYPE.HEADER_ONE
|
||||
},
|
||||
{ icon: <HeaderTwo />, type: BLOCK_TYPE.HEADER_TWO },
|
||||
{ icon: <HeaderThree />, type: BLOCK_TYPE.HEADER_THREE },
|
||||
{ icon: <QuotationIcon />, type: BLOCK_TYPE.BLOCKQUOTE },
|
||||
{
|
||||
icon: <UnorderedListIcon />,
|
||||
type: BLOCK_TYPE.UNORDERED_LIST_ITEM
|
||||
},
|
||||
{ icon: <OrderedListIcon />, type: BLOCK_TYPE.ORDERED_LIST_ITEM }
|
||||
]}
|
||||
inlineStyles={[
|
||||
{
|
||||
icon: <BoldIcon className={classes.smallIcon} />,
|
||||
type: INLINE_STYLE.BOLD
|
||||
},
|
||||
{
|
||||
icon: <ItalicIcon className={classes.smallIcon} />,
|
||||
type: INLINE_STYLE.ITALIC
|
||||
},
|
||||
{
|
||||
icon: <StrikethroughIcon />,
|
||||
type: INLINE_STYLE.STRIKETHROUGH
|
||||
}
|
||||
]}
|
||||
enableLineBreak
|
||||
entityTypes={[
|
||||
{
|
||||
attributes: ["url"],
|
||||
decorator: LinkEntity,
|
||||
icon: <LinkIcon className={classes.linkIcon} />,
|
||||
source: LinkSource,
|
||||
type: ENTITY_TYPE.LINK
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
{helperText && (
|
||||
<Typography
|
||||
|
|
Loading…
Reference in a new issue