Skip to content

Commit 7d012e8

Browse files
Merge release/2.7.0 into main branch (#1174)
* Update RichTextEditor to be a forwardRef and expose setContent through useImperativeHandle (#1173) * Bump express from 4.18.2 to 4.19.2 (#1171) * Converts Card to typescript (#1160)
1 parent 6a48a84 commit 7d012e8

File tree

8 files changed

+176
-151
lines changed

8 files changed

+176
-151
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@user-interviews/ui-design-system",
3-
"version": "2.6.1",
3+
"version": "2.7.0",
44
"dependencies": {
55
"@tiptap/core": "^2.0.3",
66
"@tiptap/extension-bold": "^2.0.3",

src/Card/Card.jsx

Lines changed: 0 additions & 111 deletions
This file was deleted.

src/Card/Card.tsx

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import React, {
2+
createElement,
3+
type DetailedHTMLProps,
4+
type HTMLAttributes,
5+
type ReactNode,
6+
} from 'react';
7+
import classNames from 'classnames';
8+
9+
import { LoadingSkeleton } from 'src/LoadingSkeleton';
10+
11+
import './Card.scss';
12+
13+
export const CardSizes = {
14+
EXTRA_SMALL: 'xs',
15+
SMALL: 'sm',
16+
MEDIUM: 'md',
17+
LARGE: 'lg',
18+
} as const;
19+
20+
type ElementProps = DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement>;
21+
22+
type CardProps = {
23+
children?: ReactNode;
24+
className?: string;
25+
divided?: boolean;
26+
elementType?: string;
27+
helperText?: ReactNode;
28+
isLoading?: boolean;
29+
loadingSkeleton?: ReactNode;
30+
loadingSkeletonParagraphCount?: number;
31+
noPadding?: boolean;
32+
size: 'xs' | 'sm' | 'md' | 'lg';
33+
subTitle?: ReactNode;
34+
title?: ReactNode;
35+
} & ElementProps;
36+
37+
const Card = ({
38+
children,
39+
className,
40+
divided = false,
41+
elementType = 'section',
42+
helperText,
43+
isLoading = false,
44+
loadingSkeleton,
45+
loadingSkeletonParagraphCount = 1,
46+
noPadding = false,
47+
size,
48+
subTitle,
49+
title,
50+
...props
51+
}: CardProps) => {
52+
const defaultLoadingSkeleton = (
53+
<>
54+
<LoadingSkeleton height={24} width="33%" />
55+
<br />
56+
{Array(loadingSkeletonParagraphCount).fill(0).map((_, i) => (
57+
// eslint-disable-next-line react/no-array-index-key
58+
<div className="Card__loading-skeleton-paragraphs" key={`skeleton-paragraph-${i}`}>
59+
<LoadingSkeleton count={3} />
60+
</div>
61+
))}
62+
</>
63+
);
64+
65+
return createElement(
66+
elementType,
67+
{
68+
...props,
69+
className: classNames(
70+
'Card',
71+
{ [`Card--${size}`]: size },
72+
className,
73+
{
74+
'Card--divided': divided,
75+
'Card--no-padding': noPadding,
76+
},
77+
),
78+
},
79+
<>
80+
{isLoading ? (loadingSkeleton || defaultLoadingSkeleton) : (
81+
<>
82+
{title && (
83+
<div className="Card__header">
84+
<h2 className="Card__title">{title}</h2>
85+
{helperText && <span className="Card__helper-text">{helperText}</span>}
86+
</div>
87+
)}
88+
89+
{divided && <hr className="Card__divider" /> }
90+
{subTitle && <h3 className="Card__subtitle">{subTitle}</h3> }
91+
{children}
92+
</>
93+
)}
94+
</>,
95+
);
96+
};
97+
98+
export default Card;
File renamed without changes.

src/RichTextEditor/RichTextEditor.mdx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import * as ComponentStories from './RichTextEditor.stories';
1010

1111
<Canvas of={ComponentStories.Default} />
1212

13-
### When to use
13+
### When to use
1414
To allow users to edit text where formatting is important, such as messages to be sent via email.
1515

1616
### When to not use
@@ -45,3 +45,7 @@ RichTextEditor also has an error state to display that the input is invalid. The
4545
but by setting `hasErrors` via your provided `onChange` callback you can check the HTML produced by the RichTextEditor for any requirements that you have.
4646

4747
<Canvas of={ComponentStories.Error} />
48+
49+
RichTextEditor can optionally take in a forwardedRef to allow content to be programmatically manipulated.
50+
51+
<Canvas of={ComponentStories.SetContent} />

src/RichTextEditor/RichTextEditor.stories.jsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import React from 'react';
1+
import React, { useRef } from 'react';
22

3+
import Button from 'src/Button';
34
import { RichTextEditor, RichTextEditorActions } from 'src/RichTextEditor';
45

56
import mdx from './RichTextEditor.mdx';
@@ -60,3 +61,22 @@ export const Error = () => (
6061
onChange={() => null}
6162
/>
6263
);
64+
65+
export const SetContent = () => {
66+
const ref = useRef(null);
67+
68+
const handleClick = () => {
69+
ref.current.setContent('Oh hey');
70+
};
71+
72+
return (
73+
<>
74+
<Button onClick={handleClick}>Set content to "Oh hey"</Button>
75+
<RichTextEditor
76+
id="text-editor"
77+
ref={ref}
78+
onChange={() => null}
79+
/>
80+
</>
81+
);
82+
};

src/RichTextEditor/RichTextEditor.tsx

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Extension, Node as TipTapNode, Mark } from '@tiptap/core';
44

55
import './RichTextEditor.scss';
66

7-
import React from 'react';
7+
import React, { forwardRef, type ForwardedRef, useImperativeHandle } from 'react';
88

99
import classNames from 'classnames';
1010

@@ -94,21 +94,28 @@ export type RichTextEditorProps = {
9494
onChange: (arg0: string) => void;
9595
}
9696

97-
function RichTextEditor({
98-
allowedAttributes,
99-
allowedTags,
100-
ariaAttributes,
101-
availableActions = RichTextEditorDefaultActionsArray,
102-
characterLimit,
103-
className,
104-
hasErrors,
105-
id,
106-
initialValue,
107-
isOneLine,
108-
onChange,
109-
placeholder,
110-
customExtensions = [],
111-
}: RichTextEditorProps) {
97+
export type RichTextEditorRef = {
98+
setContent: (content: string) => void;
99+
}
100+
101+
const RichTextEditor = forwardRef((
102+
{
103+
allowedAttributes,
104+
allowedTags,
105+
ariaAttributes,
106+
availableActions = RichTextEditorDefaultActionsArray,
107+
characterLimit,
108+
className,
109+
hasErrors,
110+
id,
111+
initialValue,
112+
isOneLine,
113+
onChange,
114+
placeholder,
115+
customExtensions = [],
116+
}: RichTextEditorProps,
117+
ref: ForwardedRef<RichTextEditorRef> = null,
118+
) => {
112119
const oneLineExtension = isOneLine ? [OneLineLimit] : [];
113120

114121
const requiredExtensions = [
@@ -183,6 +190,13 @@ function RichTextEditor({
183190
},
184191
});
185192

193+
useImperativeHandle(ref, () => ({
194+
setContent: (content: string) => {
195+
editor?.commands.setContent(content);
196+
onChange(content);
197+
},
198+
}));
199+
186200
return (
187201
editor ? (
188202
<div
@@ -221,7 +235,7 @@ function RichTextEditor({
221235
</>
222236
)
223237
);
224-
}
238+
});
225239

226240
// eslint-disable-next-line import/no-default-export
227241
export default RichTextEditor;

0 commit comments

Comments
 (0)