Adding external link with selected text now includes text in link chooser. Fix ()

pull/4410/head
Tony Yates 2018-03-20 23:35:41 +01:00 zatwierdzone przez Thibaud Colas
rodzic e64c4daca6
commit cab90e5d1b
7 zmienionych plików z 188 dodań i 18 usunięć

Wyświetl plik

@ -23,6 +23,7 @@ Changelog
* Fix: Correct dropdown arrow styling in Firefox, IE11 (Janneke Janssen, Alexs Mathilda)
* Fix: Password reset no indicates specific validation errors on certain password restrictions (Lucas Moeskops)
* Fix: Confirmation page on page deletion now respects custom `get_admin_display_title` methods (Kim Chee Leong)
* Fix: Adding external link with selected text now includes text in link chooser (Tony Yates, Thibaud Colas, Alexs Mathilda)
2.0.1 (xx.xx.xxxx) - IN DEVELOPMENT

Wyświetl plik

@ -284,6 +284,8 @@ Contributors
* Kevin Chung
* Kim Chee Leong
* Dan Swain
* Alexs Mathilda
* Tony Yates
Translators
===========

Wyświetl plik

@ -0,0 +1,44 @@
/**
* Returns collection of currently selected blocks.
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L19.
*/
const getSelectedBlocksList = (editorState) => {
const selectionState = editorState.getSelection();
const content = editorState.getCurrentContent();
const startKey = selectionState.getStartKey();
const endKey = selectionState.getEndKey();
const blockMap = content.getBlockMap();
const blocks = blockMap
.toSeq()
.skipUntil((_, k) => k === startKey)
.takeUntil((_, k) => k === endKey)
.concat([[endKey, blockMap.get(endKey)]]);
return blocks.toList();
};
/**
* Returns the currently selected text in the editor.
* See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L106.
*/
export const getSelectionText = (editorState) => {
const selection = editorState.getSelection();
let start = selection.getAnchorOffset();
let end = selection.getFocusOffset();
const selectedBlocks = getSelectedBlocksList(editorState);
if (selection.getIsBackward()) {
const temp = start;
start = end;
end = temp;
}
let selectedText = '';
for (let i = 0; i < selectedBlocks.size; i += 1) {
const blockStart = i === 0 ? start : 0;
const blockEnd = i === (selectedBlocks.size - 1) ? end : selectedBlocks.get(i).getText().length;
selectedText += selectedBlocks.get(i).getText().slice(blockStart, blockEnd);
}
return selectedText;
};

Wyświetl plik

@ -0,0 +1,121 @@
import {
EditorState,
convertFromRaw,
} from 'draft-js';
import { getSelectionText } from './DraftUtils';
describe('DraftUtils', () => {
describe('#getSelectionText', () => {
it('works', () => {
const content = convertFromRaw({
entityMap: {},
blocks: [
{
key: 'a',
text: 'test1234',
},
],
});
let editorState = EditorState.createWithContent(content);
let selection = editorState.getSelection();
selection = selection.merge({
anchorOffset: 0,
focusOffset: 4,
});
editorState = EditorState.acceptSelection(editorState, selection);
expect(getSelectionText(editorState)).toBe('test');
});
it('empty', () => {
expect(getSelectionText(EditorState.createEmpty())).toBe('');
});
it('backwards', () => {
const content = convertFromRaw({
entityMap: {},
blocks: [
{
key: 'a',
text: 'test1234',
},
],
});
let editorState = EditorState.createWithContent(content);
let selection = editorState.getSelection();
selection = selection.merge({
anchorOffset: 8,
focusOffset: 4,
isBackward: true,
});
editorState = EditorState.acceptSelection(editorState, selection);
expect(getSelectionText(editorState)).toBe('1234');
});
it('multiblock', () => {
const content = convertFromRaw({
entityMap: {},
blocks: [
{
key: 'a',
text: 'test1234',
},
{
key: 'b',
text: 'multiblock',
}
],
});
let editorState = EditorState.createWithContent(content);
let selection = editorState.getSelection();
selection = selection.merge({
anchorKey: 'a',
focusKey: 'b',
anchorOffset: 4,
focusOffset: 5,
isBackward: false,
});
editorState = EditorState.acceptSelection(editorState, selection);
expect(getSelectionText(editorState)).toBe('1234multi');
});
it('multiblock-backwards', () => {
const content = convertFromRaw({
entityMap: {},
blocks: [
{
key: 'a',
text: 'test1234',
},
{
key: 'b',
text: 'multiblock',
}
],
});
let editorState = EditorState.createWithContent(content);
let selection = editorState.getSelection();
selection = selection.merge({
focusKey: 'a',
anchorKey: 'b',
anchorOffset: 5,
focusOffset: 4,
isBackward: true,
});
editorState = EditorState.acceptSelection(editorState, selection);
expect(getSelectionText(editorState)).toBe('1234multi');
});
});
});

Wyświetl plik

