@madie/madie-design-system 1.2.65 → 1.2.68

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.
@@ -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";
@@ -22,13 +22,19 @@ import TableChartIcon from "@mui/icons-material/TableChart";
22
22
  import { Tooltip } from "@mui/material";
23
23
  import { kebabCase } from "lodash";
24
24
  import DOMPurify from "dompurify";
25
+ import { Strike } from "@tiptap/extension-strike";
25
26
 
26
27
  const MenuBar = ({ editor, disabled }) => {
27
28
  if (!editor) {
28
29
  return null;
29
30
  }
30
31
  return (
31
- <div className="control-group" data-testid="rich-text-editor-toolbar">
32
+ <div
33
+ className="control-group"
34
+ role="toolbar"
35
+ aria-label="Text formatting toolbar"
36
+ data-testid="rich-text-editor-toolbar"
37
+ >
32
38
  <div className="button-group">
33
39
  <Tooltip
34
40
  data-testid="bold-tooltip"
@@ -42,8 +48,11 @@ const MenuBar = ({ editor, disabled }) => {
42
48
  onClick={() =>
43
49
  editor.chain().focus().toggleBold().run()
44
50
  }
51
+ aria-label="Bold"
52
+ aria-pressed={editor.isActive("bold")}
45
53
  className={editor.isActive("bold") ? "is-active" : ""}
46
54
  disabled={disabled}
55
+ type="button"
47
56
  >
48
57
  <FormatBoldIcon />
49
58
  </IconButton>
@@ -60,8 +69,11 @@ const MenuBar = ({ editor, disabled }) => {
60
69
  onClick={() =>
61
70
  editor.chain().focus().toggleItalic().run()
62
71
  }
72
+ aria-label="Italic"
73
+ aria-pressed={editor.isActive("italic")}
63
74
  className={editor.isActive("italic") ? "is-active" : ""}
64
75
  disabled={disabled}
76
+ type="button"
65
77
  >
66
78
  <FormatItalicIcon />
67
79
  </IconButton>
@@ -78,10 +90,13 @@ const MenuBar = ({ editor, disabled }) => {
78
90
  onClick={() =>
79
91
  editor.chain().focus().toggleUnderline().run()
80
92
  }
93
+ aria-label="Underline"
94
+ aria-pressed={editor.isActive("underline")}
81
95
  className={
82
96
  editor.isActive("underline") ? "is-active" : ""
83
97
  }
84
98
  disabled={disabled}
99
+ type="button"
85
100
  >
86
101
  <FormatUnderlinedIcon />
87
102
  </IconButton>
@@ -98,9 +113,12 @@ const MenuBar = ({ editor, disabled }) => {
98
113
  onClick={() =>
99
114
  editor.chain().focus().toggleStrike().run()
100
115
  }
116
+ aria-label="Strikethrough"
117
+ aria-pressed={editor.isActive("strike")}
101
118
  className={editor.isActive("strike") ? "is-active" : ""}
102
119
  style={{ borderRight: "solid 1px #9c9c9c" }}
103
120
  disabled={disabled}
121
+ type="button"
104
122
  >
105
123
  <StrikethroughSIcon />
106
124
  </IconButton>
@@ -117,10 +135,13 @@ const MenuBar = ({ editor, disabled }) => {
117
135
  onClick={() =>
118
136
  editor.chain().focus().toggleOrderedList().run()
119
137
  }
138
+ aria-label="Ordered List"
139
+ aria-pressed={editor.isActive("orderedList")}
120
140
  className={
121
141
  editor.isActive("orderedList") ? "is-active" : ""
122
142
  }
123
143
  disabled={disabled}
144
+ type="button"
124
145
  >
125
146
  <FormatListNumberedIcon />
126
147
  </IconButton>
@@ -137,11 +158,14 @@ const MenuBar = ({ editor, disabled }) => {
137
158
  onClick={() =>
138
159
  editor.chain().focus().toggleBulletList().run()
139
160
  }
161
+ aria-label="Bulleted List"
162
+ aria-pressed={editor.isActive("bulletList")}
140
163
  className={
141
164
  editor.isActive("bulletList") ? "is-active" : ""
142
165
  }
143
166
  style={{ borderRight: "solid 1px #9c9c9c" }}
144
167
  disabled={disabled}
168
+ type="button"
145
169
  >
146
170
  <FormatListBulletedIcon />
147
171
  </IconButton>
@@ -162,10 +186,12 @@ const MenuBar = ({ editor, disabled }) => {
162
186
  withHeaderRow: true,
163
187
  })
164
188
  }
189
+ aria-label="Insert Table"
165
190
  className={
166
191
  editor.isActive("insertTable") ? "is-active" : ""
167
192
  }
168
193
  disabled={disabled}
194
+ type="button"
169
195
  >
170
196
  <TableChartIcon />
171
197
  </IconButton>
@@ -176,11 +202,14 @@ const MenuBar = ({ editor, disabled }) => {
176
202
  };
177
203
 
178
204
  const RichTextEditor = ({
205
+ name,
179
206
  id,
180
207
  error = false,
208
+ helperText,
181
209
  required = false,
182
210
  label,
183
211
  onChange,
212
+ onBlur,
184
213
  content,
185
214
  disabled = false,
186
215
  readOnly = false,
@@ -200,6 +229,12 @@ const RichTextEditor = ({
200
229
  TableHeader,
201
230
  TableCell,
202
231
  Underline,
232
+ Strike.extend({
233
+ strike: false, // disable default strike through
234
+ renderHTML({ HTMLAttributes }) {
235
+ return ["del", HTMLAttributes, 0];
236
+ },
237
+ }),
203
238
  ],
204
239
  shouldRerenderOnTransaction: false,
205
240
  content,
@@ -211,12 +246,28 @@ const RichTextEditor = ({
211
246
  },
212
247
  [content, disabled]
213
248
  );
249
+
250
+ React.useEffect(() => {
251
+ if (!editor || !onBlur || !name) return;
252
+
253
+ const handleBlur = () => {
254
+ onBlur({ target: { name } });
255
+ };
256
+
257
+ editor.on("blur", handleBlur);
258
+
259
+ return () => {
260
+ editor.off("blur", handleBlur);
261
+ };
262
+ }, [editor, onBlur, name]);
263
+
214
264
  return (
215
265
  <div
216
266
  className="rich-text-editor"
217
267
  data-testid={`${kebabCase(label)}-rich-text-editor`}
218
268
  >
219
269
  <InputLabel
270
+ id={`${id}-label`}
220
271
  shrink
221
272
  required={required}
222
273
  error={error}
@@ -256,11 +307,33 @@ const RichTextEditor = ({
256
307
  >
257
308
  {label}
258
309
  </InputLabel>
310
+
311
+ {helperText && (
312
+ <FormHelperText
313
+ tabIndex={0}
314
+ aria-live="polite"
315
+ id={`${id}-helper-text`}
316
+ data-testid={`${id}-helper-text`}
317
+ sx={[
318
+ {
319
+ margin: "4px 0px 0px 0px",
320
+ color: "#515151",
321
+ lineHeight: 1,
322
+ },
323
+ error && {
324
+ color: "#AE1C1C !important",
325
+ },
326
+ ]}
327
+ >
328
+ {helperText}
329
+ </FormHelperText>
330
+ )}
331
+
259
332
  {readOnly ? (
260
333
  <p
261
334
  className="rich-text-editor_read_only"
262
335
  data-testid={`${id}-value`}
263
- aria-labelledby={label}
336
+ aria-labelledby={`${id}-label`}
264
337
  dangerouslySetInnerHTML={{
265
338
  __html: content ? DOMPurify.sanitize(content) : "-",
266
339
  }}
@@ -268,7 +341,20 @@ const RichTextEditor = ({
268
341
  ) : (
269
342
  <>
270
343
  <MenuBar editor={editor} disabled={disabled} />
271
- <EditorContent editor={editor} />
344
+ <EditorContent
345
+ id={id}
346
+ tabIndex={0}
347
+ data-testid="rich-text-editor-content"
348
+ editor={editor}
349
+ aria-labelledby={`${id}-label`}
350
+ aria-describedby={
351
+ helperText ? `${id}-helper-text` : undefined
352
+ }
353
+ aria-multiline="true"
354
+ aria-required={required || undefined}
355
+ aria-invalid={error || undefined}
356
+ className={`ProseMirror ${error ? "has-error" : ""}`}
357
+ />
272
358
  </>
273
359
  )}
274
360
  </div>
@@ -277,10 +363,13 @@ const RichTextEditor = ({
277
363
 
278
364
  RichTextEditor.propTypes = {
279
365
  id: PropTypes.string,
366
+ name: PropTypes.string,
280
367
  error: PropTypes.bool,
368
+ helperText: PropTypes.string,
281
369
  required: PropTypes.bool,
282
370
  label: PropTypes.string,
283
371
  onChange: PropTypes.func,
372
+ onBlur: PropTypes.func,
284
373
  content: PropTypes.any,
285
374
  disabled: PropTypes.bool,
286
375
  readOnly: PropTypes.bool,