@madie/madie-design-system 1.2.63 → 1.2.65

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.
Files changed (34) hide show
  1. package/components/AutoComplete/AutoComplete.jsx +4 -2
  2. package/components/AutoComplete/AutoComplete.stories.js +12 -0
  3. package/components/DateField/DateField.jsx +5 -3
  4. package/components/DateField/DateField.stories.jsx +16 -0
  5. package/components/DateTimeField/DateTimeField.jsx +10 -4
  6. package/components/DateTimeField/DateTimeField.stories.jsx +28 -0
  7. package/components/NumberInput/index.jsx +3 -0
  8. package/components/RadioButton/RadioButton.jsx +7 -3
  9. package/components/RadioButton/RadioButton.stories.jsx +21 -2
  10. package/components/ReadOnlyTextField/index.jsx +9 -3
  11. package/components/RichTextEditor/RichTextEditor.stories.js +21 -3
  12. package/components/RichTextEditor/index.jsx +29 -18
  13. package/components/Select/Select.stories.js +53 -0
  14. package/components/Select/index.jsx +4 -2
  15. package/components/TextArea/TextArea.stories.js +16 -0
  16. package/components/TextArea/index.jsx +5 -2
  17. package/components/TextField/TextField.stories.js +14 -0
  18. package/components/TextField/index.jsx +4 -2
  19. package/dist/browser.js +1 -1
  20. package/dist/index.js +1 -1
  21. package/dist/react/index.js +5 -5
  22. package/dist/react/index.js.map +1 -1
  23. package/package.json +1 -1
  24. package/styles/components/_rich-text-editor.scss +5 -0
  25. package/styles/qppds/components/_rich-text-editor.scss +4 -0
  26. package/test/components/AutoComplete.test.js +25 -4
  27. package/test/components/DateField.test.js +26 -8
  28. package/test/components/DateTimeField.test.js +28 -7
  29. package/test/components/RadioButton.test.js +7 -5
  30. package/test/components/ReadOnlyTextField.test.js +4 -2
  31. package/test/components/RichTextEditor.test.js +59 -5
  32. package/test/components/Select.test.js +24 -4
  33. package/test/components/TextArea.test.js +27 -11
  34. package/test/components/TextField.test.js +21 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madie/madie-design-system",
3
- "version": "1.2.63",
3
+ "version": "1.2.65",
4
4
  "description": "Shared style guide across the Madie program.",
5
5
  "main": "dist/index.js",
6
6
  "homepage": "https://github.com/MeasureAuthoringTool/madie-design-system",
@@ -188,6 +188,11 @@
188
188
  }
189
189
  }
190
190
  }
191
+
192
+ .rich-text-editor_read_only {
193
+ white-space: pre-wrap;
194
+ }
195
+
191
196
  /* Table-specific styling */
192
197
  table {
193
198
  border-collapse: collapse;
@@ -189,6 +189,10 @@
189
189
  }
190
190
  }
191
191
 
192
+ .rich-text-editor_read_only {
193
+ white-space: pre-wrap;
194
+ }
195
+
192
196
  /* Table-specific styling */
