Closed sgober closed 1 year ago
@sgober In order to use extensions with Tiptap, you have to (1) install the packages you need (e.g. @tiptap/extension-color
) and (2) include the extension in your extensions
array when instantiating your editor (e.g., extensions: [StarterKit, Color]
).
So rather than just including StarterKit
(which bundles many common Tiptap extensions, but not all), you'd need to also pass in explicitly the other extensions you want to utilize as well.
const editor = useEditor({
extensions: [StarterKit, Color, Underline], // And all others you want to include!
content: data,
onUpdate({ editor }) {
handleChange(path, editor.getHTML());
}
});
For instance, in the demo example in mui-tiptap (essentially the same as what's in the CodeSandbox linked from the README), you can check out the full list of extensions it uses here:
Apologies that this wasn't clearer in the README. I've updated the Choosing your extensions section to hopefully help folks avoid these sorts of confusing errors in the future.
This was SUPER helpful, THANK YOU!
I pretty much got everything working except for the TextAlign stuff.
This is in my extensions declarations:
TextAlign.configure({
types: ['heading', 'paragraph', 'image']
})
With this imported: import { TextAlign } from '@tiptap/extension-text-align';
All I get is a disabled dropdown (see screenshot). Is there something I'm missing?
Also, would it be possible to add a aria-label to the inputs like font family and font size? There currently is one, but it's not on the input so there is a missing label error from an accessibility standpoint
@sgober Glad to hear that! The MenuSelectTextAlign
component will show as disabled if the editor itself is not editable (likely not the case for you) or if the Tiptap editor can't set text-alignment for the current selected content in the editor. You can see the logic here (note that enabledAlignments
will be left, center, right, and justify by default per Tiptap TextAlign
default options):
So for instance, if your cursor/caret is currently inside a code block (not one of the types
you configured in TextAlign.configure({ types: [...] })
, it will show the text align select as disabled, since you cannot align a code block. But if you put your cursor in a regular text paragraph or click to select an image, the text-align select controls should be enabled. This is the behavior in the CodeSandbox demo linked from the README for instance: https://codesandbox.io/p/sandbox/mui-tiptap-demo-3zl2l6
Try adding some text and highlighting it in your example.
Unfortunately still no luck...
https://github.com/sjdemartini/mui-tiptap/assets/35042815/b9751347-05f6-4641-b0bb-5343b33b71fe
Also, would it be possible to add a aria-label to the inputs like font family and font size? There currently is one, but it's not on the input so there is a missing label error from an accessibility standpoint
I've added a new issue to track that here https://github.com/sjdemartini/mui-tiptap/issues/175. The input
itself has aria-hidden="true"
, so I don't think it needs the aria-label
(though you could provide one via inputProps
similar to https://github.com/mui/material-ui/issues/25697#issuecomment-1121041190 if you like, since inputProps
provided to MenuSelect*
components get passed onto <Select />
). If you know more about how the a11y should be handled, please follow up on that issue!
Unfortunately still no luck...
Hm, I'm not sure what's going on. Can you create a CodeSandbox that reproduces the problem? I can definitely take a look that way. Please create a new issue for that if so!
Or can you tell what you're doing differently compared to the CodeSandbox linked from the mui-tiptap README?
I'm having a ton of issues with code sandbox at the moment where I can't even load yours to be able to for it. This is essentially what I have and matches what you're doing in the code sandbox, but this is essentially what I have. It's used within json forms you, you might have to ignore some
import React from 'react';
import PropTypes from 'prop-types';
import { and, isDescriptionHidden, isStringControl, optionIs, rankWith, showAsRequired } from '@jsonforms/core';
import { withJsonFormsControlProps } from '@jsonforms/react';
import { FormHelperText, Hidden, InputLabel, useTheme } from '@mui/material';
import { useFocus } from '@jsonforms/material-renderers';
import { useEditor } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';
import { Color } from '@tiptap/extension-color';
import { FontFamily } from '@tiptap/extension-font-family';
import { Highlight } from '@tiptap/extension-highlight';
import { Link } from '@tiptap/extension-link';
import { Subscript } from '@tiptap/extension-subscript';
import { Superscript } from '@tiptap/extension-superscript';
import { TableCell } from '@tiptap/extension-table-cell';
import { TableHeader } from '@tiptap/extension-table-header';
import { TableRow } from '@tiptap/extension-table-row';
import { TaskItem } from '@tiptap/extension-task-item';
import { TaskList } from '@tiptap/extension-task-list';
import { TextAlign } from '@tiptap/extension-text-align';
import { TextStyle } from '@tiptap/extension-text-style';
import { Underline } from '@tiptap/extension-underline';
import {
FontSize,
MenuButtonAddTable,
MenuButtonBlockquote,
MenuButtonBold,
MenuButtonBulletedList,
MenuButtonCode,
MenuButtonCodeBlock,
MenuButtonEditLink,
MenuButtonHighlightColor,
MenuButtonHorizontalRule,
MenuButtonItalic,
MenuButtonOrderedList,
MenuButtonRedo,
MenuButtonRemoveFormatting,
MenuButtonSubscript,
MenuButtonSuperscript,
MenuButtonStrikethrough,
MenuButtonTaskList,
MenuButtonTextColor,
MenuButtonUnderline,
MenuButtonUndo,
MenuControlsContainer,
MenuDivider,
MenuSelectFontFamily,
MenuSelectFontSize,
MenuSelectHeading,
MenuSelectTextAlign,
LinkBubbleMenu,
LinkBubbleMenuHandler,
RichTextField,
RichTextEditorProvider,
TableImproved
} from 'mui-tiptap';
import _ from 'lodash';
export const RichTextRenderer = props => {
const theme = useTheme();
const [focused] = useFocus();
const { config, data, description, errors, handleChange, label, path, required, uischema, visible } = props;
const isValid = errors.length === 0;
const appliedUiSchemaOptions = _.merge({}, config, uischema.options);
const showDescription = !isDescriptionHidden(
visible,
description,
focused,
appliedUiSchemaOptions.showUnfocusedDescription
);
const firstFormHelperText = showDescription ? description : !isValid ? errors : null;
const secondFormHelperText = showDescription && !isValid ? errors : null;
const editor = useEditor({
extensions: [
StarterKit,
Color,
FontFamily,
FontSize,
Highlight,
Link,
LinkBubbleMenuHandler,
Subscript.extend({
excludes: 'superscript'
}),
Superscript.extend({
excludes: 'subscript'
}),
TableCell,
TableHeader,
TableImproved.configure({
resizable: true
}),
TableRow,
TaskItem.configure({
nested: true
}),
TaskList,
TextAlign.configure({
types: ['heading', 'paragraph', 'image']
}),
TextStyle,
Underline
],
content: data,
onUpdate({ editor }) {
handleChange(path, editor.getHTML());
}
});
const fontOptions = [
{ label: 'Roboto', value: 'Roboto' },
{ label: 'Roboto Condensed', value: 'Roboto Condensed' },
{ label: 'Barlow', value: 'Barlow' },
{ label: 'Barlow Condensed', value: 'Barlow Condensed' }
];
const textColorOptions = [
{ value: '#000000', label: 'Black' },
{ value: '#ffffff', label: 'White' },
{ value: '#888888', label: 'Grey' },
{ value: theme.palette.error.main, label: 'Red' },
{ value: theme.palette.warning.main, label: 'Orange' },
{ value: '#ffff00', label: 'Yellow' },
{ value: theme.palette.success.main, label: 'Green' },
{ value: theme.palette.primary.main, label: 'Blue' }
];
const highlightColorOptions = [
{ value: '#d6d6d6', label: 'Light gray' },
{ value: theme.palette.error.background, label: 'Light red' },
{ value: theme.palette.warning.background, label: 'Light orange' },
{ value: '#ffffb1', label: 'Light yellow' },
{ value: theme.palette.success.background, label: 'Light green' },
{ value: theme.palette.primary.background, label: 'Light blue' }, // need to be updated in theme
{ value: theme.palette.secondary.background, label: 'Light blue' }, // need to be updated in theme
{ value: '#dcd7f3', label: 'Light purple' }
];
return (
<Hidden xsUp={!visible}>
<InputLabel
component="legend"
error={!isValid}
required={showAsRequired(required, appliedUiSchemaOptions.hideRequiredAsterisk)}>
{label}
</InputLabel>
<div className="rich-text">
<RichTextEditorProvider editor={editor}>
<RichTextField
controls={
<MenuControlsContainer>
<MenuSelectFontFamily options={fontOptions} />
<MenuDivider />
<MenuSelectHeading />
<MenuDivider />
<MenuSelectFontSize />
<MenuDivider />
<MenuButtonBold aria-label="Rich Text Input Bold" />
<MenuButtonItalic aria-label="Rich Text Input Italic" />
<MenuButtonUnderline aria-label="Rich Text Input Underline" />
<MenuButtonStrikethrough aria-label="Rich Text Input Strikethrough" />
<MenuButtonSubscript aria-label="Rich Text Input Subscript" />
<MenuButtonSuperscript aria-label="Rich Text Input Superscript" />
<MenuDivider />
<MenuButtonTextColor
aria-label="Rich Text Input Text Color"
defaultTextColor="#000"
swatchColors={textColorOptions}
/>
<MenuButtonHighlightColor
aria-label="Rich Text Input Highlight Color"
swatchColors={highlightColorOptions}
/>
<MenuDivider />
<MenuButtonEditLink aria-label="Rich Text Input Link" />
<MenuDivider />
{/* TODO: figure out how this works */}
<MenuSelectTextAlign />
<MenuDivider />
<MenuButtonOrderedList aria-label="Rich Text Input Ordered List" />
<MenuButtonBulletedList aria-label="Rich Text Input Bullet List" />
<MenuButtonTaskList aria-label="Rich Text Input Task List" />
<MenuDivider />
<MenuButtonBlockquote aria-label="Rich Text Input Block Quote" />
<MenuDivider />
<MenuButtonCode aria-label="Rich Text Input Code" />
<MenuButtonCodeBlock aria-label="Rich Text Input Code Block" />
<MenuDivider />
<MenuButtonHorizontalRule aria-label="Rich Text Input Horizontal Rule" />
<MenuButtonAddTable aria-label="Rich Text Input Add Table" />
<MenuDivider />
<MenuButtonRemoveFormatting aria-label="Rich Text Input Remove Formatting" />
<MenuDivider />
<MenuButtonUndo aria-label="Rich Text Input Undo" />
<MenuButtonRedo aria-label="Rich Text Input Redo" />
</MenuControlsContainer>
}
/>
<LinkBubbleMenu />
</RichTextEditorProvider>
</div>
<FormHelperText error={!isValid && !showDescription} sx={{ margin: '3px 0 0' }}>
{firstFormHelperText}
</FormHelperText>
<FormHelperText error={!isValid} sx={{ margin: '3px 0 0' }}>
{secondFormHelperText}
</FormHelperText>
</Hidden>
);
};
It very much seems like I could be missing something small in the setup. All of the tiptap extensions are version 2.1.12
@sgober Thanks, I was able to copy that locally (and remove the jsonforms stuff) and reproduce the above issue with MenuSelectTextAlign
showing as disabled.
tl;dr: It seems this can be fixed by changing types: ['heading', 'paragraph', 'image']
to types: ['heading', 'paragraph']
, since that array should only contain extension types that are part of the included Tiptap editor extensions
, and your example is not including Image
/ResizableImage
.
I didn't realize this was the behavior until testing your example. But if there are any "unknown" types in that list, the editor.can().setTextAlign()
method (used by MenuSelectTextAlign
under the hood) will always return false, since it checks to see if "every" type can receive the update of text align under the hood here. Without the Image
(or ResizableImage
) extension being passed into extensions
for useEditor
, Tiptap doesn't recognize the "image"
type so returns false for commands.updateAttributes("image", {textAlign})
.
Let me know if that resolves the problem on your end!
@sjdemartini this did the trick, THANK YOU! I appreciate you taking the time to figure out what was wrong.
One other quick question for you, I'm seeing the indent/unindent buttons disabled (if you comment the the isTouchDevice
conditional out in the code sandbox you can recreate). Is there something missing for this to work? I'm also not able to get shift+tab to work
@sgober Great! The indent and unindent actions/buttons are specifically for lists (bullet, number, task). Also note that you can only indent something if there is a list item above it that is part of the same list and is at the same indentation level as it. You could also look at Tiptap's demo here to see the behavior: https://tiptap.dev/api/nodes/bullet-list. Hope that helps.
Got it - doesn't fully serve my purposes so will probably leave it out for now. Thanks again for all your help!
Describe the bug
I am unable to use like half of the
mui-tiptap
components as they fail with errors likesetColor
ortoggleUnderline
are not functions (see screenshot below)To Reproduce
Steps to reproduce the behavior:
This is what I am trying to use with json forms:
With editor defined as follows:
Expected behavior
Every
mui-tiptap
component above that is commented out results in an error similar to the one I described above. It is odd that some of them work and some of them don'tScreenshots
What is able to work:
Error when I try to use
MenuButtonUnderline
:All other error are similar, not sure if this is a
tiptap
error or something specific tomui-tiptap
System (please complete the following information):
Additional context
Add any other context about the problem here.