Skip to content

Commit 1bc482d

Browse files
authored
Merge pull request #30 from springload/feature/overhaul. Fix #2 Fix #3 Fix #28
Editor overhaul
2 parents 93f6017 + 7450d9a commit 1bc482d

16 files changed

+236
-71
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ There are other, not so explicit dependencies at the moment:
1515

1616
- jQuery 2, separately loaded onto the page.
1717
- Specific styles from Wagtail, including its icon font.
18+
- ES6 polyfill (like for Draft.js)
1819

1920
## Development
2021

docs/README.md

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
draftail documentation
2+
======================
3+
4+
## Editor behavior
5+
6+
### Keyboard shortcuts
7+
8+
We support most of the common keyboard shortcuts users would expect to find in text editors thanks to [Draft.js key bindings](https://facebook.github.io/draft-js/docs/advanced-topics-key-bindings.html).
9+
10+
Here are the most important shortcuts:
11+
12+
|Shortcut|Function|
13+
|--------|--------|
14+
|Cmd + B | Bolden text (if enabled) |
15+
|Cmd + I | Italicise text (if enabled) |
16+
|Cmd + U | Underline text (if enabled) |
17+
|Cmd + J | Format as code (if enabled) |
18+
|Cmd + Z | Undo |
19+
|Cmd + Maj + Z | Redo |
20+
|Cmd + Left | Move selection to start of block |
21+
|Cmd + Right | Move selection to end of block |
22+
|Cmd + Tab|Increase indentation of list items|
23+
|Cmd + Maj + Tab|Decrease indentation of list items|
24+
25+
Other shortcuts we would like to support in the future:
26+
27+
|Shortcut|Function|
28+
|--------|--------|
29+
|Cmd + Option + 1/2/3/4/5/6 | Format as heading level |
30+
|Cmd + Option + 0 | Format as paragraph |
31+
|Cmd + K | Create a link (if enabled) |
32+
33+
### Expected behavior
34+
35+
### Unsupported scenarios
36+
37+
- Nesting `ol` inside `ul` or the other way around.
38+
39+
## R&D notes
40+
41+
### Other Draft.js editors
42+
43+
> Full list on https://github.com/nikgraf/awesome-draft-js
44+
45+
Other approaches:
46+
47+
- https://github.com/ianstormtaylor/slate
48+
- http://quilljs.com/
49+
50+
### Other Wagtail-integrated editors to learn from.
51+
52+
Things to borrow: keyboard shortcuts, Wagtail integration mechanism,
53+
54+
- https://github.com/jaydensmith/wagtailfroala
55+
- https://github.com/isotoma/wagtailtinymce

examples/assets/draftail.css

+7-6
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,21 @@
1414
margin-right: 4px;
1515
margin-right: 0.25rem;
1616
color: inherit;
17+
border: 0;
1718
cursor: pointer; }
1819
.RichEditor-styleButton:hover {
1920
color: inherit; }
2021