193
197
  table {
194
198
  border-collapse: collapse;
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import "@testing-library/jest-dom";
3
3
  import { describe, expect } from "@jest/globals";
4
4
  import AutoComplete from "../../components/AutoComplete/AutoComplete";
5
- import {render, screen } from "@testing-library/react";
5
+ import { render, screen } from "@testing-library/react";
6
6
  import userEvent from "@testing-library/user-event";
7
7
 
8
8
  describe("AutoComplete Component", () => {
@@ -29,7 +29,12 @@ describe("AutoComplete Component", () => {
29
29
  expect(option).toBeInTheDocument();
30
30
 
31
31
  await userEvent.click(option);
32
- expect(mockOnChange).toHaveBeenCalledWith("autocomplete", "Option1", expect.any(String), expect.any(Object));
32
+ expect(mockOnChange).toHaveBeenCalledWith(
33
+ "autocomplete",
34
+ "Option1",
35
+ expect.any(String),
36
+ expect.any(Object)
37
+ );
33
38
  });
34
39
 
35
40
  it("renders AutoComplete in disabled mode", async () => {
@@ -43,9 +48,25 @@ describe("AutoComplete Component", () => {
43
48
  />
44
49
  );
45
50
 
51
+ const disabledInput = screen.getByRole("combobox");
52
+ expect(disabledInput).toHaveValue("Option1");
53
+ expect(disabledInput).toBeDisabled();
54
+ });
55
+
56
+ it("renders AutoComplete in read only mode", async () => {
57
+ render(
58
+ <AutoComplete
59
+ id="autocomplete-disabled"
60
+ dataTestId="autocomplete-disabled"
61
+ label="Auto Complete"
62
+ readOnly
63
+ value="Option1"
64
+ />
65
+ );
66
+
46
67
  const readOnlyField = screen.getByRole("textbox");
47
- expect(readOnlyField).toHaveTextContent("Option1");
48
- expect(readOnlyField).toHaveAttribute("readOnly");
68
+ expect(readOnlyField).toHaveValue("Option1");
69
+ expect(readOnlyField).toHaveProperty("readOnly", true);
49
70
  });
50
71
 
51
72
  it("renders AutoComplete with helper text", () => {
@@ -44,7 +44,7 @@ describe("DateField", () => {
44
44
  });
45
45
  });
46
46
 
47
- it("renders ReadOnlyTextField when disabled is true", () => {
47
+ it("renders disabled DateField when disabled is true", () => {
48
48
  const mockProps = {
49
49
  id: "test_id",
50
50
  label: "Test Label",
@@ -54,20 +54,38 @@ describe("DateField", () => {
54
54
 
55
55
  render(<DateField {...mockProps} />);
56
56
 
57
- const readOnlyField = screen.getByText("Test Label");
58
- expect(readOnlyField).toBeInTheDocument();
59
- const dateField = screen.getByRole("textbox");
60
- expect(dateField).toHaveTextContent("10/01/2023");
61
- expect(dateField).toHaveAttribute("readOnly");
57
+ const label = screen.getByText("Test Label");
58
+ expect(label).toBeInTheDocument();
59
+
60
+ const disabledField = screen.getByRole("textbox");
61
+ expect(disabledField).toHaveValue("10/01/2023");
62
+ expect(disabledField).toBeDisabled();
62
63
  });
63
64
 
64
- it("renders placeholder '-' when value is null and disabled is true", () => {
65
+ it("renders ReadOnlyTextField when readOnly is true", () => {
66
+ const mockProps = {
67
+ id: "test_id",
68
+ label: "Test Label",
69
+ value: dayjs.utc("2023-10-01"),
70
+ readOnly: true,
71
+ };
72
+
73
+ render(<DateField {...mockProps} />);
74
+
75
+ const label = screen.getByText("Test Label");
76
+ expect(label).toBeInTheDocument();
77
+ const readOnlyField = screen.getByRole("textbox");
78
+ expect(readOnlyField).toHaveTextContent("10/01/2023");
79
+ expect(readOnlyField).toHaveProperty("readOnly", true);
80
+ });
81
+
82
+ it("renders placeholder '-' when value is null and readOnly is true", () => {
65
83
  const mockProps = {
66
84
  id: "test_id",
67
85
  label: "Test Label",
68
86
  value: null,
69
87
  required: false,
70
- disabled: true,
88
+ readOnly: true,
71
89
  };
72
90
 
73
91
  const { getByLabelText } = render(<DateField {...mockProps} />);
@@ -32,7 +32,7 @@ describe("DateTimeField", () => {
32
32
  });
33
33
  });
34
34
 
35
- it("renders ReadOnlyTextField when disabled is true", () => {
35
+ it("renders disabled DateTimeField when disabled is true", () => {
36
36
  const label = "Test Label";
37
37
  const dateTimeValue = new Date("2023-01-01T12:00:00Z");
38
38
  const id = "test-id";
@@ -47,13 +47,13 @@ describe("DateTimeField", () => {
47
47
  />
48
48
  );
49
49
 
50
- const readOnlyField = getByLabelText(label);
51
- expect(readOnlyField).toBeInTheDocument();
52
- expect(readOnlyField).toHaveTextContent("01/01/2023 12:00 PM");
53
- expect(readOnlyField).toHaveAttribute("readOnly");
50
+ const disabledField = getByLabelText(label);
51
+ expect(disabledField).toBeInTheDocument();
52
+ expect(disabledField).toHaveValue("01/01/2023 12:00 PM");
53
+ expect(disabledField).toBeDisabled();
54
54
  });
55
55
 
56
- it("renders empty value in ReadOnlyTextField when dateTimeValue is null", () => {
56
+ it("renders empty value in disabled DateTimeField when dateTimeValue is null", () => {
57
57
  const label = "Test Label";
58
58
  const id = "test-id";
59
59
 
@@ -67,8 +67,29 @@ describe("DateTimeField", () => {
67
67
  />
68
68
  );
69
69
 
70
+ const disabledField = getByLabelText(label);
71
+ expect(disabledField).toBeInTheDocument();
72
+ expect(disabledField).toHaveValue("");
73
+ });
74
+
75
+ it("renders ReadOnlyTextField when readOnly is true", () => {
76
+ const label = "Test Label";
77
+ const dateTimeValue = new Date("2023-01-01T12:00:00Z");
78
+ const id = "test-id";
79
+
80
+ const { getByLabelText } = render(
81
+ <DateTimeField
82
+ label={label}
83
+ dateTimeValue={dateTimeValue}
84
+ handleDateTimeChange={() => {}}
85
+ readOnly={true}
86
+ id={id}
87
+ />
88
+ );
89
+
70
90
  const readOnlyField = getByLabelText(label);
71
91
  expect(readOnlyField).toBeInTheDocument();
72
- expect(readOnlyField).toHaveTextContent("-");
92
+ expect(readOnlyField).toHaveValue("01/01/2023 12:00 PM");
93
+ expect(readOnlyField).toHaveProperty("readOnly", true);
73
94
  });
74
95
  });
@@ -50,9 +50,10 @@ describe("RadioButton Component", () => {
50
50
  />
51
51
  );
52
52
 
53
- const option1 = screen.getByRole("textbox");
54
- expect(option1).toHaveTextContent("Option 1");
55
- expect(option1).toHaveAttribute("readOnly");
53
+ const radios = screen.getAllByRole("radio");
54
+ radios.forEach((radio) => {
55
+ expect(radio).toBeDisabled();
56
+ });
56
57
  });
57
58
 
58
59
  it("renders helper text", () => {
@@ -85,7 +86,7 @@ describe("RadioButton Component", () => {
85
86
  expect(errorText).toHaveStyle("color: #d32f2f");
86
87
  });
87
88
 
88
- it("renders placeholder '-' when disabled and no value is provided", () => {
89
+ it("renders placeholder '-' when read only and no value is provided", () => {
89
90
  const options = [
90
91
  { value: "option1", label: "Option 1" },
91
92
  { value: "option2", label: "Option 2" },
@@ -97,11 +98,12 @@ describe("RadioButton Component", () => {
97
98
  dataTestId="radio-button-placeholder"
98
99
  label="Radio Button"
99
100
  options={options}
100
- disabled
101
+ readOnly
101
102
  />
102
103
  );
103
104
 
104
105
  const readOnlyField = screen.getByRole("textbox");
105
106
  expect(readOnlyField).toHaveTextContent("-");
107
+ expect(readOnlyField).toHaveProperty("readOnly", true);
106
108
  });
107
109
  });
@@ -16,9 +16,11 @@ describe("ReadOnlyTextField", () => {
16
16
  size="small"
17
17
  />
18
18
  );
19
- const textNode = getByRole(document.body, "textbox", { name: "test Name" });
19
+ const textNode = getByRole(document.body, "textbox", {
20
+ name: "test Name",
21
+ });
20
22
  expect(textNode).toBeInTheDocument();
21
- expect(textNode).toHaveAttribute("readOnly");
23
+ expect(textNode).toHaveProperty("readOnly", true);
22
24
  expect(textNode).toHaveTextContent("-");
23
25
  });
24
26
  });
@@ -1,6 +1,7 @@
1
1
  import React from "react";
2
- import {render, screen, fireEvent, waitFor} from "@testing-library/react";
2
+ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
3
3
  import RichTextEditor from "../../components/RichTextEditor/index";
4
+ import DOMPurify from "dompurify";
4
5
 
5
6
  describe("RichTextEditor Component", () => {
6
7
  const mockOnChange = jest.fn();
@@ -20,7 +21,9 @@ describe("RichTextEditor Component", () => {
20
21
  expect(screen.getByText("Test Label")).toBeInTheDocument();
21
22
 
22
23
  // Check if the editor content is rendered
23
- expect(screen.getByTestId("test-label-rich-text-editor")).toBeInTheDocument();
24
+ expect(
25
+ screen.getByTestId("test-label-rich-text-editor")
26
+ ).toBeInTheDocument();
24
27
  });
25
28
 
26
29
  it("calls onChange when content is updated", async () => {
@@ -36,7 +39,9 @@ describe("RichTextEditor Component", () => {
36
39
 
37
40
  // Simulate content change
38
41
  const editorContent = screen.getByRole("textbox");
39
- fireEvent.input(editorContent, {target: {innerHTML: "Updated Content"}});
42
+ fireEvent.input(editorContent, {
43
+ target: { innerHTML: "Updated Content" },
44
+ });
40
45
 
41
46
  // Check if onChange is called
42
47
  await waitFor(() => {
@@ -44,19 +49,68 @@ describe("RichTextEditor Component", () => {
44
49
  });
45
50
  });
46
51
 
47
- it("renders sanitized content when canEdit is false", () => {
52
+ it("renders sanitized content within editor when disabled is true", () => {
48
53
  render(
49
54
  <RichTextEditor
50
55
  id="test-editor"
51
56
  label="Test Label"
52
57
  content="<script>alert('XSS')</script><p>Safe Content</p>"
53
58
  onChange={mockOnChange}
54
- disabled={true}
59
+ disabled
55
60
  />
56
61
  );
57
62
 
58
63
  // Check if sanitized content is rendered
59
64
  expect(screen.getByText("Safe Content")).toBeInTheDocument();
60
65
  expect(screen.queryByText("alert('XSS')")).not.toBeInTheDocument();
66
+
67
+ const editorContent = screen.getByRole("textbox");
68
+ expect(editorContent).toBeInTheDocument();
69
+ expect(editorContent).toHaveAttribute("contenteditable", "false");
70
+ expect(editorContent).toHaveTextContent("Safe Content");
71
+ });
72
+
73
+ it("renders read only, sanitized content when readOnly is true", () => {
74
+ render(
75
+ <RichTextEditor
76
+ id="test-editor"
77
+ label="Test Label"
78
+ content="<script>alert('XSS')</script><p>Safe Content</p>"
79
+ onChange={mockOnChange}
80
+ readOnly
81
+ />
82
+ );
83
+
84
+ // Check if sanitized content is rendered
85
+ expect(screen.getByText("Safe Content")).toBeInTheDocument();
86
+ expect(screen.queryByText("alert('XSS')")).not.toBeInTheDocument();
87
+
88
+ const editorContent = screen.getByTestId("test-editor-value");
89
+ expect(editorContent).toBeInTheDocument();
90
+ expect(editorContent).toHaveTextContent("Safe Content");
91
+ });
92
+
93
+ it("test renders sanitized content with preserved whitespace when readOnly is true", () => {
94
+ const content = `this is a statement
95
+ this is a statement
96
+ this is a statement
97
+
98
+ ---------------------
99
+ this is a statement`;
100
+
101
+ render(
102
+ <RichTextEditor
103
+ id="test-editor"
104
+ label="Test Label"
105
+ content={content}
106
+ onChange={mockOnChange}
107
+ readOnly
108
+ />
109
+ );
110
+
111
+ const readOnlyContent = screen.getByTestId("test-editor-value");
112
+
113
+ // Ensure the innerHTML matches the sanitized version of the content
114
+ expect(readOnlyContent.innerHTML).toBe(DOMPurify.sanitize(content));
61
115
  });
62
116
  });
@@ -117,17 +117,37 @@ describe("Select", () => {
117
117
  disabled
118
118
  helperText="helper text"
119
119
  label="Test"
120
- value=""
120
+ value="select value"
121
121
  id="test-select"
122
122
  inputProps={{ "data-testid": "test-input" }}
123
123
  data-testid="test-select"
124
124
  size="small"
125
125
  />
126
126
  );
127
- const value = screen.getByRole("textbox");
128
- expect(value).toHaveTextContent("-");
129
- expect(value).toHaveAttribute("readOnly");
127
+ const select = screen.getByTestId("test-input");
128
+ expect(select).toBeDisabled();
129
+ expect(select).toHaveValue("select value");
130
130
  });
131
+
132
+ test("Select renders read only", () => {
133
+ render(
134
+ <Select
135
+ readOnly
136
+ helperText="helper text"
137
+ label="Test"
138
+ value="select value"
139
+ id="test-select"
140
+ inputProps={{ "data-testid": "test-input" }}
141
+ data-testid="test-select"
142
+ size="small"
143
+ />
144
+ );
145
+
146
+ const readOnlyField = screen.getByRole("textbox");
147
+ expect(readOnlyField).toHaveTextContent("select value");
148
+ expect(readOnlyField).toHaveProperty("readOnly", true);
149
+ });
150
+
131
151
  test("Select renders error", async () => {
132
152
  await act(async () => {
133
153
  const { findByText } = render(
@@ -6,11 +6,10 @@ import { act, Simulate } from "react-dom/test-utils";
6
6
  import { render, screen } from "@testing-library/react";
7
7
  import userEvent from "@testing-library/user-event";
8
8
 
9
-
10
9
  describe("TextArea", () => {
11
10
  test("TextArea exists, and simulates input correctly", async () => {
12
11
  await act(async () => {
13
- const {findByTestId, getByTestId} = render(
12
+ const { findByTestId, getByTestId } = render(
14
13
  <TextArea
15
14
  placeholder="test Name"
16
15
  required
@@ -32,14 +31,14 @@ describe("TextArea", () => {
32
31
  });
33
32
  test("TextArea renders with helper text", async () => {
34
33
  await act(async () => {
35
- const {findByText} = render(
34
+ const { findByText } = render(
36
35
  <TextArea
37
36
  error={false}
38
37
  placeholder="test Name"
39
38
  helperText="helper text"
40
39
  label="test Name"
41
40
  id="testName"
42
- inputProps={{"data-testid": "test-name-input"}}
41
+ inputProps={{ "data-testid": "test-name-input" }}
43
42
  data-testid="test-name-text-field"
44
43
  />
45
44
  );
@@ -47,6 +46,23 @@ describe("TextArea", () => {
47
46
  expect(helperText).toBeInTheDocument();
48
47
  });
49
48
  });
49
+ test("renders TextArea for readOnly mode", async () => {
50
+ render(
51
+ <TextArea
52
+ readOnly
53
+ placeholder="test Name"
54
+ helperText="helper text"
55
+ label="test Name"
56
+ id="testName"
57
+ inputProps={{ "data-testid": "test-name-input" }}
58
+ data-testid="test-name-text-field"
59
+ value="test value"
60
+ />
61
+ );
62
+ const textArea = await screen.findByRole("textbox");
63
+ expect(textArea).toHaveTextContent("test value");
64
+ expect(textArea).toHaveProperty("readOnly", true);
65
+ });
50
66
  test("renders TextArea for disabled mode", async () => {
51
67
  render(
52
68
  <TextArea
@@ -55,25 +71,25 @@ describe("TextArea", () => {
55
71
  helperText="helper text"
56
72
  label="test Name"
57
73
  id="testName"
58
- inputProps={{"data-testid": "test-name-input"}}
74
+ inputProps={{ "data-testid": "test-name-input" }}
59
75
  data-testid="test-name-text-field"
60
76
  value="test value"
61
77
  />
62
78
  );
63
79
  const textArea = await screen.findByRole("textbox");
64
80
  expect(textArea).toHaveTextContent("test value");
65
- expect(textArea).toHaveAttribute("readOnly");
81
+ expect(textArea).toBeDisabled();
66
82
  });
67
83
  test("HelperText renders errored", async () => {
68
84
  await act(async () => {
69
- const {findByText} = render(
85
+ const { findByText } = render(
70
86
  <TextArea
71
87
  error
72
88
  placeholder="test Name"
73
89
  helperText="helper text"
74
90
  label="test Name"
75
91
  id="testName"
76
- inputProps={{"data-testid": "test-name-input"}}
92
+ inputProps={{ "data-testid": "test-name-input" }}
77
93
  data-testid="test-name-text-field"
78
94
  />
79
95
  );
@@ -83,7 +99,7 @@ describe("TextArea", () => {
83
99
  });
84
100
  test("TextArea renders with default propTypes left out", async () => {
85
101
  await act(async () => {
86
- const {findByText} = render(
102
+ const { findByText } = render(
87
103
  <TextArea
88
104
  // error purposely omitted
89
105
  // disabled purposely omitted
@@ -91,7 +107,7 @@ describe("TextArea", () => {
91
107
  helperText="helper text"
92
108
  label="test Name"
93
109
  id="testName"
94
- inputProps={{"data-testid": "test-name-input"}}
110
+ inputProps={{ "data-testid": "test-name-input" }}
95
111
  data-testid="test-name-text-field"
96
112
  />
97
113
  );
@@ -101,7 +117,7 @@ describe("TextArea", () => {
101
117
  });
102
118
  test("TextArea shows maxLength", async () => {
103
119
  await act(async () => {
104
- const {findByTestId, getByTestId, findByText} = render(
120
+ const { findByTestId, getByTestId, findByText } = render(
105
121
  <TextArea
106
122
  placeholder="test Name"
107
123
  required
@@ -51,6 +51,24 @@ describe("TextField", () => {
51
51
  expect(helperText).toBeInTheDocument();
52
52
  });
53
53
  });
54
+ test("renders TextField for read only mode", async () => {
55
+ render(
56
+ <TextField
57
+ readOnly
58
+ placeholder="test Name"
59
+ helperText="helper text"
60
+ label="test Name"
61
+ id="testName"
62
+ inputProps={{ "data-testid": "test-name-input" }}
63
+ data-testid="test-name-text-field"
64
+ size="small"
65
+ value="test value"
66
+ />
67
+ );
68
+ const textField = await screen.findByRole("textbox");
69
+ expect(textField).toHaveValue("test value");
70
+ expect(textField).toHaveProperty("readOnly", true);
71
+ });
54
72
  test("renders TextField for disabled mode", async () => {
55
73
  render(
56
74
  <TextField
@@ -59,15 +77,15 @@ describe("TextField", () => {
59
77
  helperText="helper text"
60
78
  label="test Name"
61
79
  id="testName"
62
- inputProps={{"data-testid": "test-name-input"}}
80
+ inputProps={{ "data-testid": "test-name-input" }}
63
81
  data-testid="test-name-text-field"
64
82
  size="small"
65
83
  value="test value"
66
84
  />
67
85
  );
68
86
  const textField = await screen.findByRole("textbox");
69
- expect(textField).toHaveTextContent("test value");
70
- expect(textField).toHaveAttribute("readOnly");
87
+ expect(textField).toHaveValue("test value");
88
+ expect(textField).toBeDisabled();
71
89
  });
72
90
  test("HelperText renders errored", async () => {
73
91
  await act(async () => {