Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A11y improvment (part 2) : Customize textarea props #553

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/content-styling.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ position: 1

# Content styling

The MDXEditor component exposes a property called `contentEditableClassName` that you can use to style the content of the editor. This is useful if you want to use a different font family, or change the contents of the various blocks inside.
The MDXEditor component exposes a property called `contentEditableProps` (containing a property `className`) that you can use to style the content of the editor. This is useful if you want to use a different font family, or change the contents of the various blocks inside.

For best results, ensure that you style the editor using the same CSS classes that you use in your application.

Expand All @@ -23,7 +23,7 @@ For best results, ensure that you style the editor using the same CSS classes th
```tsx
<MDXEditor
markdown="Hello **world**!"
contentEditableClassName="prose"
contentEditableProps={{ className: "prose" }}
/>
```

Expand Down
24 changes: 18 additions & 6 deletions src/MDXEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RealmPlugin, RealmWithPlugins } from './RealmWithPlugins'
import {
Translation,
composerChildren$,
contentEditableClassName$,
contentEditableProps$,
corePlugin,
editorRootElementRef$,
editorWrappers$,
Expand All @@ -19,7 +19,7 @@ import {
viewMode$
} from './plugins/core'

import { ContentEditable } from '@lexical/react/LexicalContentEditable.js'
import { ContentEditable, Props as ContentEditableProps } from '@lexical/react/LexicalContentEditable.js'
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary.js'
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin.js'
import classNames from 'classnames'
Expand All @@ -44,13 +44,17 @@ const LexicalProvider: React.FC<{

const RichTextEditor: React.FC = () => {
const t = useTranslation()
const [contentEditableClassName, composerChildren, topAreaChildren, editorWrappers, placeholder] = useCellValues(
const [contentEditableClassName, contentEditableProps, composerChildren, topAreaChildren, editorWrappers, placeholder] = useCellValues(
contentEditableClassName$,
contentEditableProps$,
composerChildren$,
topAreaChildren$,
editorWrappers$,
placeholder$
)

const { className: contentEditablePropsClassName, ...otherContentEditableProps} = contentEditableProps

return (
<>
{topAreaChildren.map((Child, index) => (
Expand All @@ -61,12 +65,13 @@ const RichTextEditor: React.FC = () => {
<RichTextPlugin
contentEditable={
<ContentEditable
className={classNames(styles.contentEditable, contentEditableClassName)}
className={classNames(styles.contentEditable, contentEditableClassName, contentEditablePropsClassName)}
ariaLabel={t('contentArea.editableMarkdown', 'editable markdown')}
{ ...otherContentEditableProps }
/>
}
placeholder={
<div className={classNames(styles.contentEditable, styles.placeholder, contentEditableClassName)}>
<div className={classNames(styles.contentEditable, styles.placeholder, contentEditableClassName, contentEditablePropsClassName)}>
<p>{placeholder}</p>
</div>
}
Expand Down Expand Up @@ -220,8 +225,14 @@ export interface MDXEditorProps {
/**
* the CSS class to apply to the content editable element of the editor.
* Use this to style the various content elements like lists and blockquotes.
* @deprecated Will be removed in further version. Use contentEditableProps.className instead
*/
contentEditableClassName?: string
/**
* the props to apply to the content editable element of the editor.
* You can use this to style the various content elements like lists and blockquotes, to identify the textbox, etc.
*/
contentEditableProps?: ContentEditableProps
/**
* The markdown to edit. Notice that this is read only when the component is mounted.
* To change the component content dynamically, use the `MDXEditorMethods.setMarkdown` method.
Expand All @@ -247,7 +258,7 @@ export interface MDXEditorProps {
plugins?: RealmPlugin[]
/**
* The class name to apply to the root component element. Use this if you want to change the editor dimensions, maximum height, etc.
* For a content-specific styling, Use `contentEditableClassName` property.
* For a content-specific styling, Use `contentEditableProps.className` property.
*/
className?: string
/**
Expand Down Expand Up @@ -291,6 +302,7 @@ export const MDXEditor = React.forwardRef<MDXEditorMethods, MDXEditorProps>((pro
plugins={[
corePlugin({
contentEditableClassName: props.contentEditableClassName ?? '',
contentEditableProps: props.contentEditableProps ?? {},
initialMarkdown: props.markdown,
onChange: props.onChange ?? noop,
onBlur: props.onBlur ?? noop,
Expand Down
70 changes: 70 additions & 0 deletions src/examples/a11y.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from 'react'
import {MDXEditor, Separator, toolbarPlugin, UndoRedo} from '../'

export function AssociateLabelForTextBox() {
return (
<form>
<label id="mdxEditor-label">Form label associated to the textbox</label>
<MDXEditor
markdown={'MDXEditor has a default `aria-label` (in locale files) so we must use the `aria-labelledBy` prop to link the textbox to the label (cannot use the default `htmlFor` behaviour)'}
contentEditableProps={{
ariaLabelledBy: "mdxEditor-label"
}}
plugins={[
toolbarPlugin({
toolbarContents: () => (
<>
<UndoRedo />
<Separator />
</>
)
}),
]}
/>
</form>
)
}

export function CustomizeAriaLabel() {
return (
<MDXEditor
markdown={'Set an `aria-label` in the props'}
contentEditableProps={{
ariaLabel: "My custom label"
}}
plugins={[
toolbarPlugin({
toolbarContents: () => (
<>
<UndoRedo />
<Separator />
</>
)
}),
]}
/>
)
}

export function MarkTextboxToRequired() {
return (
<MDXEditor
markdown={'Set the `required` and the `aria-required` props for the field to be identified as "required" '}
contentEditableProps={{
required: true,
ariaRequired: true
}}
plugins={[
toolbarPlugin({
toolbarContents: () => (
<>
<UndoRedo />
<Separator />
</>
)
}),
]}
/>
)
}

11 changes: 11 additions & 0 deletions src/plugins/core/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { realmPlugin } from '../../RealmWithPlugins'
import { createEmptyHistoryState } from '@lexical/react/LexicalHistoryPlugin.js'
import { $isHeadingNode, HeadingTagType } from '@lexical/rich-text'
import { Props as ContentEditableProps } from '@lexical/react/LexicalContentEditable'
import { $setBlocksType } from '@lexical/selection'
import { $findMatchingParent, $insertNodeToNearestRoot, $wrapNodeInElement } from '@lexical/utils'
import { Cell, NodeRef, Realm, Signal, filter, map, scan, useCellValue, withLatestFrom } from '@mdxeditor/gurx'
Expand Down Expand Up @@ -114,9 +115,16 @@ export const activeEditor$ = Cell<LexicalEditor | null>(null)
/**
* Holds the CSS class name of the content editable element.
* @group Core
* @deprecated Will be removed in further version. Use contentEditableProps.className instead
*/
export const contentEditableClassName$ = Cell('')

/**
* Holds the props to pass to the content editable element.
* @group Core
*/
export const contentEditableProps$ = Cell<ContentEditableProps>({})

/**
* Holds the readOnly state of the editor.
* @group Core
Expand Down Expand Up @@ -840,6 +848,7 @@ export const translation$ = Cell<Translation>(() => {
export const corePlugin = realmPlugin<{
initialMarkdown: string
contentEditableClassName: string
contentEditableProps: ContentEditableProps
placeholder?: React.ReactNode
autoFocus: boolean | { defaultSelection?: 'rootStart' | 'rootEnd'; preventScroll?: boolean | undefined }
onChange: (markdown: string) => void
Expand Down Expand Up @@ -870,6 +879,7 @@ export const corePlugin = realmPlugin<{

[addComposerChild$]: SharedHistoryPlugin,
[contentEditableClassName$]: params?.contentEditableClassName,
[contentEditableProps$]: params?.contentEditableProps,
[toMarkdownOptions$]: params?.toMarkdownOptions,
[autoFocus$]: params?.autoFocus,
[placeholder$]: params?.placeholder,
Expand Down Expand Up @@ -933,6 +943,7 @@ export const corePlugin = realmPlugin<{
update(realm, params) {
realm.pubIn({
[contentEditableClassName$]: params?.contentEditableClassName,
[contentEditableProps$]: params?.contentEditableProps,
[toMarkdownOptions$]: params?.toMarkdownOptions,
[autoFocus$]: params?.autoFocus,
[placeholder$]: params?.placeholder,
Expand Down