mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-29 01:22:07 +01:00
parent
c37847a6fb
commit
d52484f524
@ -15,6 +15,7 @@ Changelog
|
||||
* Add `AbstractImage.get_renditions()` for efficient generation of multiple renditions (Andy Babic)
|
||||
* Optimise queries in collection permission policies using cache on the user object (Sage Abdullah)
|
||||
* Phone numbers entered via a link chooser will now have any spaces stripped out, ensuring a valid href="tel:..." attribute (Sahil Jangra)
|
||||
* Auto-select the `StreamField` block when only one block type is declared (Sébastien Corbin)
|
||||
* Fix: Prevent choosers from failing when initial value is an unrecognised ID, e.g. when moving a page from a location where `parent_page_types` would disallow it (Dan Braghis)
|
||||
* Fix: Move comment notifications toggle to the comments side panel (Sage Abdullah)
|
||||
* Fix: Remove comment button on InlinePanel fields (Sage Abdullah)
|
||||
|
@ -85,6 +85,18 @@ class StreamBlockMenu extends BaseInsertionControl {
|
||||
$(placeholder).replaceWith(dom);
|
||||
this.element = dom.get(0);
|
||||
this.addButton = dom.find('button');
|
||||
|
||||
const blockItems = this.blockItems;
|
||||
if (blockItems.length === 1 && blockItems[0].items.length === 1) {
|
||||
// Only one child type can be added, bypass the combobox
|
||||
this.addButton.click(() => {
|
||||
if (this.onRequestInsert) {
|
||||
this.onRequestInsert(this.index, blockItems[0].items[0]);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.combobox = document.createElement('div');
|
||||
this.canAddBlock = true;
|
||||
this.disabledBlockTypes = new Set();
|
||||
@ -104,8 +116,8 @@ class StreamBlockMenu extends BaseInsertionControl {
|
||||
});
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
const items = this.groupedChildBlockDefs.map(([group, blockDefs]) => {
|
||||
get blockItems() {
|
||||
return this.groupedChildBlockDefs.map(([group, blockDefs]) => {
|
||||
const groupItems = blockDefs
|
||||
// Allow adding all blockDefs even when disabled, so validation only impedes when saving.
|
||||
// Keeping the previous filtering here for future reference.
|
||||
@ -122,12 +134,15 @@ class StreamBlockMenu extends BaseInsertionControl {
|
||||
items: groupItems,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
renderMenu() {
|
||||
const blockItems = this.blockItems;
|
||||
ReactDOM.render(
|
||||
<ComboBox
|
||||
label={comboBoxLabel}
|
||||
placeholder={comboBoxLabel}
|
||||
items={items}
|
||||
items={blockItems}
|
||||
getItemLabel={(type, item) => item.label}
|
||||
getItemDescription={(item) => item.label}
|
||||
getSearchFields={(item) => [item.label, item.type]}
|
||||
|
@ -925,3 +925,77 @@ describe('telepath: wagtail.blocks.StreamBlock with blockCounts.max_num set', ()
|
||||
assertCanAddBlock();
|
||||
});
|
||||
});
|
||||
|
||||
describe('telepath: wagtail.blocks.StreamBlock with unique block type', () => {
|
||||
let boundBlock;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create mocks for callbacks
|
||||
constructor = jest.fn();
|
||||
setState = jest.fn();
|
||||
getState = jest.fn();
|
||||
getValue = jest.fn();
|
||||
focus = jest.fn();
|
||||
|
||||
// Define a test block
|
||||
const blockDef = new StreamBlockDefinition(
|
||||
'',
|
||||
[
|
||||
[
|
||||
'',
|
||||
[
|
||||
new FieldBlockDefinition(
|
||||
'test_block_a',
|
||||
new DummyWidgetDefinition('Block A widget'),
|
||||
{
|
||||
label: 'Test Block A',
|
||||
required: true,
|
||||
icon: 'placeholder',
|
||||
classname: 'w-field w-field--char_field w-field--text_input',
|
||||
},
|
||||
),
|
||||
],
|
||||
],
|
||||
],
|
||||
{
|
||||
test_block_a: 'Block A options',
|
||||
},
|
||||
{
|
||||
label: '',
|
||||
required: true,
|
||||
icon: 'placeholder',
|
||||
classname: null,
|
||||
helpText: 'use <strong>plenty</strong> of this',
|
||||
helpIcon: '<svg></svg>',
|
||||
maxNum: null,
|
||||
minNum: null,
|
||||
blockCounts: {},
|
||||
strings: {
|
||||
MOVE_UP: 'Move up',
|
||||
MOVE_DOWN: 'Move down',
|
||||
DELETE: 'Delete',
|
||||
DUPLICATE: 'Duplicate',
|
||||
ADD: 'Add',
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Render it
|
||||
document.body.innerHTML = '<div id="placeholder"></div>';
|
||||
boundBlock = blockDef.render($('#placeholder'), 'the-prefix', []);
|
||||
});
|
||||
|
||||
test('it renders correctly without combobox', () => {
|
||||
expect(document.body.innerHTML).toMatchSnapshot();
|
||||
expect(document.querySelector('[role="listbox"]')).toBe(null);
|
||||
expect(boundBlock.children.length).toEqual(0);
|
||||
});
|
||||
|
||||
test('it can add block', () => {
|
||||
boundBlock.inserters[0].addButton.click();
|
||||
|
||||
expect(document.body.innerHTML).toMatchSnapshot();
|
||||
expect(boundBlock.children.length).toEqual(1);
|
||||
expect(boundBlock.children[0].type).toEqual('test_block_a');
|
||||
});
|
||||
});
|
||||
|
@ -923,3 +923,97 @@ exports[`telepath: wagtail.blocks.StreamBlock setError renders error messages 1`
|
||||
`;
|
||||
|
||||
exports[`telepath: wagtail.blocks.StreamBlock with labels that need escaping it renders correctly 1`] = `"<div class=\\"w-combobox__optgroup\\"><div role=\\"option\\" aria-selected=\\"false\\" id=\\"downshift-2-item-0\\" class=\\"w-combobox__option w-combobox__option--col1\\"><div class=\\"w-combobox__option-icon\\"><svg class=\\"icon icon-placeholder \\" aria-hidden=\\"true\\"><use href=\\"#icon-placeholder\\"></use></svg></div><div class=\\"w-combobox__option-text\\">Test Block <A></div></div><div role=\\"option\\" aria-selected=\\"false\\" id=\\"downshift-2-item-1\\" class=\\"w-combobox__option w-combobox__option--col2\\"><div class=\\"w-combobox__option-icon\\"><svg class=\\"icon icon-pilcrow \\" aria-hidden=\\"true\\"><use href=\\"#icon-pilcrow\\"></use></svg></div><div class=\\"w-combobox__option-text\\">Test Block <B></div></div></div>"`;
|
||||
|
||||
exports[`telepath: wagtail.blocks.StreamBlock with unique block type it can add block 1`] = `
|
||||
"<div class=\\"c-sf-help\\">
|
||||
<div class=\\"help\\">
|
||||
use <strong>plenty</strong> of this
|
||||
</div>
|
||||
</div><div class=\\"\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-count\\" data-streamfield-stream-count=\\"\\" value=\\"1\\">
|
||||
<div data-streamfield-stream-container=\\"\\"><div>
|
||||
<button type=\\"button\\" title=\\"Insert a block\\" class=\\"c-sf-add-button\\">
|
||||
<svg class=\\"icon icon-plus\\" aria-hidden=\\"true\\"><use href=\\"#icon-plus\\"></use></svg>
|
||||
</button>
|
||||
</div><div data-contentpath=\\"fake-uuid-v4-value\\" style=\\"display: none;\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-0-deleted\\" value=\\"\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-0-order\\" value=\\"0\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-0-type\\" value=\\"test_block_a\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-0-id\\" value=\\"fake-uuid-v4-value\\">
|
||||
<section class=\\"w-panel w-panel--nested\\" id=\\"block-fake-uuid-v4-value-section\\" aria-labelledby=\\"block-fake-uuid-v4-value-heading\\" data-panel=\\"\\">
|
||||
<div class=\\"w-panel__header\\">
|
||||
<a class=\\"w-panel__anchor w-panel__anchor--prefix\\" href=\\"#block-fake-uuid-v4-value-section\\" aria-labelledby=\\"block-fake-uuid-v4-value-heading\\" data-panel-anchor=\\"\\">
|
||||
<svg class=\\"icon icon-link w-panel__icon\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-link\\"></use>
|
||||
</svg>
|
||||
</a>
|
||||
<button class=\\"w-panel__toggle\\" type=\\"button\\" aria-label=\\"Toggle section\\" aria-describedby=\\"block-fake-uuid-v4-value-heading\\" data-panel-toggle=\\"\\" aria-controls=\\"block-fake-uuid-v4-value-content\\" aria-expanded=\\"true\\">
|
||||
<svg class=\\"icon icon-placeholder w-panel__icon\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-placeholder\\"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<h2 class=\\"w-panel__heading w-panel__heading--label\\" aria-level=\\"3\\" id=\\"block-fake-uuid-v4-value-heading\\" data-panel-heading=\\"\\">
|
||||
<span data-panel-heading-text=\\"\\" class=\\"c-sf-block__title\\"></span>
|
||||
<span class=\\"c-sf-block__type\\">Test Block A</span>
|
||||
<span class=\\"w-required-mark\\" data-panel-required=\\"\\">*</span>
|
||||
</h2>
|
||||
<a class=\\"w-panel__anchor w-panel__anchor--suffix\\" href=\\"#block-fake-uuid-v4-value-section\\" aria-labelledby=\\"block-fake-uuid-v4-value-heading\\">
|
||||
<svg class=\\"icon icon-link w-panel__icon\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-link\\"></use>
|
||||
</svg>
|
||||
</a>
|
||||
<div class=\\"w-panel__divider\\"></div>
|
||||
<div class=\\"w-panel__controls\\" data-panel-controls=\\"\\"><button type=\\"button\\" class=\\"button button--icon text-replace white\\" title=\\"Move up\\" disabled=\\"disabled\\">
|
||||
<svg class=\\"icon icon-arrow-up\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-arrow-up\\"></use>
|
||||
</svg>
|
||||
</button><button type=\\"button\\" class=\\"button button--icon text-replace white\\" title=\\"Move down\\" disabled=\\"disabled\\">
|
||||
<svg class=\\"icon icon-arrow-down\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-arrow-down\\"></use>
|
||||
</svg>
|
||||
</button><button type=\\"button\\" class=\\"button button--icon text-replace white\\" title=\\"Duplicate\\">
|
||||
<svg class=\\"icon icon-copy\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-copy\\"></use>
|
||||
</svg>
|
||||
</button><button type=\\"button\\" class=\\"button button--icon text-replace white\\" title=\\"Delete\\">
|
||||
<svg class=\\"icon icon-bin\\" aria-hidden=\\"true\\">
|
||||
<use href=\\"#icon-bin\\"></use>
|
||||
</svg>
|
||||
</button></div>
|
||||
</div>
|
||||
<div id=\\"block-fake-uuid-v4-value-content\\" class=\\"w-panel__content\\">
|
||||
<div class=\\"w-field__wrapper\\" data-field-wrapper=\\"\\">
|
||||
<div class=\\"w-field w-field--char_field w-field--text_input\\" data-field=\\"\\">
|
||||
<div class=\\"w-field__errors\\" id=\\"the-prefix-0-value-errors\\" data-field-errors=\\"\\">
|
||||
<svg class=\\"icon icon-warning w-field__errors-icon\\" aria-hidden=\\"true\\" hidden=\\"\\"><use href=\\"#icon-warning\\"></use></svg>
|
||||
</div>
|
||||
<div class=\\"w-field__help\\" id=\\"the-prefix-0-value-helptext\\" data-field-help=\\"\\"></div>
|
||||
<div class=\\"w-field__input\\" data-field-input=\\"\\">
|
||||
<p name=\\"the-prefix-0-value\\" id=\\"the-prefix-0-value\\">Block A widget</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div><div>
|
||||
<button type=\\"button\\" title=\\"Insert a block\\" class=\\"c-sf-add-button\\">
|
||||
<svg class=\\"icon icon-plus\\" aria-hidden=\\"true\\"><use href=\\"#icon-plus\\"></use></svg>
|
||||
</button>
|
||||
</div></div>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`telepath: wagtail.blocks.StreamBlock with unique block type it renders correctly without combobox 1`] = `
|
||||
"<div class=\\"c-sf-help\\">
|
||||
<div class=\\"help\\">
|
||||
use <strong>plenty</strong> of this
|
||||
</div>
|
||||
</div><div class=\\"\\">
|
||||
<input type=\\"hidden\\" name=\\"the-prefix-count\\" data-streamfield-stream-count=\\"\\" value=\\"0\\">
|
||||
<div data-streamfield-stream-container=\\"\\"><div>
|
||||
<button type=\\"button\\" title=\\"Insert a block\\" class=\\"c-sf-add-button\\">
|
||||
<svg class=\\"icon icon-plus\\" aria-hidden=\\"true\\"><use href=\\"#icon-plus\\"></use></svg>
|
||||
</button>
|
||||
</div></div>
|
||||
</div>"
|
||||
`;
|
||||
|
@ -39,6 +39,7 @@ Thank you to Damilola for his work, and to Google for sponsoring this project.
|
||||
* Add [`AbstractImage.get_renditions()`](image_renditions_multiple) for efficient generation of multiple renditions (Andy Babic)
|
||||
* Optimise queries in collection permission policies using cache on the user object (Sage Abdullah)
|
||||
* Phone numbers entered via a link chooser will now have any spaces stripped out, ensuring a valid `href="tel:..."` attribute (Sahil Jangra)
|
||||
* Auto-select the `StreamField` block when only one block type is declared (Sébastien Corbin)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user