0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-21 18:09:02 +01:00

Update to Draftail v2 alpha

This commit is contained in:
Thibaud Colas 2022-07-06 08:04:29 +01:00
parent 7286c530e9
commit d30eec40a8
14 changed files with 98 additions and 66 deletions

View File

@ -58,6 +58,12 @@ Changelog
* Migrate the dashboard (home) view header to the shared header template and update designs (Paarth Agarwal)
* Update classes and styles for the shared header templates to align with UI guidelines (Paarth Agarwal)
* Clean up multiple eslint rules usage and configs to align better with the Wagtail coding guidelines (LB (Ben Johnston))
* Add a live-updating character count to the Draftail rich text editor (Thibaud Colas)
* Add rich text editor paste to auto-create links (Thibaud Colas)
* Add rich text editor text shortcuts undo, to allow typing text normally detected as a shortcut (Thibaud Colas)
* Add support for right-to-left (RTL) languages to the rich text editor (Thibaud Colas)
* Change rich text editor placeholder to follow the users focus on empty blocks (Thibaud Colas)
* Add rich text editor empty block highlight by showing their block type (Thibaud Colas)
* Fix: Typo in `ResumeWorkflowActionFormatter` message (Stefan Hammer)
* Fix: Throw a meaningful error when saving an image to an unrecognised image format (Christian Franke)
* Fix: Remove extra padding for headers with breadcrumbs on mobile viewport (Steven Steinwand)

View File

