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

Add support for using Draftail as a controlled component (#180) #207

Merged
merged 16 commits into from
Aug 14, 2019
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
### Added

- Add ability to disable the editor on demand with the [`readOnly`](https://www.draftail.org/docs/next/api#draftaileditor) prop, matching behavior of Draft.js. [#201](https://github.com/springload/draftail/issues/201), [#206](https://github.com/springload/draftail/pull/206), thanks to [@SpearThruster](https://github.com/SpearThruster).
- Add ability to use the editor as a controlled component, like vanilla Draft.js editors, with [`editorState` and `onChange`](https://www.draftail.org/docs/next/api#editorstate-and-onchange) props. Have a look at the [controlled component documentation](https://www.draftail.org/docs/next/controlled-component) for further details. [#180](https://github.com/springload/draftail/issues/180), [#207](https://github.com/springload/draftail/pull/207).

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Here are important features worth highlighting:

## Documentation

- [Getting started](https://www.draftail.org/docs/getting-started.html)
- [Getting started](https://www.draftail.org/docs/getting-started)
- [API reference](https://www.draftail.org/docs/api)
- [User guide](https://www.draftail.org/docs/user-guide)
- [Getting started with extensions](https://www.draftail.org/docs/getting-started-with-extensions)
Expand Down
63 changes: 50 additions & 13 deletions examples/components/EditorWrapper.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// @flow
import React, { Component } from "react";
/* :: import { EditorState } from "draft-js"; */
import type { RawDraftContentState } from "draft-js/lib/RawDraftContentState";

import { DraftailEditor } from "../../lib";
import { DraftailEditor, serialiseEditorStateToRaw } from "../../lib";

import SentryBoundary from "./SentryBoundary";
import Highlight from "./Highlight";
Expand All @@ -14,7 +15,10 @@ const DRAFTAIL_VERSION =

type Props = {|
id: string,
onSave: ?(?RawDraftContentState) => void,
rawContentState: ?RawDraftContentState,
editorState: ?EditorState,
onSave: ?(content: null | RawDraftContentState) => void,
onChange: ?(editorState: EditorState) => void,
|};

type State = {|
Expand All @@ -32,10 +36,11 @@ class EditorWrapper extends Component<Props, State> {
};

this.onSave = this.onSave.bind(this);
this.onChange = this.onChange.bind(this);
}

/* :: onSave: (content: ?RawDraftContentState) => void; */
onSave(content: ?RawDraftContentState) {
/* :: onSave: (content: null | RawDraftContentState) => void; */
onSave(content: null | RawDraftContentState) {
const { id, onSave } = this.props;

this.setState(({ saveCount }) => ({ content, saveCount: saveCount + 1 }));
Expand All @@ -47,19 +52,48 @@ class EditorWrapper extends Component<Props, State> {
}
}

/* :: onChange: (nextState: EditorState) => void; */
onChange(nextState: EditorState) {
const { id, onChange } = this.props;
const content = serialiseEditorStateToRaw(nextState);

this.setState(({ saveCount }) => ({ content, saveCount: saveCount + 1 }));

sessionStorage.setItem(`${id}:content`, JSON.stringify(content));

if (onChange) {
onChange(nextState);
}
}

render() {
const { id, onSave, ...editorProps } = this.props;
const {
id,
editorState,
rawContentState,
onSave,
onChange,
...editorProps
} = this.props;
const { content, saveCount } = this.state;
const storedContent = sessionStorage.getItem(`${id}:content`) || null;
const initialContent = storedContent ? JSON.parse(storedContent) : null;
const dataProps = {};
let initialContent;

if (editorState) {
dataProps.editorState = editorState;
dataProps.onChange = this.onChange;
} else {
const storedContent = sessionStorage.getItem(`${id}:content`) || "null";
initialContent =
rawContentState || (storedContent ? JSON.parse(storedContent) : null);
dataProps.rawContentState = initialContent;
dataProps.onSave = this.onSave;
}

return (
<div className={`EditorWrapper EditorWrapper--${id}`}>
<SentryBoundary>
<DraftailEditor
rawContentState={initialContent}
{...editorProps}
onSave={this.onSave}
/>
<DraftailEditor {...dataProps} {...editorProps} />
</SentryBoundary>
<details className="EditorWrapper__details">
<summary>
Expand All @@ -73,7 +107,10 @@ class EditorWrapper extends Component<Props, State> {
<span>{`Saves: ${saveCount}`}</span>
</li>
</ul>
<EditorBenchmark componentProps={this.props} runOnMount />
{/* Running multiple editors with the same base state is a source of issues. */}
{editorState ? null : (
<EditorBenchmark componentProps={this.props} runOnMount />
)}
<Highlight
value={JSON.stringify(content || initialContent, null, 2)}
/>
Expand Down
21 changes: 18 additions & 3 deletions examples/docs.story.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { storiesOf } from "@storybook/react";
import React from "react";
import React, { useState } from "react";
import { injectIntl } from "react-intl";
import { convertFromHTML, convertToHTML } from "draft-convert";
import { convertToRaw, convertFromRaw } from "draft-js";
import { EditorState, convertToRaw, convertFromRaw } from "draft-js";
import { Formik } from "formik";

import { DraftailEditor, INLINE_STYLE, ENTITY_TYPE, BLOCK_TYPE } from "../lib";
Expand All @@ -22,6 +22,8 @@ import PrismDecorator from "./components/PrismDecorator";
import ReadingTime from "./components/ReadingTime";

storiesOf("Docs", module)
// Add a decorator rendering story as a component for hooks support.
.addDecorator((Story) => <Story />)
.add("Built-in formats", () => (
<EditorWrapper
id="docs-built-in-formats"
Expand Down Expand Up @@ -495,4 +497,17 @@ storiesOf("Docs", module)
</form>
)}
</Formik>
));
))
.add("Controlled component", () => {
const [editorState, setEditorState] = useState(EditorState.createEmpty());
return (
<EditorWrapper
id="controlled-component"
editorState={editorState}
onChange={setEditorState}
entityTypes={[ENTITY_CONTROL.LINK]}
blockTypes={[BLOCK_CONTROL.UNORDERED_LIST_ITEM]}
inlineStyles={[INLINE_CONTROL.BOLD]}
/>
);
});
35 changes: 0 additions & 35 deletions lib/api/conversion.js

This file was deleted.

64 changes: 0 additions & 64 deletions lib/api/conversion.test.js

This file was deleted.

Loading