@ -4,6 +4,7 @@ import { AtomicBlockUtils, Modifier, RichUtils, EditorState } from 'draft-js';
import { ENTITY_TYPE } from 'draftail';
import { STRINGS } from '../../../config/wagtailConfig';
import { getSelectionText } from '../DraftUtils';
const $ = global.jQuery;
@ -16,7 +17,7 @@ MUTABILITY[DOCUMENT] = 'MUTABLE';
MUTABILITY[ENTITY_TYPE.IMAGE] = 'IMMUTABLE';
MUTABILITY[EMBED] = 'IMMUTABLE';
export const getChooserConfig = (entityType, entity) => {
export const getChooserConfig = (entityType, entity, selectedText) => {
const chooserURL = {};
chooserURL[ENTITY_TYPE.IMAGE] = `${global.chooserUrls.imageChooser}?select_format=true`;
chooserURL[EMBED] = global.chooserUrls.embedsChooser;
@ -32,10 +33,7 @@ export const getChooserConfig = (entityType, entity) => {
allow_external_link: true,
allow_email_link: true,
can_choose_root: 'false',
// This does not initialise the modal with the currently selected text.
// This will need to be implemented in the future.
// See https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/block.js#L106.
link_text: '',
link_text: selectedText,
};
if (entity) {
@ -113,8 +111,9 @@ class ModalWorkflowSource extends Component {
}
componentDidMount() {
const { onClose, entityType, entity } = this.props;
const { url, urlParams } = getChooserConfig(entityType, entity);
const { onClose, entityType, entity, editorState } = this.props;
const selectedText = getSelectionText(editorState);
const { url, urlParams } = getChooserConfig(entityType, entity, selectedText);
$(document.body).on('hidden.bs.modal', this.onClose);

Wyświetl plik

@ -2,6 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import ModalWorkflowSource, { getChooserConfig, filterEntityData } from './ModalWorkflowSource';
import * as DraftUtils from '../DraftUtils';
import { EditorState, convertFromRaw, AtomicBlockUtils, RichUtils, Modifier } from 'draft-js';
global.ModalWorkflow = () => {};
@ -9,6 +10,7 @@ global.ModalWorkflow = () => {};
describe('ModalWorkflowSource', () => {
beforeEach(() => {
jest.spyOn(global, 'ModalWorkflow');
jest.spyOn(DraftUtils, 'getSelectionText').mockImplementation(() => '');
});
afterEach(() => {
@ -29,21 +31,21 @@ describe('ModalWorkflowSource', () => {
describe('#getChooserConfig', () => {
it('IMAGE', () => {
expect(getChooserConfig({ type: 'IMAGE' })).toEqual({
expect(getChooserConfig({ type: 'IMAGE' }, null, '')).toEqual({
url: '/admin/images/chooser/?select_format=true',
urlParams: {},
});
});
it('EMBED', () => {
expect(getChooserConfig({ type: 'EMBED' })).toEqual({
expect(getChooserConfig({ type: 'EMBED' }, null, '')).toEqual({
url: '/admin/embeds/chooser/',
urlParams: {},
});
});
it('DOCUMENT', () => {
expect(getChooserConfig({ type: 'DOCUMENT' })).toEqual({
expect(getChooserConfig({ type: 'DOCUMENT' }, null, '')).toEqual({
url: '/admin/documents/chooser/',
urlParams: {},
});
@ -51,25 +53,25 @@ describe('ModalWorkflowSource', () => {
describe('LINK', () => {
it('no entity', () => {
expect(getChooserConfig({ type: 'LINK' })).toMatchSnapshot();
expect(getChooserConfig({ type: 'LINK' }, null, '')).toMatchSnapshot();
});
it('page', () => {
expect(getChooserConfig({ type: 'LINK' }, {
getData: () => ({ id: 1, parentId: 0 })
})).toMatchSnapshot();
}, '')).toMatchSnapshot();
});
it('mail', () => {
expect(getChooserConfig({ type: 'LINK' }, {
getData: () => ({ url: 'mailto:test@example.com' })
})).toMatchSnapshot();
}, '')).toMatchSnapshot();
});
it('external', () => {
expect(getChooserConfig({ type: 'LINK' }, {
getData: () => ({ url: 'https://www.example.com/' })
})).toMatchSnapshot();
}, '')).toMatchSnapshot();
});
});
});
@ -146,7 +148,7 @@ describe('ModalWorkflowSource', () => {
it('#componentDidMount', () => {
const wrapper = shallow((
<ModalWorkflowSource
editorState={{}}
editorState={EditorState.createEmpty()}
entityType={{}}
entity={{}}
onComplete={() => {}}
@ -171,7 +173,7 @@ describe('ModalWorkflowSource', () => {
const wrapper = shallow((
<ModalWorkflowSource
editorState={{}}
editorState={EditorState.createEmpty()}
entityType={{}}
entity={{}}
onComplete={() => {}}
@ -192,7 +194,7 @@ describe('ModalWorkflowSource', () => {
it('#componentWillUnmount', () => {
const wrapper = shallow((
<ModalWorkflowSource
editorState={{}}
editorState={EditorState.createEmpty()}
entityType={{}}
entity={{}}
onComplete={() => {}}
@ -335,7 +337,7 @@ describe('ModalWorkflowSource', () => {
const onClose = jest.fn();
const wrapper = shallow((
<ModalWorkflowSource
editorState={{}}
editorState={EditorState.createEmpty()}
entityType={{}}
entity={{}}
onComplete={() => {}}

Wyświetl plik

@ -37,6 +37,7 @@ Bug fixes
* Correct dropdown arrow styling in Firefox, IE11 (Janneke Janssen, Alexs Mathilda)
* Password reset no indicates specific validation errors on certain password restrictions (Lucas Moeskops)
* Confirmation page on page deletion now respects custom ``get_admin_display_title`` methods (Kim Chee Leong)
* Adding external link with selected text now includes text in link chooser (Tony Yates, Thibaud Colas, Alexs Mathilda)
Upgrade considerations