@ -2,7 +2,7 @@ import React, { ReactNode } from 'react';
import { Provider } from 'react-redux';
import { mount } from 'enzyme';
import { createEditorStateFromRaw } from 'draftail';
import { EditorState, SelectionState } from 'draft-js';
import { DraftInlineStyleType, EditorState, SelectionState } from 'draft-js';
import { CommentApp } from '../../CommentApp/main';
import { updateGlobalSettings } from '../../CommentApp/actions/settings';
@ -42,7 +42,7 @@ describe('CommentableEditor', () => {
{
offset: 0,
length: 1,
style: 'COMMENT-1',
style: 'COMMENT-1' as DraftInlineStyleType,
},
],
text: 'test',
@ -61,12 +61,12 @@ describe('CommentableEditor', () => {
{
offset: 0,
length: 10,
style: 'COMMENT-2',
style: 'COMMENT-2' as DraftInlineStyleType,
},
{
offset: 0,
length: 20,
style: 'COMMENT-1',
style: 'COMMENT-1' as DraftInlineStyleType,
},
],
text: 'test_test_test_test_test_test_test',
@ -85,12 +85,12 @@ describe('CommentableEditor', () => {
{
offset: 21,
length: 4,
style: 'COMMENT-2',
style: 'COMMENT-2' as DraftInlineStyleType,
},
{
offset: 0,
length: 20,
style: 'COMMENT-1',
style: 'COMMENT-1' as DraftInlineStyleType,
},
],
text: 'test_test_test_test_test_test_test',

View File

@ -3,6 +3,9 @@ import {
ToolbarButton,
createEditorStateFromRaw,
serialiseEditorStateToRaw,
InlineStyleControl,
ControlComponentProps,
DraftailEditorProps,
} from 'draftail';
import {
CharacterMetadata,
@ -21,7 +24,6 @@ import { filterInlineStyles } from 'draftjs-filters';
import React, {
MutableRefObject,
ReactNode,
ReactText,
useEffect,
useMemo,
useRef,
@ -266,12 +268,6 @@ function createFromBlockArrayOrPlaceholder(blockArray: ContentBlock[]) {
return ContentState.createFromText(' ');
}
interface ControlProps {
getEditorState: () => EditorState;
// eslint-disable-next-line react/no-unused-prop-types
onChange: (editorState: EditorState) => void;
}
export function splitState(editorState: EditorState) {
const selection = editorState.getSelection();
const anchorKey = selection.getAnchorKey();
@ -345,7 +341,7 @@ export function getSplitControl(
</button>
);
}
return ({ getEditorState }: ControlProps) => (
return ({ getEditorState }: ControlComponentProps) => (
<ToolbarButton
name={name}
active={false}
@ -370,7 +366,7 @@ function getCommentControl(
contentPath: string,
fieldNode: Element,
) {
return ({ getEditorState, onChange }: ControlProps) => (
return ({ getEditorState, onChange }: ControlComponentProps) => (
<span className="Draftail-CommentControl" data-comment-add>
<ToolbarButton
name="comment"
@ -666,14 +662,6 @@ function handleArrowAtContentEnd(
);
}
interface InlineStyle {
label?: string;
description?: string;
icon?: string | string[] | Node;
type: string;
style?: Record<string, string | number | ReactText | undefined>;
}
interface ColorConfigProp {
standardHighlight: string;
overlappingHighlight: string;
@ -685,14 +673,14 @@ interface CommentableEditorProps {
fieldNode: Element;
contentPath: string;
rawContentState: RawDraftContentState;
onSave: (rawContent: RawDraftContentState) => void;
inlineStyles: Array<InlineStyle>;
onSave: (rawContent: RawDraftContentState | null) => void;
inlineStyles: InlineStyleControl[];
editorRef: (editor: ReactNode) => void;
colorConfig: ColorConfigProp;
isCommentShortcut: (e: React.KeyboardEvent) => boolean;
// Unfortunately the EditorPlugin type isn't exported in our version of 'draft-js-plugins-editor'
plugins?: Record<string, unknown>[];
controls?: Array<(props: ControlProps) => JSX.Element>;
controls?: DraftailEditorProps['controls'];
}
function CommentableEditor({
@ -733,7 +721,7 @@ function CommentableEditor({
[comments],
);
const commentStyles: Array<InlineStyle> = useMemo(
const commentStyles: InlineStyleControl[] = useMemo(
() =>
ids.map((id) => ({
type: `${COMMENT_STYLE_IDENTIFIER}${id}`,
@ -863,7 +851,9 @@ function CommentableEditor({
setEditorState(newEditorState);
}}
editorState={editorState}
controls={enabled ? controls.concat([CommentControl]) : controls}
controls={
enabled ? controls.concat([{ block: CommentControl }]) : controls
}
inlineStyles={inlineStyles.concat(commentStyles)}
plugins={plugins.concat([
{

View File

@ -13,7 +13,7 @@ $draftail-toolbar-icon-size: 1em;
$draftail-editor-font-family: $font-sans;
@import '../../../../node_modules/draft-js/dist/Draft';
@import '../../../../node_modules/draftail/lib/index';
@import '../../../../node_modules/draftail/src/index';
@import './Tooltip/Tooltip';
@import './CommentableEditor/CommentableEditor';

View File

@ -3,11 +3,18 @@
exports[`Draftail #initEditor options 1`] = `
Object {
"ariaDescribedBy": null,
"ariaExpanded": null,
"ariaLabel": null,
"ariaLabelledBy": null,
"ariaOwneeID": null,
"ariaRequired": null,
"autoCapitalize": null,
"autoComplete": null,
"autoCorrect": null,
"blockTypes": Array [],
"bottomToolbar": null,
"commandToolbar": [Function],
"commands": false,
"controls": Array [],
"decorators": Array [],
"editorState": null,
@ -27,6 +34,7 @@ Object {
],
"inlineStyles": Array [],
"maxListNesting": 4,
"multiline": true,
"onBlur": null,
"onChange": null,
"onFocus": null,

View File

@ -15,7 +15,6 @@ exports[`MediaBlock no data 1`] = `
<Icon
className="MediaBlock__icon"
icon="#icon-test"
title={null}
/>
</span>
<img
@ -62,7 +61,6 @@ exports[`MediaBlock renders 1`] = `
<Icon
className="MediaBlock__icon"
icon="#icon-test"
title={null}
/>
</span>
<img

View File

@ -11,7 +11,6 @@ exports[`TooltipEntity #openTooltip 1`] = `
<Icon
className="TooltipEntity__icon"
icon="#icon-test"
title={null}
/>
test
<Portal
@ -84,7 +83,6 @@ exports[`TooltipEntity works 1`] = `
<Icon
className="TooltipEntity__icon"
icon="#icon-test"
title={null}
/>
test
</a>

View File

@ -241,11 +241,13 @@ class DraftailRichTextArea {
...currentOptions,
controls: [
...(originalOptions || []),
// eslint-disable-next-line no-undef
draftail.getSplitControl(
newCapability.fn,
!!newCapability.enabled,
),
{
// eslint-disable-next-line no-undef
block: draftail.getSplitControl(
newCapability.fn,
!!newCapability.enabled,
),
},
],
});
}

View File

@ -416,7 +416,7 @@ describe('telepath: wagtail.widgets.DraftailRichTextArea', () => {
icon: 'link',
description: 'Link',
attributes: ['url', 'id', 'parentId'],
whitelist: {
allowlist: {
href: '^(http:|https:|undefined$)',
},
},
@ -427,7 +427,7 @@ describe('telepath: wagtail.widgets.DraftailRichTextArea', () => {
icon: 'image',
description: 'Image',
attributes: ['id', 'src', 'alt', 'format'],
whitelist: {
allowlist: {
id: true,
},
},

View File

@ -13,6 +13,17 @@ depth: 1
When using a queryset to render a list of images, you can now use the `prefetch_renditions()` queryset method to prefetch the renditions needed for rendering with a single extra query, similar to `prefetch_related`. If you have many renditions per image, you can also call it with filters as arguments - `prefetch_renditions("fill-700x586", "min-600x400")` - to fetch only the renditions you intend on using for a smaller query. For long lists of images, this can provide a significant boost to performance. See [](prefetching_image_renditions) for more examples. This feature was developed by Tidiane Dia and Karl Hobley.
### Rich text improvements
As part of the page editor redesign project sponsored by Google, we have made a number of improvements to our rich text editor:
* Character count: The character count is displayed underneath the editor, live-updating as you type. This counts the length of the text, not of any formatting.
* Paste to auto-create links: To add a link from your copy-paste clipboard, select text and paste the URL.
* Text shortcuts undo: The editor normally converts text starting with `1. ` to a list item. Its now possible to un-do this change and keep the text as-is. This works for all Markdown-style shortcuts.
* RTL support: The editors UI now displays correctly in right-to-left languages.
* Focus-aware placeholder: The editors placeholder text will now follow the users focus, to make it easier to understand where to type in long fields.
* Empty heading highlight: The editor now highlights empty headings and list items by showing their type (“Heading 3”) as a placeholder, so content is less likely to be published with empty headings.
### Other features
* Add clarity to confirmation when being asked to convert an external link to an internal one (Thijs Kramer)

65
package-lock.json generated
View File

@ -11,7 +11,7 @@
"@tippyjs/react": "^4.2.6",
"a11y-dialog": "^7.4.0",
"draft-js": "^0.10.5",
"draftail": "^1.4.1",
"draftail": "^2.0.0-alpha.1",
"draftjs-filters": "^2.5.0",
"focus-trap-react": "^8.4.2",
"immer": "^9.0.6",
@ -16420,7 +16420,6 @@
},
"node_modules/compute-scroll-into-view": {
"version": "1.0.17",
"dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
@ -17831,7 +17830,6 @@
},
"node_modules/downshift": {
"version": "6.1.7",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.14.8",
@ -17874,26 +17872,37 @@
}
},
"node_modules/draftail": {
"version": "1.4.1",
"license": "MIT",
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/draftail/-/draftail-2.0.0-alpha.1.tgz",
"integrity": "sha512-D6IEHS3KBFgnPIy4Et+O2SU8hbpZ+jdLyVCVXD7uifavK5c4ZFQ/w/zkVw7/8HJoMTyUIHNNSOHmdbCPnogQRg==",
"dependencies": {
"@tippyjs/react": "^4.2.6",
"decorate-component-with-props": "^1.0.2",
"downshift": "^6.1.7",
"draft-js-plugins-editor": "^2.1.1",
"draftjs-conductor": "^1.0.0",
"draftjs-filters": "^2.2.3"
"draftjs-conductor": "^3.0.0",
"draftjs-filters": "^3.0.1"
},
"peerDependencies": {
"draft-js": "^0.10.5",
"react": "^16.0.0",
"react-dom": "^16.0.0"
"react": "^16.6.0",
"react-dom": "^16.6.0"
}
},
"node_modules/draftail/node_modules/draftjs-filters": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/draftjs-filters/-/draftjs-filters-3.0.1.tgz",
"integrity": "sha512-sms/ACV6w/dc/mRkmrBw6o0+lxE8SevRg+5k31Y/FJIYf+ALVKGltcVixEwh5XSYVgddjHkMJoC4hmx1m6Fcxg==",
"peerDependencies": {
"draft-js": "^0.10.4 || ^0.11.0 || ^0.12.0"
}
},
"node_modules/draftjs-conductor": {
"version": "1.2.0",
"license": "MIT",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/draftjs-conductor/-/draftjs-conductor-3.0.0.tgz",
"integrity": "sha512-OjFRwOT41IVKUvdOC/jnLl9CYMP8mVbonVh5MS9Ud2LGOhllWFFFqz90uc9vUAh3DPMHe5iMFGSqXUSW6a5PFw==",
"peerDependencies": {
"draft-js": "^0.10.5 || ^0.11.0",
"react": "^16.0.0"
"draft-js": "^0.10.4 || ^0.11.0 || ^0.12.0"
}
},
"node_modules/draftjs-filters": {
@ -29932,7 +29941,6 @@
},
"node_modules/tslib": {
"version": "2.3.1",
"dev": true,
"license": "0BSD"
},
"node_modules/tsutils": {
@ -42796,8 +42804,7 @@
}
},
"compute-scroll-into-view": {
"version": "1.0.17",
"dev": true
"version": "1.0.17"
},
"concat-map": {
"version": "0.0.1",
@ -43767,7 +43774,6 @@
},
"downshift": {
"version": "6.1.7",
"dev": true,
"requires": {
"@babel/runtime": "^7.14.8",
"compute-scroll-into-view": "^1.0.17",
@ -43795,16 +43801,30 @@
}
},
"draftail": {
"version": "1.4.1",
"version": "2.0.0-alpha.1",
"resolved": "https://registry.npmjs.org/draftail/-/draftail-2.0.0-alpha.1.tgz",
"integrity": "sha512-D6IEHS3KBFgnPIy4Et+O2SU8hbpZ+jdLyVCVXD7uifavK5c4ZFQ/w/zkVw7/8HJoMTyUIHNNSOHmdbCPnogQRg==",
"requires": {
"@tippyjs/react": "^4.2.6",
"decorate-component-with-props": "^1.0.2",
"downshift": "^6.1.7",
"draft-js-plugins-editor": "^2.1.1",
"draftjs-conductor": "^1.0.0",
"draftjs-filters": "^2.2.3"
"draftjs-conductor": "^3.0.0",
"draftjs-filters": "^3.0.1"
},
"dependencies": {
"draftjs-filters": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/draftjs-filters/-/draftjs-filters-3.0.1.tgz",
"integrity": "sha512-sms/ACV6w/dc/mRkmrBw6o0+lxE8SevRg+5k31Y/FJIYf+ALVKGltcVixEwh5XSYVgddjHkMJoC4hmx1m6Fcxg==",
"requires": {}
}
}
},
"draftjs-conductor": {
"version": "1.2.0",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/draftjs-conductor/-/draftjs-conductor-3.0.0.tgz",
"integrity": "sha512-OjFRwOT41IVKUvdOC/jnLl9CYMP8mVbonVh5MS9Ud2LGOhllWFFFqz90uc9vUAh3DPMHe5iMFGSqXUSW6a5PFw==",
"requires": {}
},
"draftjs-filters": {
@ -51673,8 +51693,7 @@
}
},
"tslib": {
"version": "2.3.1",
"dev": true
"version": "2.3.1"
},
"tsutils": {
"version": "3.21.0",

View File

@ -101,7 +101,7 @@
"@tippyjs/react": "^4.2.6",
"a11y-dialog": "^7.4.0",
"draft-js": "^0.10.5",
"draftail": "^1.4.1",
"draftail": "^2.0.0-alpha.1",
"draftjs-filters": "^2.5.0",
"focus-trap-react": "^8.4.2",
"immer": "^9.0.6",

View File

@ -733,7 +733,7 @@ def register_core_features(features):
# We want to enforce constraints on which links can be pasted into rich text.
# Keep only the attributes Wagtail needs.
"attributes": ["url", "id", "parentId"],
"whitelist": {
"allowlist": {
# Keep pasted links with http/https protocol, and not-pasted links (href = undefined).
"href": "^(http:|https:|undefined$)",
},

View File

@ -94,7 +94,7 @@ def register_image_feature(features):
# Keep only the attributes Wagtail needs.
"attributes": ["id", "src", "alt", "format"],
# Keep only images which are from Wagtail.
"whitelist": {
"allowlist": {
"id": True,
},
},