kopia lustrzana https://github.com/wagtail/wagtail
Add getTextLabel handling to all default bound widget classes
rodzic
08018d565b
commit
b1b7b6e696
|
|
@ -34,7 +34,8 @@ Changelog
|
|||
* Add a keyboard shortcut to easily toggle the visibility of the minimap side panel (Dhruvi Patel)
|
||||
* Add API for extracting preview page content (Sage Abdullah)
|
||||
* Add toggle from grid to list layout for image listings (Joel William)
|
||||
* Add support for opt-in collapsible `StructBlock`s (Sage Abdullah)
|
||||
* Make `StructBlock`s collapsible when nested to support block settings (Sage Abdullah)
|
||||
* Improve `label_format` support for more widget types in StreamField (Sage Abdullah)
|
||||
* Add `form_attrs` support to all StreamField blocks (Sage Abdullah)
|
||||
* Update project template documentation to include testing instructions and include starting test file in template (Aditya (megatrron))
|
||||
* Add support for `preview_value` and `default` in `Block` meta options as callables for dynamic previews within StreamField (Ziyao Yan, Sage Abdullah)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { setAttrs } from '../../utils/attrs';
|
||||
import { gettext } from '../../utils/gettext';
|
||||
import { runInlineScripts } from '../../utils/runInlineScripts';
|
||||
|
||||
/**
|
||||
|
|
@ -70,14 +71,20 @@ export class BoundWidget {
|
|||
}
|
||||
}
|
||||
|
||||
getValueForLabel() {
|
||||
return this.getValue();
|
||||
}
|
||||
|
||||
getTextLabel(opts) {
|
||||
const val = this.getValue();
|
||||
if (typeof val !== 'string') return null;
|
||||
const val = this.getValueForLabel();
|
||||
const allowedTypes = ['string', 'number', 'boolean'];
|
||||
if (!allowedTypes.includes(typeof val)) return null;
|
||||
const valString = String(val).trim();
|
||||
const maxLength = opts && opts.maxLength;
|
||||
if (maxLength && val.length > maxLength) {
|
||||
return val.substring(0, maxLength - 1) + '…';
|
||||
if (maxLength && valString.length > maxLength) {
|
||||
return valString.substring(0, maxLength - 1) + '…';
|
||||
}
|
||||
return val;
|
||||
return valString;
|
||||
}
|
||||
|
||||
focus() {
|
||||
|
|
@ -156,6 +163,10 @@ export class BoundCheckboxInput extends BoundWidget {
|
|||
setState(state) {
|
||||
this.input.checked = state;
|
||||
}
|
||||
|
||||
getValueForLabel() {
|
||||
return this.getValue() ? gettext('Yes') : gettext('No');
|
||||
}
|
||||
}
|
||||
|
||||
export class CheckboxInput extends Widget {
|
||||
|
|
@ -173,6 +184,27 @@ export class BoundRadioSelect {
|
|||
this.selector = `input[name="${name}"]:checked`;
|
||||
}
|
||||
|
||||
getValueForLabel() {
|
||||
const getLabels = (input) => {
|
||||
const labels = Array.from(input?.labels || [])
|
||||
.map((label) => label.textContent.trim())
|
||||
.filter(Boolean);
|
||||
return labels.join(', ');
|
||||
};
|
||||
if (this.isMultiple) {
|
||||
return Array.from(this.element.querySelectorAll(this.selector))
|
||||
.map(getLabels)
|
||||
.join(', ');
|
||||
}
|
||||
return getLabels(this.element.querySelector(this.selector));
|
||||
}
|
||||
|
||||
getTextLabel() {
|
||||
// This class does not extend BoundWidget, so we don't have the truncating
|
||||
// logic without duplicating the code here. Skip it for now.
|
||||
return this.getValueForLabel();
|
||||
}
|
||||
|
||||
getValue() {
|
||||
if (this.isMultiple) {
|
||||
return Array.from(this.element.querySelectorAll(this.selector)).map(
|
||||
|
|
@ -217,7 +249,7 @@ export class RadioSelect extends Widget {
|
|||
}
|
||||
|
||||
export class BoundSelect extends BoundWidget {
|
||||
getTextLabel() {
|
||||
getValueForLabel() {
|
||||
return Array.from(this.input.selectedOptions)
|
||||
.map((option) => option.text)
|
||||
.join(', ');
|
||||
|
|
|
|||
|
|
@ -238,6 +238,20 @@ describe('RadioSelect', () => {
|
|||
).toBeNull();
|
||||
});
|
||||
|
||||
test('getTextLabel() returns the text of selected option', () => {
|
||||
expect(boundWidget.getTextLabel()).toBe('Tea');
|
||||
});
|
||||
|
||||
test('getTextLabel() safely handles input with no labels', () => {
|
||||
// Disassociate the label from the input by removing the "for" attribute
|
||||
// and moving the input outside of the label.
|
||||
const label = document.querySelector('label[for="the-id_0"]');
|
||||
label.removeAttribute('for');
|
||||
const input = document.querySelector('input[value="tea"]');
|
||||
document.body.appendChild(input);
|
||||
expect(boundWidget.getTextLabel()).toBe('');
|
||||
});
|
||||
|
||||
test('focus() focuses the first element', () => {
|
||||
boundWidget.focus();
|
||||
|
||||
|
|
@ -299,6 +313,20 @@ describe('RadioSelect for CheckboxSelectMultiple', () => {
|
|||
expect(document.querySelector('input[value="green"]').checked).toBe(true);
|
||||
expect(document.querySelector('input[value="blue"]').checked).toBe(false);
|
||||
});
|
||||
|
||||
test('getTextLabel() returns the text of selected options', () => {
|
||||
expect(boundWidget.getTextLabel()).toBe('Red, Blue');
|
||||
});
|
||||
|
||||
test('getTextLabel() safely handles input with no labels', () => {
|
||||
// Disassociate the label from the input by removing the "for" attribute
|
||||
// and moving the input outside of the label.
|
||||
const label = document.querySelector('label[for="the-id_0"]');
|
||||
label.removeAttribute('for');
|
||||
const input = document.querySelector('input[value="red"]');
|
||||
document.body.appendChild(input);
|
||||
expect(boundWidget.getTextLabel()).toBe('Blue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('CheckboxInput', () => {
|
||||
|
|
@ -338,6 +366,12 @@ describe('CheckboxInput', () => {
|
|||
expect(document.querySelector('input[id="id-sugar"]').checked).toBe(true);
|
||||
});
|
||||
|
||||
test('getTextLabel() returns a human-readable value', () => {
|
||||
expect(boundWidget.getTextLabel()).toBe('Yes');
|
||||
boundWidget.setState(false);
|
||||
expect(boundWidget.getTextLabel()).toBe('No');
|
||||
});
|
||||
|
||||
test('focus() focuses the checkbox', () => {
|
||||
boundWidget.focus();
|
||||
|
||||
|
|
@ -377,8 +411,9 @@ describe('Select', () => {
|
|||
expect(selectedOptions[0].value).toBe('1');
|
||||
});
|
||||
|
||||
test('getTextLabel() returns the text of selected option', () => {
|
||||
test('getTextLabel() returns the truncated text of selected option', () => {
|
||||
expect(boundWidget.getTextLabel()).toBe('Option 1');
|
||||
expect(boundWidget.getTextLabel({ maxLength: 6 })).toBe('Optio…');
|
||||
});
|
||||
|
||||
test('getValue() returns the current value', () => {
|
||||
|
|
@ -429,8 +464,9 @@ describe('Select multiple', () => {
|
|||
expect(selectedOptions[1].value).toBe('blue');
|
||||
});
|
||||
|
||||
test('getTextLabel() returns the text of selected options', () => {
|
||||
test('getTextLabel() returns the truncated text of selected options', () => {
|
||||
expect(boundWidget.getTextLabel()).toBe('Red, Blue');
|
||||
expect(boundWidget.getTextLabel({ maxLength: 6 })).toBe('Red, …');
|
||||
});
|
||||
|
||||
test('getValue() returns the current values', () => {
|
||||
|
|
|
|||
|
|
@ -51,8 +51,10 @@ class SettingsBlock(blocks.StructBlock):
|
|||
|
||||
class Meta:
|
||||
icon = 'cog'
|
||||
collapsed = True # This block will be initially collapsed
|
||||
label_format = "Settings (Theme: {theme})" # The label when collapsed
|
||||
# This block will be initially collapsed
|
||||
collapsed = True
|
||||
# The label when the block is collapsed
|
||||
label_format = "Settings (Theme: {theme}, Available: {available})"
|
||||
|
||||
|
||||
class PersonBlock(blocks.StructBlock):
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@ For more details, see the documentation on [enabling the user bar](headless_user
|
|||
* Add a keyboard shortcut to easily toggle the visibility of the minimap side panel (Dhruvi Patel)
|
||||
* Add API for extracting preview page content (Sage Abdullah)
|
||||
* Add `form_attrs` support to all StreamField blocks (Sage Abdullah)
|
||||
* Improve `label_format` support for more widget types in StreamField (Sage Abdullah)
|
||||
* Update project template documentation to include testing instructions and include starting test file in template (Aditya (megatrron))
|
||||
* Add support for `preview_value` and `default` in `Block` meta options as callables for [dynamic previews within StreamField](configuring_block_previews) (Ziyao Yan, Sage Abdullah)
|
||||
* Provide client-side access to the editing form panel structure (Matt Westcott)
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue