@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.
@@ -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 className="control-group" data-testid="rich-text-editor-toolbar">
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 editor={editor} />
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,