@madie/madie-design-system 1.2.65 → 1.2.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/RichTextEditor/RichTextEditor.stories.js +20 -0
- package/components/RichTextEditor/index.jsx +86 -4
- package/dist/browser.js +1 -1
- package/dist/index.js +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/components/_rich-text-editor.scss +6 -2
- package/styles/qppds/components/_rich-text-editor.scss +6 -2
- package/test/components/RichTextEditor.test.js +17 -0
|
@@ -63,4 +63,24 @@ export const DisabledTextEditor = () => {
|
|
|
63
63
|
/>
|
|
64
64
|
);
|
|
65
65
|
};
|
|
66
|
+
|
|
67
|
+
export const TextEditorWithError = () => {
|
|
68
|
+
const [value, setValue] = useState(
|
|
69
|
+
"<p>There is an error</p>"
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const handleChange = (selectedVal) => {
|
|
73
|
+
setValue(selectedVal);
|
|
74
|
+
};
|
|
75
|
+
return (
|
|
76
|
+
<RichTextEditor
|
|
77
|
+
label="Test Text Editor"
|
|
78
|
+
error={true}
|
|
79
|
+
helperText="This field is required"
|
|
80
|
+
required={true}
|
|
81
|
+
onChange={handleChange}
|
|
82
|
+
content={value}
|
|
83
|
+
/>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
66
86
|
TextEditor.storyName = "Rich Text Editor";
|
|
@@ -11,7 +11,7 @@ import TableHeader from "@tiptap/extension-table-header";
|
|
|
11
11
|
import TableRow from "@tiptap/extension-table-row";
|
|
12
12
|
import Underline from "@tiptap/extension-underline";
|
|
13
13
|
import StarterKit from "@tiptap/starter-kit";
|
|
14
|
-
import { IconButton } from "@mui/material";
|
|
14
|
+
import { FormHelperText, IconButton } from "@mui/material";
|
|
15
15
|
import FormatBoldIcon from "@mui/icons-material/FormatBold";
|
|
16
16
|
import FormatItalicIcon from "@mui/icons-material/FormatItalic";
|
|
17
17
|
import FormatUnderlinedIcon from "@mui/icons-material/FormatUnderlined";
|
|
@@ -28,7 +28,12 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
28
28
|
return null;
|
|
29
29
|
}
|
|
30
30
|
return (
|
|
31
|
-
<div
|
|
31
|
+
<div
|
|
32
|
+
className="control-group"
|
|
33
|
+
role="toolbar"
|
|
34
|
+
aria-label="Text formatting toolbar"
|
|
35
|
+
data-testid="rich-text-editor-toolbar"
|
|
36
|
+
>
|
|
32
37
|
<div className="button-group">
|
|
33
38
|
<Tooltip
|
|
34
39
|
data-testid="bold-tooltip"
|
|
@@ -42,8 +47,11 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
42
47
|
onClick={() =>
|
|
43
48
|
editor.chain().focus().toggleBold().run()
|
|
44
49
|
}
|
|
50
|
+
aria-label="Bold"
|
|
51
|
+
aria-pressed={editor.isActive("bold")}
|
|
45
52
|
className={editor.isActive("bold") ? "is-active" : ""}
|
|
46
53
|
disabled={disabled}
|
|
54
|
+
type="button"
|
|
47
55
|
>
|
|
48
56
|
<FormatBoldIcon />
|
|
49
57
|
</IconButton>
|
|
@@ -60,8 +68,11 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
60
68
|
onClick={() =>
|
|
61
69
|
editor.chain().focus().toggleItalic().run()
|
|
62
70
|
}
|
|
71
|
+
aria-label="Italic"
|
|
72
|
+
aria-pressed={editor.isActive("italic")}
|
|
63
73
|
className={editor.isActive("italic") ? "is-active" : ""}
|
|
64
74
|
disabled={disabled}
|
|
75
|
+
type="button"
|
|
65
76
|
>
|
|
66
77
|
<FormatItalicIcon />
|
|
67
78
|
</IconButton>
|
|
@@ -78,10 +89,13 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
78
89
|
onClick={() =>
|
|
79
90
|
editor.chain().focus().toggleUnderline().run()
|
|
80
91
|
}
|
|
92
|
+
aria-label="Underline"
|
|
93
|
+
aria-pressed={editor.isActive("underline")}
|
|
81
94
|
className={
|
|
82
95
|
editor.isActive("underline") ? "is-active" : ""
|
|
83
96
|
}
|
|
84
97
|
disabled={disabled}
|
|
98
|
+
type="button"
|
|
85
99
|
>
|
|
86
100
|
<FormatUnderlinedIcon />
|
|
87
101
|
</IconButton>
|
|
@@ -98,9 +112,12 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
98
112
|
onClick={() =>
|
|
99
113
|
editor.chain().focus().toggleStrike().run()
|
|
100
114
|
}
|
|
115
|
+
aria-label="Strikethrough"
|
|
116
|
+
aria-pressed={editor.isActive("strike")}
|
|
101
117
|
className={editor.isActive("strike") ? "is-active" : ""}
|
|
102
118
|
style={{ borderRight: "solid 1px #9c9c9c" }}
|
|
103
119
|
disabled={disabled}
|
|
120
|
+
type="button"
|
|
104
121
|
>
|
|
105
122
|
<StrikethroughSIcon />
|
|
106
123
|
</IconButton>
|
|
@@ -117,10 +134,13 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
117
134
|
onClick={() =>
|
|
118
135
|
editor.chain().focus().toggleOrderedList().run()
|
|
119
136
|
}
|
|
137
|
+
aria-label="Ordered List"
|
|
138
|
+
aria-pressed={editor.isActive("orderedList")}
|
|
120
139
|
className={
|
|
121
140
|
editor.isActive("orderedList") ? "is-active" : ""
|
|
122
141
|
}
|
|
123
142
|
disabled={disabled}
|
|
143
|
+
type="button"
|
|
124
144
|
>
|
|
125
145
|
<FormatListNumberedIcon />
|
|
126
146
|
</IconButton>
|
|
@@ -137,11 +157,14 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
137
157
|
onClick={() =>
|
|
138
158
|
editor.chain().focus().toggleBulletList().run()
|
|
139
159
|
}
|
|
160
|
+
aria-label="Bulleted List"
|
|
161
|
+
aria-pressed={editor.isActive("bulletList")}
|
|
140
162
|
className={
|
|
141
163
|
editor.isActive("bulletList") ? "is-active" : ""
|
|
142
164
|
}
|
|
143
165
|
style={{ borderRight: "solid 1px #9c9c9c" }}
|
|
144
166
|
disabled={disabled}
|
|
167
|
+
type="button"
|
|
145
168
|
>
|
|
146
169
|
<FormatListBulletedIcon />
|
|
147
170
|
</IconButton>
|
|
@@ -162,10 +185,12 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
162
185
|
withHeaderRow: true,
|
|
163
186
|
})
|
|
164
187
|
}
|
|
188
|
+
aria-label="Insert Table"
|
|
165
189
|
className={
|
|
166
190
|
editor.isActive("insertTable") ? "is-active" : ""
|
|
167
191
|
}
|
|
168
192
|
disabled={disabled}
|
|
193
|
+
type="button"
|
|
169
194
|
>
|
|
170
195
|
<TableChartIcon />
|
|
171
196
|
</IconButton>
|
|
@@ -176,11 +201,14 @@ const MenuBar = ({ editor, disabled }) => {
|
|
|
176
201
|
};
|
|
177
202
|
|
|
178
203
|
const RichTextEditor = ({
|
|
204
|
+
name,
|
|
179
205
|
id,
|
|
180
206
|
error = false,
|
|
207
|
+
helperText,
|
|
181
208
|
required = false,
|
|
182
209
|
label,
|
|
183
210
|
onChange,
|
|
211
|
+
onBlur,
|
|
184
212
|
content,
|
|
185
213
|
disabled = false,
|
|
186
214
|
readOnly = false,
|
|
@@ -211,12 +239,28 @@ const RichTextEditor = ({
|
|
|
211
239
|
},
|
|
212
240
|
[content, disabled]
|
|
213
241
|
);
|
|
242
|
+
|
|
243
|
+
React.useEffect(() => {
|
|
244
|
+
if (!editor || !onBlur || !name) return;
|
|
245
|
+
|
|
246
|
+
const handleBlur = () => {
|
|
247
|
+
onBlur({ target: { name } });
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
editor.on("blur", handleBlur);
|
|
251
|
+
|
|
252
|
+
return () => {
|
|
253
|
+
editor.off("blur", handleBlur);
|
|
254
|
+
};
|
|
255
|
+
}, [editor, onBlur, name]);
|
|
256
|
+
|
|
214
257
|
return (
|
|
215
258
|
<div
|
|
216
259
|
className="rich-text-editor"
|
|
217
260
|
data-testid={`${kebabCase(label)}-rich-text-editor`}
|
|
218
261
|
>
|
|
219
262
|
<InputLabel
|
|
263
|
+
id={`${id}-label`}
|
|
220
264
|
shrink
|
|
221
265
|
required={required}
|
|
222
266
|
error={error}
|
|
@@ -256,11 +300,33 @@ const RichTextEditor = ({
|
|
|
256
300
|
>
|
|
257
301
|
{label}
|
|
258
302
|
</InputLabel>
|
|
303
|
+
|
|
304
|
+
{helperText && (
|
|
305
|
+
<FormHelperText
|
|
306
|
+
tabIndex={0}
|
|
307
|
+
aria-live="polite"
|
|
308
|
+
id={`${id}-helper-text`}
|
|
309
|
+
data-testid={`${id}-helper-text`}
|
|
310
|
+
sx={[
|
|
311
|
+
{
|
|
312
|
+
margin: "4px 0px 0px 0px",
|
|
313
|
+
color: "#515151",
|
|
314
|
+
lineHeight: 1,
|
|
315
|
+
},
|
|
316
|
+
error && {
|
|
317
|
+
color: "#AE1C1C !important",
|
|
318
|
+
},
|
|
319
|
+
]}
|
|
320
|
+
>
|
|
321
|
+
{helperText}
|
|
322
|
+
</FormHelperText>
|
|
323
|
+
)}
|
|
324
|
+
|
|
259
325
|
{readOnly ? (
|
|
260
326
|
<p
|
|
261
327
|
className="rich-text-editor_read_only"
|
|
262
328
|
data-testid={`${id}-value`}
|
|
263
|
-
aria-labelledby={label}
|
|
329
|
+
aria-labelledby={`${id}-label`}
|
|
264
330
|
dangerouslySetInnerHTML={{
|
|
265
331
|
__html: content ? DOMPurify.sanitize(content) : "-",
|
|
266
332
|
}}
|
|
@@ -268,7 +334,20 @@ const RichTextEditor = ({
|
|
|
268
334
|
) : (
|
|
269
335
|
<>
|
|
270
336
|
<MenuBar editor={editor} disabled={disabled} />
|
|
271
|
-
<EditorContent
|
|
337
|
+
<EditorContent
|
|
338
|
+
id={id}
|
|
339
|
+
tabIndex={0}
|
|
340
|
+
data-testid="rich-text-editor-content"
|
|
341
|
+
editor={editor}
|
|
342
|
+
aria-labelledby={`${id}-label`}
|
|
343
|
+
aria-describedby={
|
|
344
|
+
helperText ? `${id}-helper-text` : undefined
|
|
345
|
+
}
|
|
346
|
+
aria-multiline="true"
|
|
347
|
+
aria-required={required || undefined}
|
|
348
|
+
aria-invalid={error || undefined}
|
|
349
|
+
className={`ProseMirror ${error ? "has-error" : ""}`}
|
|
350
|
+
/>
|
|
272
351
|
</>
|
|
273
352
|
)}
|
|
274
353
|
</div>
|
|
@@ -277,10 +356,13 @@ const RichTextEditor = ({
|
|
|
277
356
|
|
|
278
357
|
RichTextEditor.propTypes = {
|
|
279
358
|
id: PropTypes.string,
|
|
359
|
+
name: PropTypes.string,
|
|
280
360
|
error: PropTypes.bool,
|
|
361
|
+
helperText: PropTypes.string,
|
|
281
362
|
required: PropTypes.bool,
|
|
282
363
|
label: PropTypes.string,
|
|
283
364
|
onChange: PropTypes.func,
|
|
365
|
+
onBlur: PropTypes.func,
|
|
284
366
|
content: PropTypes.any,
|
|
285
367
|
disabled: PropTypes.bool,
|
|
286
368
|
readOnly: PropTypes.bool,
|