2122
.RichEditor-activeButton {
2223
background: #666;
23-
color: #fff; }
24+
color: #ffffff; }
2425
.RichEditor-activeButton:hover {
25-
color: #fff; }
26+
color: #ffffff; }
2627

2728
.RichEditor-tooltip__button {
2829
appearance: none;
2930
background: #808080;
30-
color: #fff;
31+
color: #ffffff;
3132
padding: 0.25rem 0.5rem;
3233
border: 0;
3334
border-radius: 1px; }
@@ -58,7 +59,7 @@
5859
justify-content: center; }
5960

6061
.Modal-content {
61-
background: #fff;
62+
background: #ffffff;
6263
max-width: 500px; }
6364

6465
.Modal-header {
@@ -311,7 +312,7 @@
311312
padding: 0.375rem 0.5rem;
312313
margin-top: 0.5rem;
313314
background-color: #333;
314-
color: #fff;
315+
color: #ffffff;
315316
z-index: 1; }
316317
.RichEditor-tooltip:before {
317318
content: '';
@@ -328,7 +329,7 @@
328329
display: inline-block; }
329330

330331
.RichEditor-tooltip__link:hover {
331-
color: #fff; }
332+
color: #ffffff; }
332333

333334
.RichEditor-tooltip__link:last-child {
334335
margin-right: 0; }

examples/basic.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const options = {
2424
],
2525
};
2626

27-
const value = {
27+
const rawContentState = {
2828
entityMap: {},
2929
blocks: [
3030
{
@@ -223,8 +223,16 @@ const value = {
223223
],
224224
};
225225

226+
const onSave = (contentState) => {
227+
console.log('Save basic example:', contentState);
228+
};
229+
226230
const editor = (
227-
<DraftailEditor name="basic" value={JSON.stringify(value)} options={options} />
231+
<DraftailEditor
232+
rawContentState={rawContentState}
233+
options={options}
234+
onSave={onSave}
235+
/>
228236
);
229237

230238
ReactDOM.render(editor, mount);

examples/entities.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,21 @@ const options = {
8888
],
8989
};
9090

91+
const saveField = document.createElement('input');
92+
saveField.type = 'hidden';
93+
mount.parentNode.appendChild(saveField);
94+
95+
const onSave = (rawContentState) => {
96+
const serialised = JSON.stringify(rawContentState);
97+
saveField.value = serialised;
98+
localStorage.setItem('entities:rawContentState', serialised);
99+
};
100+
91101
const editor = (
92102
<DraftailEditor
93-
name="test"
94-
value="{}"
103+
rawContentState={JSON.parse(localStorage.getItem('entities:rawContentState')) || {}}
95104
options={options}
105+
onSave={onSave}
96106
/>
97107
);
98108

examples/index.html

+6
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,11 @@ <h3>Usage</h3>
4444
<script src="assets/vendor.bundle.js"></script>
4545
<script src="assets/basic.bundle.js"></script>
4646
<script src="assets/entities.bundle.js"></script>
47+
48+
<script>
49+
window.ga=function(){ga.q.push(arguments)};ga.q=[];ga.l=+new Date;
50+
ga('create','UA-79835767-6','auto');ga('send','pageview')
51+
</script>
52+
<script src="https://www.google-analytics.com/analytics.js" async defer></script>
4753
</body>
4854
</html>

lib/api/conversion.js

+8-9
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import { EditorState, convertFromRaw, convertToRaw } from 'draft-js';
22

33
export default {
4-
// Serialised RawDraftContentState => EditorState.
5-
createEditorState(serialisedState, decorators) {
6-
const rawState = JSON.parse(serialisedState);
4+
// RawDraftContentState + decorators => EditorState.
5+
createEditorState(rawContentState, decorators) {
76
let editorState;
87

9-
if (rawState && Object.keys(rawState).length !== 0) {
10-
const contentState = convertFromRaw(rawState);
8+
if (rawContentState && Object.keys(rawContentState).length !== 0) {
9+
const contentState = convertFromRaw(rawContentState);
1110
editorState = EditorState.createWithContent(contentState, decorators);
1211
} else {
1312
editorState = EditorState.createEmpty(decorators);
@@ -16,17 +15,17 @@ export default {
1615
return editorState;
1716
},
1817

19-
// EditorState => Serialised RawDraftContentState.
18+
// EditorState => RawDraftContentState.
2019
serialiseEditorState(editorState) {
2120
const contentState = editorState.getCurrentContent();
22-
const rawContent = convertToRaw(contentState);
21+
const rawContentState = convertToRaw(contentState);
2322

24-
const isEmpty = rawContent.blocks.every((block) => {
23+
const isEmpty = rawContentState.blocks.every((block) => {
2524
return block.text.trim().length === 0
2625
&& block.entityRanges.length === 0
2726
&& block.inlineStyleRanges.length === 0;
2827
});
2928

30-
return JSON.stringify(isEmpty ? {} : rawContent);
29+
return isEmpty ? {} : rawContentState;
3130
},
3231
};

lib/api/conversion.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import conversion from '../api/conversion';
44
const emptyDecorator = new CompositeDecorator([]);
55

66
const stubs = {
7-
emptyContent: JSON.stringify({}),
8-
realContent: JSON.stringify({
7+
emptyContent: {},
8+
realContent: {
99
entityMap: {},
1010
blocks: [
1111
{
@@ -27,7 +27,7 @@ const stubs = {
2727
data: {},
2828
},
2929
],
30-
}),
30+
},
3131
};
3232

3333
describe('conversion', () => {

lib/components/Button.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import React from 'react';
22

3+
const onMouseDown = (onClick, e) => {
4+
e.preventDefault();
5+
6+
onClick();
7+
};
8+
39
const Button = ({ icon, label, active, onClick }) => (
4-
<a
5-
href="#"
10+
<button
611
className={`RichEditor-styleButton${active ? ' RichEditor-activeButton' : ''}${icon ? ` icon icon-${icon}` : ''}`}
7-
onClick={onClick}
12+
onMouseDown={onMouseDown.bind(null, onClick)}
813
>
914
{label}
10-
</a>
15+
</button>
1116
);
1217

1318
Button.propTypes = {
@@ -17,4 +22,9 @@ Button.propTypes = {
1722
active: React.PropTypes.bool,
1823
};
1924

25+
Button.defaultProps = {
26+
icon: '',
27+
active: false,
28+
};
29+
2030
export default Button;

lib/components/Button.test.js

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react';
2+
import { shallow } from 'enzyme';
3+
import Button from '../components/Button';
4+
5+
describe('Button', () => {
6+
it('exists', () => {
7+
expect(Button).toBeDefined();
8+
});
9+
10+
it('basic', () => {
11+
expect(shallow(<Button label="Test" onClick={() => {}} />)).toBeDefined();
12+
});
13+
});

0 commit comments

Comments
 (0)