kopia lustrzana https://github.com/wagtail/wagtail
rodzic
f9d20d1fa6
commit
93a8227a52
|
@ -9,7 +9,7 @@ import MediaBlock from '../blocks/MediaBlock';
|
|||
* Editor block to display media and edit content.
|
||||
*/
|
||||
const EmbedBlock = props => {
|
||||
const { entity, onRemoveEntity } = props.blockProps;
|
||||
const { entity, onEditEntity, onRemoveEntity } = props.blockProps;
|
||||
const { url, title, thumbnail } = entity.getData();
|
||||
|
||||
return (
|
||||
|
@ -25,7 +25,9 @@ const EmbedBlock = props => {
|
|||
{title}
|
||||
</a>
|
||||
) : null}
|
||||
|
||||
<button className="button Tooltip__button" type="button" onClick={onEditEntity}>
|
||||
{STRINGS.EDIT}
|
||||
</button>
|
||||
<button className="button button-secondary no Tooltip__button" onClick={onRemoveEntity}>
|
||||
{STRINGS.DELETE}
|
||||
</button>
|
||||
|
|
|
@ -8,7 +8,9 @@ describe('EmbedBlock', () => {
|
|||
expect(
|
||||
shallow(
|
||||
<EmbedBlock
|
||||
block={{}}
|
||||
blockProps={{
|
||||
editorState: {},
|
||||
entityType: {},
|
||||
entity: {
|
||||
getData: () => ({
|
||||
|
@ -17,6 +19,7 @@ describe('EmbedBlock', () => {
|
|||
thumbnail: 'http://www.example.com/example.png',
|
||||
}),
|
||||
},
|
||||
onChange: () => {},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -27,11 +30,14 @@ describe('EmbedBlock', () => {
|
|||
expect(
|
||||
shallow(
|
||||
<EmbedBlock
|
||||
block={{}}
|
||||
blockProps={{
|
||||
editorState: {},
|
||||
entityType: {},
|
||||
entity: {
|
||||
getData: () => ({}),
|
||||
},
|
||||
onChange: () => {},
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import { DraftUtils } from 'draftail';
|
||||
import React from 'react';
|
||||
|
||||
import { STRINGS } from '../../../config/wagtailConfig';
|
||||
|
||||
|
@ -9,41 +8,25 @@ import MediaBlock from '../blocks/MediaBlock';
|
|||
/**
|
||||
* Editor block to preview and edit images.
|
||||
*/
|
||||
class ImageBlock extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const ImageBlock = props => {
|
||||
const { blockProps } = props;
|
||||
const { entity, onEditEntity, onRemoveEntity } = blockProps;
|
||||
const { src, alt } = entity.getData();
|
||||
const altLabel = `${STRINGS.ALT_TEXT}: “${alt || ''}”`;
|
||||
|
||||
this.changeAlt = this.changeAlt.bind(this);
|
||||
}
|
||||
return (
|
||||
<MediaBlock {...props} src={src} alt="">
|
||||
<p className="ImageBlock__alt">{altLabel}</p>
|
||||
|
||||
changeAlt(e) {
|
||||
const { block, blockProps } = this.props;
|
||||
const { editorState, onChange } = blockProps;
|
||||
|
||||
const data = {
|
||||
alt: e.target.value,
|
||||
};
|
||||
|
||||
onChange(DraftUtils.updateBlockEntity(editorState, block, data));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { blockProps } = this.props;
|
||||
const { entity, onRemoveEntity } = blockProps;
|
||||
const { src, alt } = entity.getData();
|
||||
const altLabel = `${STRINGS.ALT_TEXT}: “${alt || ''}”`;
|
||||
|
||||
return (
|
||||
<MediaBlock {...this.props} src={src} alt="">
|
||||
<p className="ImageBlock__alt">{altLabel}</p>
|
||||
|
||||
<button className="button button-secondary no Tooltip__button" onClick={onRemoveEntity}>
|
||||
{STRINGS.DELETE}
|
||||
</button>
|
||||
</MediaBlock>
|
||||
);
|
||||
}
|
||||
}
|
||||
<button className="button Tooltip__button" type="button" onClick={onEditEntity}>
|
||||
{STRINGS.EDIT}
|
||||
</button>
|
||||
<button className="button button-secondary no Tooltip__button" onClick={onRemoveEntity}>
|
||||
{STRINGS.DELETE}
|
||||
</button>
|
||||
</MediaBlock>
|
||||
);
|
||||
};
|
||||
|
||||
ImageBlock.propTypes = {
|
||||
block: PropTypes.object.isRequired,
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { DraftUtils } from 'draftail';
|
||||
|
||||
import ImageBlock from '../blocks/ImageBlock';
|
||||
|
||||
describe('ImageBlock', () => {
|
||||
|
@ -64,48 +62,4 @@ describe('ImageBlock', () => {
|
|||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('changeAlt', () => {
|
||||
jest.spyOn(DraftUtils, 'updateBlockEntity');
|
||||
DraftUtils.updateBlockEntity.mockImplementation(e => e);
|
||||
|
||||
const onChange = jest.fn();
|
||||
const wrapper = shallow(
|
||||
<ImageBlock
|
||||
block={{}}
|
||||
blockProps={{
|
||||
editorState: {},
|
||||
entityType: {},
|
||||
entity: {
|
||||
getData: () => ({
|
||||
src: 'example.png',
|
||||
alt: 'Test',
|
||||
}),
|
||||
},
|
||||
onChange,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
// // Alt field is readonly for now.
|
||||
wrapper.instance().changeAlt({
|
||||
target: {
|
||||
value: 'new alt',
|
||||
}
|
||||
});
|
||||
// wrapper.find('[type="text"]').simulate('change', {
|
||||
// target: {
|
||||
// value: 'new alt',
|
||||
// },
|
||||
// });
|
||||
|
||||
expect(onChange).toHaveBeenCalled();
|
||||
expect(DraftUtils.updateBlockEntity).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
{},
|
||||
expect.objectContaining({ alt: 'new alt' })
|
||||
);
|
||||
|
||||
DraftUtils.updateBlockEntity.mockRestore();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ import { Icon } from 'draftail';
|
|||
|
||||
import Tooltip from '../Tooltip/Tooltip';
|
||||
import Portal from '../../Portal/Portal';
|
||||
import { SelectionState, EditorState } from 'draft-js';
|
||||
|
||||
// Constraints the maximum size of the tooltip.
|
||||
const OPTIONS_MAX_WIDTH = 300;
|
||||
|
@ -21,12 +22,14 @@ class MediaBlock extends Component {
|
|||
showTooltipAt: null,
|
||||
};
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
this.selectCurrentBlock = this.selectCurrentBlock.bind(this);
|
||||
this.openTooltip = this.openTooltip.bind(this);
|
||||
this.closeTooltip = this.closeTooltip.bind(this);
|
||||
this.renderTooltip = this.renderTooltip.bind(this);
|
||||
}
|
||||
|
||||
openTooltip(e) {
|
||||
onClick(e) {
|
||||
const trigger = e.target.closest('[data-draftail-trigger]');
|
||||
|
||||
// Click is within the tooltip.
|
||||
|
@ -34,6 +37,24 @@ class MediaBlock extends Component {
|
|||
return;
|
||||
}
|
||||
|
||||
this.selectCurrentBlock();
|
||||
this.openTooltip(trigger);
|
||||
}
|
||||
|
||||
selectCurrentBlock() {
|
||||
const { block, blockProps } = this.props;
|
||||
const { editorState, onChange } = blockProps;
|
||||
const selection = new SelectionState({
|
||||
anchorKey: block.getKey(),
|
||||
anchorOffset: 0,
|
||||
focusKey: block.getKey(),
|
||||
focusOffset: block.getLength(),
|
||||
hasFocus: true,
|
||||
});
|
||||
onChange(EditorState.forceSelection(editorState, selection));
|
||||
}
|
||||
|
||||
openTooltip(trigger) {
|
||||
const container = trigger.closest('[data-draftail-editor-wrapper]');
|
||||
const containerRect = container.getBoundingClientRect();
|
||||
const rect = trigger.getBoundingClientRect();
|
||||
|
@ -84,7 +105,7 @@ class MediaBlock extends Component {
|
|||
type="button"
|
||||
tabIndex={-1}
|
||||
className="MediaBlock"
|
||||
onClick={this.openTooltip}
|
||||
onClick={this.onClick}
|
||||
data-draftail-trigger
|
||||
>
|
||||
<span className="MediaBlock__icon-wrapper" aria-hidden>
|
||||
|
@ -102,7 +123,10 @@ class MediaBlock extends Component {
|
|||
MediaBlock.propTypes = {
|
||||
blockProps: PropTypes.shape({
|
||||
entityType: PropTypes.object.isRequired,
|
||||
editorState: PropTypes.object.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
block: PropTypes.object.isRequired,
|
||||
src: PropTypes.string,
|
||||
alt: PropTypes.string,
|
||||
children: PropTypes.node.isRequired,
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react';
|
|||
import { shallow, mount } from 'enzyme';
|
||||
|
||||
import MediaBlock from '../blocks/MediaBlock';
|
||||
import { EditorState } from 'draft-js';
|
||||
|
||||
describe('MediaBlock', () => {
|
||||
it('renders', () => {
|
||||
|
@ -10,7 +11,9 @@ describe('MediaBlock', () => {
|
|||
<MediaBlock
|
||||
src="example.png"
|
||||
alt=""
|
||||
block={{}}
|
||||
blockProps={{
|
||||
editorState: {},
|
||||
entityType: {
|
||||
icon: '#icon-test',
|
||||
},
|
||||
|
@ -19,6 +22,7 @@ describe('MediaBlock', () => {
|
|||
src: 'example.png',
|
||||
}),
|
||||
},
|
||||
onChange: () => {},
|
||||
}}
|
||||
>
|
||||
Test
|
||||
|
@ -33,13 +37,16 @@ describe('MediaBlock', () => {
|
|||
<MediaBlock
|
||||
src=""
|
||||
alt=""
|
||||
block={{}}
|
||||
blockProps={{
|
||||
editorState: {},
|
||||
entityType: {
|
||||
icon: '#icon-test',
|
||||
},
|
||||
entity: {
|
||||
getData: () => ({}),
|
||||
},
|
||||
onChange: () => {},
|
||||
}}
|
||||
>
|
||||
Test
|
||||
|
@ -48,37 +55,61 @@ describe('MediaBlock', () => {
|
|||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe('tooltip', () => {
|
||||
describe('on click', () => {
|
||||
let target;
|
||||
let wrapper;
|
||||
let blockProps;
|
||||
|
||||
beforeEach(() => {
|
||||
target = document.createElement('div');
|
||||
target.setAttribute('data-draftail-trigger', true);
|
||||
document.body.appendChild(target);
|
||||
document.body.setAttribute('data-draftail-editor-wrapper', true);
|
||||
|
||||
blockProps = {
|
||||
editorState: EditorState.createEmpty(),
|
||||
entityType: {
|
||||
icon: '#icon-test',
|
||||
},
|
||||
entity: {
|
||||
getData: () => ({
|
||||
src: 'example.png',
|
||||
}),
|
||||
},
|
||||
onChange: () => {},
|
||||
};
|
||||
wrapper = mount(
|
||||
<MediaBlock
|
||||
src="example.png"
|
||||
alt=""
|
||||
blockProps={{
|
||||
entityType: {
|
||||
icon: '#icon-test',
|
||||
},
|
||||
entity: {
|
||||
getData: () => ({
|
||||
src: 'example.png',
|
||||
}),
|
||||
},
|
||||
block={{
|
||||
getKey: () => 'abcde',
|
||||
getLength: () => 1,
|
||||
}}
|
||||
blockProps={blockProps}
|
||||
>
|
||||
<div id="test">Test</div>
|
||||
</MediaBlock>
|
||||
);
|
||||
});
|
||||
|
||||
it('opens', () => {
|
||||
it('selected', () => {
|
||||
blockProps.onChange = (editorState) => {
|
||||
const selecttion = editorState.getSelection();
|
||||
|
||||
expect(selecttion.getAnchorKey()).toEqual('abcde');
|
||||
expect(selecttion.getAnchorOffset()).toEqual(0);
|
||||
expect(selecttion.getFocusKey()).toEqual('abcde');
|
||||
expect(selecttion.getFocusOffset()).toEqual(1);
|
||||
};
|
||||
|
||||
jest.spyOn(blockProps, 'onChange');
|
||||
|
||||
wrapper.simulate('click', { target });
|
||||
|
||||
expect(blockProps.onChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('tooltip opens', () => {
|
||||
wrapper.simulate('click', { target });
|
||||
|
||||
expect(
|
||||
|
@ -97,7 +128,7 @@ describe('MediaBlock', () => {
|
|||
expect(target.getBoundingClientRect).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('large viewport', () => {
|
||||
it('tooltip in large viewport', () => {
|
||||
target.getBoundingClientRect = () => ({
|
||||
top: 0,
|
||||
left: 0,
|
||||
|
@ -114,7 +145,7 @@ describe('MediaBlock', () => {
|
|||
).toBe('Tooltip Tooltip--left');
|
||||
});
|
||||
|
||||
it('closes', () => {
|
||||
it('tooltip closes', () => {
|
||||
jest.spyOn(target, 'getBoundingClientRect');
|
||||
|
||||
expect(wrapper.state('showTooltipAt')).toBe(null);
|
||||
|
|
|
@ -3,16 +3,25 @@
|
|||
exports[`EmbedBlock no data 1`] = `
|
||||
<MediaBlock
|
||||
alt=""
|
||||
block={Object {}}
|
||||
blockProps={
|
||||
Object {
|
||||
"editorState": Object {},
|
||||
"entity": Object {
|
||||
"getData": [Function],
|
||||
},
|
||||
"entityType": Object {},
|
||||
"onChange": [Function],
|
||||
}
|
||||
}
|
||||
src={null}
|
||||
>
|
||||
<button
|
||||
className="button Tooltip__button"
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="button button-secondary no Tooltip__button"
|
||||
>
|
||||
|
@ -24,12 +33,15 @@ exports[`EmbedBlock no data 1`] = `
|
|||
exports[`EmbedBlock renders 1`] = `
|
||||
<MediaBlock
|
||||
alt=""
|
||||
block={Object {}}
|
||||
blockProps={
|
||||
Object {
|
||||
"editorState": Object {},
|
||||
"entity": Object {
|
||||
"getData": [Function],
|
||||
},
|
||||
"entityType": Object {},
|
||||
"onChange": [Function],
|
||||
}
|
||||
}
|
||||
src="http://www.example.com/example.png"
|
||||
|
@ -43,6 +55,12 @@ exports[`EmbedBlock renders 1`] = `
|
|||
>
|
||||
Test title
|
||||
</a>
|
||||
<button
|
||||
className="button Tooltip__button"
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="button button-secondary no Tooltip__button"
|
||||
>
|
||||
|
|
|
@ -21,6 +21,12 @@ exports[`ImageBlock alt 1`] = `
|
|||
>
|
||||
Alt text: “Test”
|
||||
</p>
|
||||
<button
|
||||
className="button Tooltip__button"
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="button button-secondary no Tooltip__button"
|
||||
>
|
||||
|
@ -50,6 +56,12 @@ exports[`ImageBlock no data 1`] = `
|
|||
>
|
||||
Alt text: “”
|
||||
</p>
|
||||
<button
|
||||
className="button Tooltip__button"
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="button button-secondary no Tooltip__button"
|
||||
>
|
||||
|
@ -79,6 +91,12 @@ exports[`ImageBlock renders 1`] = `
|
|||
>
|
||||
Alt text: “”
|
||||
</p>
|
||||
<button
|
||||
className="button Tooltip__button"
|
||||
type="button"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
<button
|
||||
className="button button-secondary no Tooltip__button"
|
||||
>
|
||||
|
|
|
@ -54,7 +54,7 @@ exports[`MediaBlock renders 1`] = `
|
|||
</button>
|
||||
`;
|
||||
|
||||
exports[`MediaBlock tooltip opens 1`] = `
|
||||
exports[`MediaBlock on click tooltip opens 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="Tooltip Tooltip--top-left"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { Component } from 'react';
|
||||
import { AtomicBlockUtils, Modifier, RichUtils, EditorState } from 'draft-js';
|
||||
import { ENTITY_TYPE } from 'draftail';
|
||||
import { ENTITY_TYPE, DraftUtils } from 'draftail';
|
||||
|
||||
import { STRINGS } from '../../../config/wagtailConfig';
|
||||
import { getSelectionText } from '../DraftUtils';
|
||||
|
@ -23,16 +23,31 @@ export const getChooserConfig = (entityType, entity, selectedText) => {
|
|||
|
||||
switch (entityType.type) {
|
||||
case ENTITY_TYPE.IMAGE:
|
||||
if (entity) {
|
||||
const data = entity.getData();
|
||||
url = `${global.chooserUrls.imageChooser}${data.id}/select_format/`;
|
||||
urlParams = {
|
||||
format: data.format,
|
||||
alt_text: data.alt,
|
||||
};
|
||||
} else {
|
||||
url = `${global.chooserUrls.imageChooser}?select_format=true`;
|
||||
urlParams = {};
|
||||
}
|
||||
return {
|
||||
url: `${global.chooserUrls.imageChooser}?select_format=true`,
|
||||
urlParams: {},
|
||||
url,
|
||||
urlParams,
|
||||
onload: global.IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS,
|
||||
};
|
||||
|
||||
case EMBED:
|
||||
urlParams = {};
|
||||
if (entity) {
|
||||
urlParams.url = entity.getData().url;
|
||||
}
|
||||
return {
|
||||
url: global.chooserUrls.embedsChooser,
|
||||
urlParams: {},
|
||||
urlParams,
|
||||
onload: global.EMBED_CHOOSER_MODAL_ONLOAD_HANDLERS,
|
||||
};
|
||||
|
||||
|
@ -180,33 +195,38 @@ class ModalWorkflowSource extends Component {
|
|||
}
|
||||
|
||||
onChosen(data) {
|
||||
const { editorState, entityType, onComplete } = this.props;
|
||||
const { editorState, entity, entityKey, entityType, onComplete } = this.props;
|
||||
const content = editorState.getCurrentContent();
|
||||
const selection = editorState.getSelection();
|
||||
|
||||
const entityData = filterEntityData(entityType, data);
|
||||
const mutability = MUTABILITY[entityType.type];
|
||||
const contentWithEntity = content.createEntity(entityType.type, mutability, entityData);
|
||||
const entityKey = contentWithEntity.getLastCreatedEntityKey();
|
||||
|
||||
let nextState;
|
||||
|
||||
if (entityType.block) {
|
||||
// Only supports adding entities at the moment, not editing existing ones.
|
||||
// See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/ImageSource.js#L44-L62.
|
||||
// See https://github.com/springload/draftail/blob/cdc8988fe2e3ac32374317f535a5338ab97e8637/examples/sources/EmbedSource.js#L64-L91
|
||||
nextState = AtomicBlockUtils.insertAtomicBlock(editorState, entityKey, ' ');
|
||||
if (entity && entityKey) {
|
||||
// Replace the data for the currently selected block
|
||||
const blockKey = selection.getAnchorKey();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
nextState = DraftUtils.updateBlockEntity(editorState, block, entityData);
|
||||
} else {
|
||||
// Add new entity if there is none selected
|
||||
const contentWithEntity = content.createEntity(entityType.type, mutability, entityData);
|
||||
const newEntityKey = contentWithEntity.getLastCreatedEntityKey();
|
||||
nextState = AtomicBlockUtils.insertAtomicBlock(editorState, newEntityKey, ' ');
|
||||
}
|
||||
} else {
|
||||
const contentWithEntity = content.createEntity(entityType.type, mutability, entityData);
|
||||
const newEntityKey = contentWithEntity.getLastCreatedEntityKey();
|
||||
|
||||
// Replace text if the chooser demands it, or if there is no selected text in the first place.
|
||||
const shouldReplaceText = data.prefer_this_title_as_link_text || selection.isCollapsed();
|
||||
|
||||
if (shouldReplaceText) {
|
||||
// If there is a title attribute, use it. Otherwise we inject the URL.
|
||||
const newText = data.title || data.url;
|
||||
const newContent = Modifier.replaceText(content, selection, newText, null, entityKey);
|
||||
const newContent = Modifier.replaceText(content, selection, newText, null, newEntityKey);
|
||||
nextState = EditorState.push(editorState, newContent, 'insert-characters');
|
||||
} else {
|
||||
nextState = RichUtils.toggleLink(editorState, selection, entityKey);
|
||||
nextState = RichUtils.toggleLink(editorState, selection, newEntityKey);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +254,7 @@ ModalWorkflowSource.propTypes = {
|
|||
editorState: PropTypes.object.isRequired,
|
||||
entityType: PropTypes.object.isRequired,
|
||||
entity: PropTypes.object,
|
||||
entityKey: PropTypes.string,
|
||||
onComplete: PropTypes.func.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import { shallow } from 'enzyme';
|
|||
|
||||
import ModalWorkflowSource, { getChooserConfig, filterEntityData } from './ModalWorkflowSource';
|
||||
import * as DraftUtils from '../DraftUtils';
|
||||
import { DraftUtils as DraftailUtils } from 'draftail';
|
||||
import { EditorState, convertFromRaw, AtomicBlockUtils, RichUtils, Modifier } from 'draft-js';
|
||||
|
||||
global.ModalWorkflow = () => {};
|
||||
|
@ -30,7 +31,7 @@ describe('ModalWorkflowSource', () => {
|
|||
});
|
||||
|
||||
describe('#getChooserConfig', () => {
|
||||
it('IMAGE', () => {
|
||||
it('IMAGE without entity', () => {
|
||||
expect(getChooserConfig({ type: 'IMAGE' }, null, '')).toEqual({
|
||||
url: '/admin/images/chooser/?select_format=true',
|
||||
urlParams: {},
|
||||
|
@ -38,7 +39,19 @@ describe('ModalWorkflowSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('EMBED', () => {
|
||||
it('IMAGE with entity', () => {
|
||||
const entity = { getData: () => ({ id: 1, format: 'left', alt: 'alt' }) };
|
||||
expect(getChooserConfig({ type: 'IMAGE' }, entity, '')).toEqual({
|
||||
url: '/admin/images/chooser/1/select_format/',
|
||||
urlParams: {
|
||||
format: 'left',
|
||||
alt_text: 'alt',
|
||||
},
|
||||
onload: global.IMAGE_CHOOSER_MODAL_ONLOAD_HANDLERS,
|
||||
});
|
||||
});
|
||||
|
||||
it('EMBED without entity', () => {
|
||||
expect(getChooserConfig({ type: 'EMBED' }, null, '')).toEqual({
|
||||
url: '/admin/embeds/chooser/',
|
||||
urlParams: {},
|
||||
|
@ -46,6 +59,15 @@ describe('ModalWorkflowSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('EMBED with entity', () => {
|
||||
const entity = { getData: () => ({ url: 'http://example.org/content' }) };
|
||||
expect(getChooserConfig({ type: 'EMBED' }, entity, '')).toEqual({
|
||||
url: '/admin/embeds/chooser/',
|
||||
urlParams: { url: 'http://example.org/content' },
|
||||
onload: global.EMBED_CHOOSER_MODAL_ONLOAD_HANDLERS,
|
||||
});
|
||||
});
|
||||
|
||||
it('DOCUMENT', () => {
|
||||
expect(getChooserConfig({ type: 'DOCUMENT' }, null, '')).toEqual({
|
||||
url: '/admin/documents/chooser/',
|
||||
|
@ -265,7 +287,7 @@ describe('ModalWorkflowSource', () => {
|
|||
RichUtils.toggleLink.mockRestore();
|
||||
});
|
||||
|
||||
it('block', () => {
|
||||
it('block for new entity', () => {
|
||||
jest.spyOn(AtomicBlockUtils, 'insertAtomicBlock');
|
||||
|
||||
const onComplete = jest.fn();
|
||||
|
@ -307,6 +329,57 @@ describe('ModalWorkflowSource', () => {
|
|||
AtomicBlockUtils.insertAtomicBlock.mockRestore();
|
||||
});
|
||||
|
||||
it('block for existing entity', () => {
|
||||
jest.spyOn(DraftailUtils, 'updateBlockEntity');
|
||||
const onComplete = jest.fn();
|
||||
const close = jest.fn();
|
||||
|
||||
let editorState = EditorState.createWithContent(convertFromRaw({
|
||||
blocks: [
|
||||
{
|
||||
key: 'a',
|
||||
text: ' ',
|
||||
type: 'atomic',
|
||||
entityRanges: [{ offset: 0, length: 1, key: 'first' }],
|
||||
data: {},
|
||||
}
|
||||
],
|
||||
entityMap: {
|
||||
first: {
|
||||
type: 'IMAGE',
|
||||
mutability: 'IMMUTABLE',
|
||||
data: {},
|
||||
}
|
||||
}
|
||||
}));
|
||||
let selection = editorState.getSelection();
|
||||
selection = selection.merge({
|
||||
anchorKey: 'a',
|
||||
});
|
||||
editorState = EditorState.acceptSelection(editorState, selection);
|
||||
const wrapper = shallow((
|
||||
<ModalWorkflowSource
|
||||
editorState={editorState}
|
||||
entityType={{
|
||||
block: () => {},
|
||||
}}
|
||||
entity={{}}
|
||||
entityKey={'first'}
|
||||
onComplete={onComplete}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
));
|
||||
|
||||
wrapper.instance().workflow = { close };
|
||||
wrapper.instance().onChosen({});
|
||||
|
||||
expect(onComplete).toHaveBeenCalled();
|
||||
expect(DraftailUtils.updateBlockEntity).toHaveBeenCalled();
|
||||
expect(close).toHaveBeenCalled();
|
||||
|
||||
DraftailUtils.updateBlockEntity.mockRestore();
|
||||
});
|
||||
|
||||
it('prefer_this_title_as_link_text', () => {
|
||||
jest.spyOn(Modifier, 'replaceText');
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ global.wagtailConfig = {
|
|||
},
|
||||
STRINGS: {
|
||||
DELETE: 'Delete',
|
||||
EDIT: 'Edit',
|
||||
PAGE: 'Page',
|
||||
PAGES: 'Pages',
|
||||
LOADING: 'Loading…',
|
||||
|
|
|
@ -50,6 +50,7 @@ WAGTAILADMIN_PROVIDED_LANGUAGES = [
|
|||
def get_js_translation_strings():
|
||||
return {
|
||||
'DELETE': _('Delete'),
|
||||
'EDIT': _('Edit'),
|
||||
'PAGE': _('Page'),
|
||||
'PAGES': _('Pages'),
|
||||
'LOADING': _('Loading…'),
|
||||
|
|
Ładowanie…
Reference in New Issue