@kaizen/components 1.48.0 → 1.49.1
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/dist/cjs/DateInput/DateInput/DateInput.cjs +1 -0
- package/dist/cjs/DatePicker/DatePicker.cjs +7 -3
- package/dist/cjs/DateRangePicker/DateRangePicker.cjs +7 -2
- package/dist/cjs/EmptyState/EmptyState.cjs +0 -1
- package/dist/cjs/RichTextEditor/EditableRichTextContent/EditableRichTextContent.cjs +1 -0
- package/dist/cjs/RichTextEditor/EditableRichTextContent/EditableRichTextContent.module.scss.cjs +1 -0
- package/dist/cjs/RichTextEditor/RichTextEditor/RichTextEditor.cjs +1 -0
- package/dist/cjs/RichTextEditor/RichTextEditor/RichTextEditor.module.scss.cjs +1 -0
- package/dist/cjs/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.cjs +5 -1
- package/dist/cjs/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.module.scss.cjs +6 -0
- package/dist/cjs/RichTextEditor/utils/plugins/LinkManager/validation.cjs +8 -1
- package/dist/cjs/Select/Select.cjs +6 -2
- package/dist/cjs/__future__/Select/Select.cjs +12 -11
- package/dist/esm/DateInput/DateInput/DateInput.mjs +1 -0
- package/dist/esm/DatePicker/DatePicker.mjs +7 -3
- package/dist/esm/DateRangePicker/DateRangePicker.mjs +7 -2
- package/dist/esm/EmptyState/EmptyState.mjs +0 -1
- package/dist/esm/RichTextEditor/EditableRichTextContent/EditableRichTextContent.mjs +3 -2
- package/dist/esm/RichTextEditor/EditableRichTextContent/EditableRichTextContent.module.scss.mjs +1 -0
- package/dist/esm/RichTextEditor/RichTextEditor/RichTextEditor.mjs +3 -2
- package/dist/esm/RichTextEditor/RichTextEditor/RichTextEditor.module.scss.mjs +1 -0
- package/dist/esm/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.mjs +5 -1
- package/dist/esm/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.module.scss.mjs +4 -0
- package/dist/esm/RichTextEditor/utils/plugins/LinkManager/validation.mjs +2 -1
- package/dist/esm/Select/Select.mjs +6 -2
- package/dist/esm/__future__/Select/Select.mjs +12 -11
- package/dist/styles.css +9 -8
- package/dist/types/Input/Input/Input.d.ts +5 -0
- package/dist/types/RichTextEditor/utils/plugins/LinkManager/validation.d.ts +1 -0
- package/dist/types/Select/Select.d.ts +10 -0
- package/dist/types/TextArea/TextArea.d.ts +5 -0
- package/dist/types/__future__/Select/Select.d.ts +5 -0
- package/dist/types/__future__/Select/subcomponents/SelectToggle/SelectToggle.d.ts +8 -0
- package/package.json +6 -6
- package/src/DateInput/DateInput/DateInput.tsx +1 -0
- package/src/DatePicker/DatePicker.spec.tsx +14 -14
- package/src/DatePicker/DatePicker.tsx +20 -11
- package/src/DateRangePicker/DateRangePicker.tsx +14 -2
- package/src/DateRangePicker/_docs/DateRangePicker.mdx +5 -1
- package/src/DateRangePicker/_docs/DateRangePicker.stories.tsx +99 -3
- package/src/EmptyState/EmptyState.tsx +1 -4
- package/src/FieldGroup/_docs/FieldGroup.stickersheet.stories.tsx +2 -12
- package/src/FieldGroup/_docs/FieldGroup.stories.tsx +4 -9
- package/src/Input/Input/Input.tsx +5 -0
- package/src/Input/InputSearch/InputSearch.spec.tsx +10 -7
- package/src/Notification/ToastNotification/ToastNotificationsList/ToastNotificationsList.module.scss +1 -1
- package/src/RichTextEditor/EditableRichTextContent/EditableRichTextContent.module.scss +25 -7
- package/src/RichTextEditor/EditableRichTextContent/EditableRichTextContent.tsx +3 -1
- package/src/RichTextEditor/EditableRichTextContent/_docs/EditableRichTextContent.mdx +6 -4
- package/src/RichTextEditor/EditableRichTextContent/_docs/EditableRichTextContent.stickersheet.stories.tsx +98 -0
- package/src/RichTextEditor/EditableRichTextContent/_docs/EditableRichTextContent.stories.tsx +8 -5
- package/src/RichTextEditor/EditableRichTextContent/_docs/defaultContent.json +11 -0
- package/src/RichTextEditor/EditableRichTextContent/_docs/dummyContent.json +44 -39
- package/src/RichTextEditor/RichTextContent/_docs/RichTextContent.mdx +11 -1
- package/src/RichTextEditor/RichTextContent/_docs/RichTextContent.stories.tsx +47 -2
- package/src/RichTextEditor/RichTextEditor/RichTextEditor.module.scss +6 -1
- package/src/RichTextEditor/RichTextEditor/RichTextEditor.spec.stories.tsx +48 -0
- package/src/RichTextEditor/RichTextEditor/RichTextEditor.tsx +7 -2
- package/src/RichTextEditor/RichTextEditor/_docs/RichTextEditor.mdx +66 -7
- package/src/RichTextEditor/RichTextEditor/_docs/RichTextEditor.stories.tsx +60 -7
- package/src/RichTextEditor/_mixins.scss +1 -0
- package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.module.scss +5 -0
- package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.spec.stories.tsx +37 -0
- package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.stories.tsx +33 -0
- package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.tsx +9 -1
- package/src/RichTextEditor/utils/plugins/LinkManager/{validation.ts → validation.tsx} +11 -1
- package/src/Select/Select.tsx +9 -1
- package/src/Select/_docs/Select.stories.tsx +0 -3
- package/src/TextArea/TextArea.tsx +5 -0
- package/src/__future__/Select/Select.tsx +6 -1
- package/src/__future__/Select/_docs/Select.stickersheet.stories.tsx +0 -9
- package/src/__future__/Select/subcomponents/SelectToggle/SelectToggle.tsx +4 -0
|
@@ -1,11 +1,50 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"type": "paragraph",
|
|
4
|
+
"content": [
|
|
5
|
+
{
|
|
6
|
+
"type": "text",
|
|
7
|
+
"text": "User text goes here"
|
|
8
|
+
}
|
|
9
|
+
]
|
|
10
|
+
},
|
|
2
11
|
{
|
|
3
12
|
"type": "paragraph",
|
|
4
13
|
"content": [
|
|
5
14
|
{
|
|
6
15
|
"type": "text",
|
|
7
16
|
"marks": [{ "type": "strong" }],
|
|
8
|
-
"text": "
|
|
17
|
+
"text": "bold"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"type": "paragraph",
|
|
23
|
+
"content": [
|
|
24
|
+
{
|
|
25
|
+
"type": "text",
|
|
26
|
+
"marks": [{ "type": "em" }],
|
|
27
|
+
"text": "underline"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "paragraph",
|
|
33
|
+
"content": [
|
|
34
|
+
{
|
|
35
|
+
"type": "text",
|
|
36
|
+
"text": "link",
|
|
37
|
+
"marks": [
|
|
38
|
+
{
|
|
39
|
+
"type": "link",
|
|
40
|
+
"attrs": {
|
|
41
|
+
"href": "https://cultureamp.design",
|
|
42
|
+
"_metadata": null,
|
|
43
|
+
"target": "_blank",
|
|
44
|
+
"rel": "noreferrer"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
9
48
|
}
|
|
10
49
|
]
|
|
11
50
|
},
|
|
@@ -17,24 +56,7 @@
|
|
|
17
56
|
"content": [
|
|
18
57
|
{
|
|
19
58
|
"type": "paragraph",
|
|
20
|
-
"content": [
|
|
21
|
-
{ "type": "text", "text": "Note 1 " },
|
|
22
|
-
{
|
|
23
|
-
"type": "text",
|
|
24
|
-
"marks": [
|
|
25
|
-
{
|
|
26
|
-
"type": "link",
|
|
27
|
-
"attrs": {
|
|
28
|
-
"href": "https://cultureamp.design",
|
|
29
|
-
"_metadata": null,
|
|
30
|
-
"target": "_blank",
|
|
31
|
-
"rel": "noreferrer"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
],
|
|
35
|
-
"text": "with link"
|
|
36
|
-
}
|
|
37
|
-
]
|
|
59
|
+
"content": [{ "type": "text", "text": "Bullet list" }]
|
|
38
60
|
},
|
|
39
61
|
{
|
|
40
62
|
"type": "bulletList",
|
|
@@ -73,29 +95,12 @@
|
|
|
73
95
|
"content": [
|
|
74
96
|
{
|
|
75
97
|
"type": "paragraph",
|
|
76
|
-
"content": [
|
|
77
|
-
{ "type": "text", "text": "Another really " },
|
|
78
|
-
{
|
|
79
|
-
"type": "text",
|
|
80
|
-
"marks": [{ "type": "underline" }],
|
|
81
|
-
"text": "important note"
|
|
82
|
-
},
|
|
83
|
-
{ "type": "text", "text": " " },
|
|
84
|
-
{
|
|
85
|
-
"type": "text",
|
|
86
|
-
"marks": [{ "type": "em" }],
|
|
87
|
-
"text": "(that I'd like to emphasise)"
|
|
88
|
-
}
|
|
89
|
-
]
|
|
98
|
+
"content": [{ "type": "text", "text": "List item" }]
|
|
90
99
|
}
|
|
91
100
|
]
|
|
92
101
|
}
|
|
93
102
|
]
|
|
94
103
|
},
|
|
95
|
-
{
|
|
96
|
-
"type": "paragraph",
|
|
97
|
-
"content": [{ "type": "text", "text": "Additionally:" }]
|
|
98
|
-
},
|
|
99
104
|
{
|
|
100
105
|
"type": "orderedList",
|
|
101
106
|
"attrs": { "order": 1 },
|
|
@@ -105,7 +110,7 @@
|
|
|
105
110
|
"content": [
|
|
106
111
|
{
|
|
107
112
|
"type": "paragraph",
|
|
108
|
-
"content": [{ "type": "text", "text": "
|
|
113
|
+
"content": [{ "type": "text", "text": "Numbered list" }]
|
|
109
114
|
},
|
|
110
115
|
{
|
|
111
116
|
"type": "orderedList",
|
|
@@ -149,7 +154,7 @@
|
|
|
149
154
|
"content": [
|
|
150
155
|
{
|
|
151
156
|
"type": "paragraph",
|
|
152
|
-
"content": [{ "type": "text", "text": "
|
|
157
|
+
"content": [{ "type": "text", "text": "List item" }]
|
|
153
158
|
}
|
|
154
159
|
]
|
|
155
160
|
}
|
|
@@ -15,7 +15,17 @@ import * as RichTextContentStories from "./RichTextContent.stories"
|
|
|
15
15
|
|
|
16
16
|
## Overview
|
|
17
17
|
|
|
18
|
-
To render
|
|
18
|
+
To render rich content as it appears in the [RichTextEditor](/docs/components-richtexteditor-richtexteditor--docs) in read-only format.
|
|
19
19
|
|
|
20
20
|
<Canvas of={RichTextContentStories.Playground} />
|
|
21
21
|
<Controls of={RichTextContentStories.Playground} />
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
A common use case of `RichTextContent` is to display user generated output as read-only text.
|
|
27
|
+
|
|
28
|
+
<Canvas of={RichTextContentStories.ReadOnly} />
|
|
29
|
+
|
|
30
|
+
This should not be used out of the box to toggle between active and inactive states of the `RichTextEditor`. Instead we recommend using the [EditableRichTextContent](/docs/components-richtexteditor-editablerichtextcontent--docs#usage) pattern, which indicates interactivity to the user and ensures compliance with WCAG guidelines.
|
|
31
|
+
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
+
import React from "react"
|
|
1
2
|
import { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { Text } from "~components/Text"
|
|
2
4
|
import { RichTextContent } from "../index"
|
|
3
|
-
import dummyContent from "./dummyContent.json"
|
|
4
5
|
|
|
5
6
|
const meta = {
|
|
6
7
|
title: "Components/RichTextEditor/RichTextContent",
|
|
7
8
|
component: RichTextContent,
|
|
8
9
|
args: {
|
|
9
|
-
content:
|
|
10
|
+
content: [
|
|
11
|
+
{
|
|
12
|
+
type: "paragraph",
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "User text goes here",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
},
|
|
20
|
+
],
|
|
10
21
|
},
|
|
11
22
|
argTypes: {
|
|
12
23
|
content: { control: false },
|
|
@@ -22,3 +33,37 @@ export const Playground: Story = {
|
|
|
22
33
|
chromatic: { disable: false },
|
|
23
34
|
},
|
|
24
35
|
}
|
|
36
|
+
|
|
37
|
+
export const ReadOnly: Story = {
|
|
38
|
+
parameters: {
|
|
39
|
+
chromatic: { disable: false },
|
|
40
|
+
},
|
|
41
|
+
args: {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "paragraph",
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: "User text goes here",
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
render: args => (
|
|
55
|
+
<>
|
|
56
|
+
{/* Note that since RichTextContent is not content editable, it is essentially just a div. This is why we haven't used the Label component */}
|
|
57
|
+
<Text
|
|
58
|
+
variant="small"
|
|
59
|
+
tag="div"
|
|
60
|
+
classNameOverride="block mb-6 leading-paragraph font-weight-heading"
|
|
61
|
+
>
|
|
62
|
+
Read only state
|
|
63
|
+
</Text>
|
|
64
|
+
<div className="p-12 bg-gray-200 rounded-default">
|
|
65
|
+
<RichTextContent {...args} />
|
|
66
|
+
</div>
|
|
67
|
+
</>
|
|
68
|
+
),
|
|
69
|
+
}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
border-color $animation-duration-immediate;
|
|
17
17
|
|
|
18
18
|
&:hover,
|
|
19
|
-
&:focus {
|
|
19
|
+
&:focus-visible {
|
|
20
20
|
border-color: $color-gray-600;
|
|
21
21
|
background: $color-gray-200;
|
|
22
22
|
}
|
|
@@ -46,6 +46,11 @@
|
|
|
46
46
|
border-top-right-radius: 0;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
.editorLabel {
|
|
50
|
+
margin-bottom: $spacing-6;
|
|
51
|
+
display: inline-block;
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
/* stylelint-disable no-descending-specificity */
|
|
50
55
|
.editorWrapper {
|
|
51
56
|
position: relative;
|
|
@@ -147,3 +147,51 @@ export const IncreaseIndent: Story = {
|
|
|
147
147
|
})
|
|
148
148
|
},
|
|
149
149
|
}
|
|
150
|
+
|
|
151
|
+
export const CreateALink: Story = {
|
|
152
|
+
...TestBase,
|
|
153
|
+
name: "Create a link",
|
|
154
|
+
play: async context => {
|
|
155
|
+
const { canvasElement, step } = context
|
|
156
|
+
const { getByRole, getByText } = within(canvasElement)
|
|
157
|
+
const editor = getByRole("textbox")
|
|
158
|
+
await step("Focus on editor", async () => {
|
|
159
|
+
await userEvent.click(editor)
|
|
160
|
+
expect(editor).toHaveFocus()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
await step("Input text and select the first word", async () => {
|
|
164
|
+
await userEvent.keyboard("Link me")
|
|
165
|
+
await userEvent.pointer([
|
|
166
|
+
{
|
|
167
|
+
target: getByText("Link me"),
|
|
168
|
+
offset: 0,
|
|
169
|
+
keys: "[MouseLeft>]",
|
|
170
|
+
},
|
|
171
|
+
{ offset: 4 },
|
|
172
|
+
])
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
await step("click the link button", async () => {
|
|
176
|
+
await userEvent.click(getByRole("button", { name: "Link" }))
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// wait for the transition to end and focus to shift
|
|
180
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
181
|
+
|
|
182
|
+
await step("Enter text", async () => {
|
|
183
|
+
await userEvent.keyboard("https://www.google.com")
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
187
|
+
|
|
188
|
+
await step("Tab and save", async () => {
|
|
189
|
+
await userEvent.keyboard("{Tab}{Enter}")
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
await step("Link exists in the RTE", async () => {
|
|
193
|
+
const link = getByRole("link", { name: "Link" })
|
|
194
|
+
expect(link).toBeInTheDocument()
|
|
195
|
+
})
|
|
196
|
+
},
|
|
197
|
+
}
|
|
@@ -159,8 +159,13 @@ export const RichTextEditor = ({
|
|
|
159
159
|
|
|
160
160
|
return (
|
|
161
161
|
<>
|
|
162
|
-
{!labelledBy && labelText &&
|
|
163
|
-
|
|
162
|
+
{!labelledBy && labelText && (
|
|
163
|
+
<Label
|
|
164
|
+
classNameOverride={styles.editorLabel}
|
|
165
|
+
id={labelId}
|
|
166
|
+
labelText={labelText}
|
|
167
|
+
/>
|
|
168
|
+
)}
|
|
164
169
|
<div className={classnames(styles.editorWrapper, styles[status])}>
|
|
165
170
|
{controls && (
|
|
166
171
|
<Toolbar
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Canvas, Controls, DocsStory, Meta } from "@storybook/blocks"
|
|
2
2
|
import { ResourceLinks, KAIOInstallation } from "~storybook/components"
|
|
3
|
+
import * as EditableRichTextContentStories from "../../EditableRichTextContent/_docs/EditableRichTextContent.stories"
|
|
4
|
+
import * as RichTextContentStories from "../../RichTextContent/_docs/RichTextContent.stories"
|
|
3
5
|
import * as RichTextEditorStories from "./RichTextEditor.stories"
|
|
4
6
|
|
|
5
7
|
<Meta of={RichTextEditorStories} />
|
|
@@ -24,21 +26,78 @@ A text area with additional capabilities for users to format the input text.
|
|
|
24
26
|
|
|
25
27
|
## API
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
### Controls
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
`controls` accepts an array of `ToolbarItems` that are used to create the `RichTextEditor` schema and build out its core functionality. It also offers the ability to group the items in the toolbar.
|
|
32
|
+
|
|
33
|
+
<Canvas of={RichTextEditorStories.Controls} />
|
|
34
|
+
|
|
35
|
+
As the schema is defined by the `controls`, removing an item will remove its functionality from the `RichTextEditor`.
|
|
36
|
+
|
|
37
|
+
<Canvas of={RichTextEditorStories.ControlsWithoutBold} />
|
|
38
|
+
|
|
39
|
+
With all controls, the Kaizen `RichTextEditor` can create and render formatted text, lists, and links.
|
|
40
|
+
|
|
41
|
+
<Canvas of={RichTextEditorStories.AllAvailableContent} />
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
### The EditorContentArray and defaultValue
|
|
45
|
+
|
|
46
|
+
The `defaultValue` is the initial content of the `RichTextEditor`. It accepts an array of objects in the [ProseMirror's rich text format](https://cultureamp.atlassian.net/wiki/spaces/TV/pages/3380543671/ProseMirror+rich+formatted+text+data+format).
|
|
32
47
|
|
|
33
48
|
<Canvas of={RichTextEditorStories.DefaultValue} />
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
|
|
50
|
+
As mentioned in the previous section, the data (`EditorContentArray`) that is passed to the `defaultValue` must be able to map to the `controls` provided.
|
|
51
|
+
|
|
52
|
+
For example: if your `defaultValue` contains bolded text, you must pass bold into your `controls`.
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
<RichTextEditor
|
|
56
|
+
labelText="Rich text"
|
|
57
|
+
defaultValue={rteData}
|
|
58
|
+
onChange={handleOnChange}
|
|
59
|
+
controls: [
|
|
60
|
+
{ name: "bold", group: "inline" },
|
|
61
|
+
{/* other controls */}
|
|
62
|
+
]
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Data errors and onDataError
|
|
67
|
+
When content is passed to the `defaultValue` that does not match to the `RichTextEditor`'s [schema](https://github.com/cultureamp/kaizen-design-system/blob/main/packages/components/src/RichTextEditor/RichTextEditor/schema.ts), the component will throw and render a generic error.
|
|
37
68
|
|
|
38
69
|
<Canvas of={RichTextEditorStories.MalformedContent} />
|
|
39
70
|
|
|
40
|
-
|
|
71
|
+
This will also throw if you have passed in an `EditorContentArray` that contains data that cannot map to the `controls` provided to the component.
|
|
72
|
+
|
|
73
|
+
<Canvas of={RichTextEditorStories.IncorrectDataForControls}/>
|
|
74
|
+
|
|
75
|
+
The `dataError` React Node and `onDataError` callback also allows you to handle these edge cases without breaking the page.
|
|
76
|
+
|
|
77
|
+
### Rows
|
|
78
|
+
|
|
79
|
+
Sets the minimum height for the editable area of the RichTextEditor.
|
|
80
|
+
|
|
81
|
+
<Canvas of={RichTextEditorStories.Rows} />
|
|
41
82
|
|
|
42
83
|
<DocsStory of={RichTextEditorStories.Description} />
|
|
43
84
|
|
|
44
85
|
<DocsStory of={RichTextEditorStories.Validation} />
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
### Inactive states and read-only text
|
|
89
|
+
|
|
90
|
+
In addition to the `RichTextEditor`, there are two additional Kaizen components that support rendering data in the RTE format.
|
|
91
|
+
|
|
92
|
+
#### EditableRichTextContent
|
|
93
|
+
|
|
94
|
+
For rendering editable content that can toggle between an active and inactive state we recommend the [EditableRichTextContent](/docs/components-richtexteditor-editablerichtextcontent--docs).
|
|
95
|
+
|
|
96
|
+
<Canvas of={EditableRichTextContentStories.UsageExample} />
|
|
97
|
+
|
|
98
|
+
#### RichTextContent
|
|
99
|
+
|
|
100
|
+
For rendering content as read-only text we recommend using the [RichTextContent](/docs/components-richtexteditor-richtextcontent--docs).
|
|
101
|
+
|
|
102
|
+
<Canvas of={RichTextContentStories.Playground} />
|
|
103
|
+
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React, { useState } from "react"
|
|
2
2
|
import { Meta, StoryObj } from "@storybook/react"
|
|
3
|
+
import { fn } from "@storybook/test"
|
|
4
|
+
import dummyContent from "../../EditableRichTextContent/_docs/dummyContent.json"
|
|
3
5
|
import { EditorContentArray } from "../../index"
|
|
4
6
|
import { RichTextEditor, RichTextEditorProps } from "../index"
|
|
5
7
|
|
|
@@ -77,9 +79,26 @@ export const Controls: Story = {
|
|
|
77
79
|
{ name: "bold", group: "inline" },
|
|
78
80
|
{ name: "italic", group: "inline" },
|
|
79
81
|
{ name: "underline", group: "inline" },
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const ControlsWithoutBold: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
controls: [
|
|
89
|
+
{ name: "italic", group: "inline" },
|
|
90
|
+
{ name: "underline", group: "inline" },
|
|
91
|
+
],
|
|
92
|
+
defaultValue: [
|
|
93
|
+
{
|
|
94
|
+
type: "paragraph",
|
|
95
|
+
content: [
|
|
96
|
+
{
|
|
97
|
+
type: "text",
|
|
98
|
+
text: "This user text cannot be bolded",
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
83
102
|
],
|
|
84
103
|
},
|
|
85
104
|
}
|
|
@@ -92,7 +111,7 @@ export const DefaultValue: Story = {
|
|
|
92
111
|
content: [
|
|
93
112
|
{
|
|
94
113
|
type: "text",
|
|
95
|
-
text: "
|
|
114
|
+
text: "User text goes here",
|
|
96
115
|
},
|
|
97
116
|
],
|
|
98
117
|
},
|
|
@@ -100,6 +119,20 @@ export const DefaultValue: Story = {
|
|
|
100
119
|
},
|
|
101
120
|
}
|
|
102
121
|
|
|
122
|
+
export const AllAvailableContent: Story = {
|
|
123
|
+
args: {
|
|
124
|
+
defaultValue: dummyContent,
|
|
125
|
+
controls: [
|
|
126
|
+
{ name: "bold", group: "inline" },
|
|
127
|
+
{ name: "italic", group: "inline" },
|
|
128
|
+
{ name: "underline", group: "inline" },
|
|
129
|
+
{ name: "orderedList", group: "list" },
|
|
130
|
+
{ name: "bulletList", group: "list" },
|
|
131
|
+
{ name: "link", group: "link" },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
|
|
103
136
|
export const Rows: Story = {
|
|
104
137
|
args: {
|
|
105
138
|
labelText: "1 Row",
|
|
@@ -109,13 +142,13 @@ export const Rows: Story = {
|
|
|
109
142
|
|
|
110
143
|
export const Description: Story = {
|
|
111
144
|
args: {
|
|
112
|
-
description: "
|
|
145
|
+
description: "Description text",
|
|
113
146
|
},
|
|
114
147
|
}
|
|
115
148
|
|
|
116
149
|
export const Validation: Story = {
|
|
117
150
|
args: {
|
|
118
|
-
validationMessage: "
|
|
151
|
+
validationMessage: "Error message",
|
|
119
152
|
status: "error",
|
|
120
153
|
},
|
|
121
154
|
}
|
|
@@ -129,10 +162,30 @@ export const MalformedContent: Story = {
|
|
|
129
162
|
{
|
|
130
163
|
type: "text",
|
|
131
164
|
marks: [{ type: "strong" }],
|
|
132
|
-
text: "
|
|
165
|
+
text: "User text goes here in bold text",
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
},
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const IncorrectDataForControls: Story = {
|
|
174
|
+
args: {
|
|
175
|
+
defaultValue: [
|
|
176
|
+
{
|
|
177
|
+
type: "paragraph",
|
|
178
|
+
content: [
|
|
179
|
+
{
|
|
180
|
+
type: "text",
|
|
181
|
+
marks: [{ type: "strong" }],
|
|
182
|
+
text: "User text goes here in bold text",
|
|
133
183
|
},
|
|
134
184
|
],
|
|
135
185
|
},
|
|
136
186
|
],
|
|
187
|
+
controls: [{ name: "italic", group: "inline" }],
|
|
188
|
+
dataError: <>Cannot bold text without a bold control</>,
|
|
189
|
+
onDataError: () => fn(),
|
|
137
190
|
},
|
|
138
191
|
}
|
package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.spec.stories.tsx
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { userEvent, waitFor, fn, expect } from "@storybook/test"
|
|
3
|
+
import { LinkModal } from "./LinkModal"
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "Components/RichTextEditor/Subcomponents/LinkModal/Tests",
|
|
7
|
+
component: LinkModal,
|
|
8
|
+
args: {
|
|
9
|
+
onSubmit: fn(),
|
|
10
|
+
onDismiss: fn(),
|
|
11
|
+
onAfterLeave: fn(),
|
|
12
|
+
isOpen: true,
|
|
13
|
+
},
|
|
14
|
+
} satisfies Meta<typeof LinkModal>
|
|
15
|
+
|
|
16
|
+
export default meta
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof meta>
|
|
19
|
+
|
|
20
|
+
export const InvalidLink: Story = {
|
|
21
|
+
parameters: {
|
|
22
|
+
chromatic: { disable: false },
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
defaultHref: "google.com",
|
|
26
|
+
},
|
|
27
|
+
play: async () => {
|
|
28
|
+
await new Promise(resolve => setTimeout(resolve, 500))
|
|
29
|
+
await userEvent.keyboard("{Tab}{Enter}")
|
|
30
|
+
|
|
31
|
+
await waitFor(() => {
|
|
32
|
+
expect(document.activeElement).toHaveAccessibleDescription(
|
|
33
|
+
/Empty or invalid link\. Links must start with http or https/
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
},
|
|
37
|
+
}
|
package/src/RichTextEditor/utils/plugins/LinkManager/components/LinkModal/LinkModal.stories.tsx
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Meta, StoryObj } from "@storybook/react"
|
|
2
|
+
import { fn } from "@storybook/test"
|
|
3
|
+
import { LinkModal } from "./LinkModal"
|
|
4
|
+
|
|
5
|
+
const meta = {
|
|
6
|
+
title: "Components/RichTextEditor/Subcomponents/LinkModal",
|
|
7
|
+
component: LinkModal,
|
|
8
|
+
args: {
|
|
9
|
+
onSubmit: fn(),
|
|
10
|
+
onDismiss: fn(),
|
|
11
|
+
onAfterLeave: fn(),
|
|
12
|
+
isOpen: true,
|
|
13
|
+
},
|
|
14
|
+
} satisfies Meta<typeof LinkModal>
|
|
15
|
+
|
|
16
|
+
export default meta
|
|
17
|
+
|
|
18
|
+
type Story = StoryObj<typeof meta>
|
|
19
|
+
|
|
20
|
+
export const AddLink: Story = {
|
|
21
|
+
parameters: {
|
|
22
|
+
chromatic: { disable: false },
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const EditLink: Story = {
|
|
27
|
+
parameters: {
|
|
28
|
+
chromatic: { disable: false },
|
|
29
|
+
},
|
|
30
|
+
args: {
|
|
31
|
+
defaultHref: "http://google.com",
|
|
32
|
+
},
|
|
33
|
+
}
|
|
@@ -2,6 +2,7 @@ import React, { useRef, useState } from "react"
|
|
|
2
2
|
import { InputEditModal } from "~components/Modal"
|
|
3
3
|
import { TextField } from "~components/TextField"
|
|
4
4
|
import { ValidationResponse, validateLink } from "../../validation"
|
|
5
|
+
import styles from "./LinkModal.module.scss"
|
|
5
6
|
|
|
6
7
|
type LinkModalProps = {
|
|
7
8
|
onSubmit: (href: string) => void
|
|
@@ -51,8 +52,15 @@ export const LinkModal = ({
|
|
|
51
52
|
type="text"
|
|
52
53
|
defaultValue={href ?? ""}
|
|
53
54
|
labelText="Link URL"
|
|
55
|
+
description="Must start with http:// or https://"
|
|
54
56
|
inputRef={inputRef}
|
|
55
|
-
validationMessage={
|
|
57
|
+
validationMessage={
|
|
58
|
+
validationStatus?.message && (
|
|
59
|
+
<div className={styles.validationErrorMessage}>
|
|
60
|
+
{validationStatus.message}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
56
64
|
status={validationStatus.status}
|
|
57
65
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
58
66
|
setHref(e.target.value)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
1
3
|
export type ValidationResponse = {
|
|
2
4
|
status: "success" | "error" | "default"
|
|
3
5
|
message?: React.ReactNode
|
|
@@ -9,7 +11,15 @@ export const validateLink = (href: string): ValidationResponse => {
|
|
|
9
11
|
if (!isValidLink) {
|
|
10
12
|
return {
|
|
11
13
|
status: "error",
|
|
12
|
-
message:
|
|
14
|
+
message: (
|
|
15
|
+
<>
|
|
16
|
+
Empty or invalid link. Links must start with http or https, e.g:
|
|
17
|
+
<ul>
|
|
18
|
+
<li>https://google.com</li>
|
|
19
|
+
<li>http://www.google.com</li>
|
|
20
|
+
</ul>
|
|
21
|
+
</>
|
|
22
|
+
),
|
|
13
23
|
}
|
|
14
24
|
}
|
|
15
25
|
|
package/src/Select/Select.tsx
CHANGED
|
@@ -35,6 +35,11 @@ export type SelectProps = {
|
|
|
35
35
|
* @default false
|
|
36
36
|
*/
|
|
37
37
|
fullWidth?: boolean
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Use of placeholder text goes against our a11y standards.
|
|
40
|
+
* Use the `labelText` prop to provide a concise name, and the `description` prop for any help text.
|
|
41
|
+
*/
|
|
42
|
+
placeholder?: string
|
|
38
43
|
} & ReactSelectProps<any, boolean>
|
|
39
44
|
|
|
40
45
|
/**
|
|
@@ -53,6 +58,7 @@ export const Select = React.forwardRef<any, SelectProps>(
|
|
|
53
58
|
fullWidth: propsFullWidth,
|
|
54
59
|
className: propsClassName,
|
|
55
60
|
isDisabled,
|
|
61
|
+
placeholder,
|
|
56
62
|
...props
|
|
57
63
|
},
|
|
58
64
|
ref
|
|
@@ -92,6 +98,7 @@ export const Select = React.forwardRef<any, SelectProps>(
|
|
|
92
98
|
{...props}
|
|
93
99
|
ref={ref}
|
|
94
100
|
aria-labelledby={labelId}
|
|
101
|
+
placeholder={placeholder || ""}
|
|
95
102
|
components={{
|
|
96
103
|
Control,
|
|
97
104
|
Placeholder,
|
|
@@ -126,12 +133,13 @@ interface AsyncProps
|
|
|
126
133
|
|
|
127
134
|
export const AsyncSelect = React.forwardRef(
|
|
128
135
|
(
|
|
129
|
-
{ className: propsClassName, ...props }: AsyncProps,
|
|
136
|
+
{ className: propsClassName, placeholder, ...props }: AsyncProps,
|
|
130
137
|
ref: React.Ref<any>
|
|
131
138
|
) => (
|
|
132
139
|
<Async
|
|
133
140
|
{...props}
|
|
134
141
|
ref={ref}
|
|
142
|
+
placeholder={placeholder || ""}
|
|
135
143
|
components={{
|
|
136
144
|
Control,
|
|
137
145
|
Placeholder,
|