Skip to content

Commit 0fb20e2

Browse files
Merge release/v3.2.0 into main branch (#1244)
* fixes class name redundancies (#1241) * Allow RichTextEditor to be read-only (#1243)
1 parent 2f78f8a commit 0fb20e2

File tree

5 files changed

+59
-25
lines changed

5 files changed

+59
-25
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": "3.1.3",
3+
"version": "3.2.0",
44
"dependencies": {
55
"@tiptap/core": "^2.3.1",
66
"@tiptap/extension-bold": "^2.3.1",

src/FlexContainer/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export function computeBreakpointClassNames(
55
props: Omit<FlexContainerProps, 'children' | 'className'>,
66
bp?: 'xs' | 'sm' | 'md' | 'lg'| 'xl' | 'xxl',
77
): string[] {
8-
if (bp && bp in props) {
8+
if (bp) {
99
const attributes = props[bp];
1010
return [
1111
styles[`${bp}_alignItems_${attributes?.alignItems}`],

src/RichTextEditor/RichTextEditor.test.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,17 @@ import { render, screen, waitFor } from '@testing-library/react';
22
import React from 'react';
33

44
import RichTextEditor, { type RichTextEditorProps } from './RichTextEditor';
5+
import { RichTextEditorDefaultActionsArray } from './richTextEditorActions';
56

67
describe('<RichTextEditor />', () => {
8+
const elements = {
9+
textbox: {
10+
find: () => screen.findByRole('textbox'),
11+
},
12+
allButtons: {
13+
findAll: () => screen.findAllByRole('button'),
14+
},
15+
};
716
const Setup = (overrides: Omit<RichTextEditorProps, 'id' | 'onChange'> = {}) => (
817
<RichTextEditor
918
id="some-id"
@@ -27,4 +36,20 @@ describe('<RichTextEditor />', () => {
2736
expect(await screen.findByText('hello world')).toBeInTheDocument();
2837
});
2938
});
39+
40+
describe('with prop editable set to false', () => {
41+
it('renders disabled editor', async () => {
42+
render(<Setup editable={false} initialValue="<p>hello world</p>" />);
43+
44+
expect(await screen.findByText('hello world')).toBeInTheDocument();
45+
const textbox = await elements.textbox.find();
46+
expect(textbox).toBeInTheDocument();
47+
48+
const buttons = await elements.allButtons.findAll();
49+
const disabledButtons = buttons.filter((button) => button.hasAttribute('disabled'));
50+
51+
expect(disabledButtons.length).toBe(RichTextEditorDefaultActionsArray.length);
52+
expect(textbox.firstChild).toHaveAttribute('contenteditable', 'false');
53+
});
54+
});
3055
});

src/RichTextEditor/RichTextEditor.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Extension, Node as TipTapNode, Mark } from '@tiptap/core';
44
import './RichTextEditor.scss';
55

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

1010
import classNames from 'classnames';
@@ -81,6 +81,7 @@ export type RichTextEditorProps = {
8181
https://tiptap.dev/docs/editor/guide/custom-extensions
8282
*/
8383
customExtensions?: (Extension | TipTapNode | Mark)[];
84+
editable?: boolean;
8485
hasErrors?: boolean;
8586
id: string;
8687
/**
@@ -105,6 +106,7 @@ const RichTextEditor = forwardRef((
105106
allowedTags,
106107
ariaAttributes,
107108
availableActions = RichTextEditorDefaultActionsArray,
109+
editable = true,
108110
characterLimit,
109111
className,
110112
hasErrors,
@@ -189,6 +191,7 @@ const RichTextEditor = forwardRef((
189191

190192
onChange(sanitizedHtml);
191193
},
194+
editable,
192195
});
193196

194197
useImperativeHandle(ref, () => ({
@@ -198,6 +201,12 @@ const RichTextEditor = forwardRef((
198201
},
199202
}));
200203

204+
useEffect(() => {
205+
if (editor) {
206+
editor.setEditable(editable);
207+
}
208+
}, [editor, editable]);
209+
201210
return (
202211
editor ? (
203212
<div
@@ -207,6 +216,7 @@ const RichTextEditor = forwardRef((
207216
{availableActions.length > 0 && (
208217
<RichTextEditorMenuBar
209218
availableActions={availableActions}
219+
editable={editable}
210220
editor={editor}
211221
/>
212222
)}

src/RichTextEditor/RichTextEditorMenuBar.tsx

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import type { Editor } from '@tiptap/core';
33
import './RichTextEditorMenuBar.scss';
44

55
import React from 'react';
6-
import * as propTypes from 'prop-types';
7-
86
import classNames from 'classnames';
97

108
import {
@@ -17,68 +15,74 @@ import {
1715
} from '@fortawesome/pro-regular-svg-icons';
1816
import IconButton from '../IconButton';
1917

20-
import { RichTextEditorActions, RichTextEditorAllActionsArray } from './richTextEditorActions';
18+
import { RichTextEditorActions } from './richTextEditorActions';
2119
import { createActionHandlers } from './actionHandlers';
2220

2321
type RichTextEditorMenuBarProps = {
2422
availableActions: typeof RichTextEditorActions[keyof typeof RichTextEditorActions][];
2523
editor: Editor;
24+
editable?: boolean
2625
}
2726

2827
function RichTextEditorMenuBar({
2928
availableActions,
3029
editor,
30+
editable = true,
3131
}: RichTextEditorMenuBarProps) {
3232
const actionHandlers = createActionHandlers(editor);
3333

3434
const actions = [
3535
{
3636
label: 'Bold',
3737
name: RichTextEditorActions.BOLD,
38-
disabled: availableActions.includes(RichTextEditorActions.BOLD) && !editor.can()
39-
.chain()
40-
.focus()
41-
.toggleBold()
42-
.run(),
38+
disabled: !editable || (
39+
availableActions.includes(RichTextEditorActions.BOLD) && !editor.can()
40+
.chain()
41+
.focus()
42+
.toggleBold()
43+
.run()
44+
),
4345
onClick: actionHandlers.bold,
4446
icon: faBold,
4547
},
4648
{
4749
label: 'Italic',
4850
name: RichTextEditorActions.ITALIC,
49-
disabled: availableActions.includes(RichTextEditorActions.ITALIC) && !editor.can()
50-
.chain()
51-
.focus()
52-
.toggleItalic()
53-
.run(),
51+
disabled: !editable || (
52+
availableActions.includes(RichTextEditorActions.ITALIC) && !editor.can()
53+
.chain()
54+
.focus()
55+
.toggleItalic()
56+
.run()
57+
),
5458
onClick: actionHandlers.italic,
5559
icon: faItalic,
5660
},
5761
{
5862
label: 'Link',
5963
name: RichTextEditorActions.LINK,
60-
disabled: false,
64+
disabled: !editable,
6165
onClick: actionHandlers.link,
6266
icon: faLink,
6367
},
6468
{
6569
label: 'Unlink',
6670
name: RichTextEditorActions.UNLINK,
67-
disabled: availableActions.includes(RichTextEditorActions.LINK) && !editor.isActive('link'),
71+
disabled: !editable || (availableActions.includes(RichTextEditorActions.LINK) && !editor.isActive('link')),
6872
onClick: actionHandlers.unlink,
6973
icon: faUnlink,
7074
},
7175
{
7276
label: 'Unordered List',
7377
name: RichTextEditorActions.UNORDERED_LIST,
74-
disabled: false,
78+
disabled: !editable,
7579
onClick: actionHandlers.unorderedList,
7680
icon: faListUl,
7781
},
7882
{
7983
label: 'Ordered List',
8084
name: RichTextEditorActions.ORDERED_LIST,
81-
disabled: false,
85+
disabled: !editable,
8286
onClick: actionHandlers.orderedList,
8387
icon: faListOl,
8488
},
@@ -104,10 +108,5 @@ function RichTextEditorMenuBar({
104108
);
105109
}
106110

107-
RichTextEditorMenuBar.propTypes = {
108-
availableActions: propTypes.arrayOf(propTypes.oneOf(RichTextEditorAllActionsArray)).isRequired,
109-
editor: propTypes.object.isRequired,
110-
};
111-
112111
// eslint-disable-next-line import/no-default-export
113112
export default RichTextEditorMenuBar;

0 commit comments

Comments
 (0)