0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-29 01:22:07 +01:00

Auto-select single block in StreamField

- Closes #3998
This commit is contained in:
SebCorbin 2023-06-27 12:14:43 +02:00 committed by LB (Ben Johnston)
parent c37847a6fb
commit d52484f524
5 changed files with 188 additions and 3 deletions

View File

@ -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)

View File

@ -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]}

View File

@ -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');
});
});

View File

@ -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 &lt;A&gt;</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 &lt;B&gt;</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>"
`;

View File

@ -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