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

Slug Controller - enhance default compare function

- Allow compareAs behaviour to be overridden via the event detail OR a param
- Enhance types to be more strict, refine JSDoc to be clearer about the default behaviour
- Add unit tests for default compare function and make logic a bit easier to read in regards to how the slugify/urilify methods are conditionally called
- Relates to #10532
This commit is contained in:
LB Johnston 2023-06-10 13:18:26 +10:00 committed by Matt Westcott
parent ccec24b888
commit 015c4339d7
2 changed files with 96 additions and 12 deletions

View File

@ -81,7 +81,7 @@ describe('compare behaviour', () => {
const slugInput = document.querySelector('#id_slug');
slugInput.dataset.action = [
'blur->w-slug#slugify',
'blur->w-slug#urlify',
'custom:event->w-slug#compare',
].join(' ');
});
@ -113,6 +113,76 @@ describe('compare behaviour', () => {
expect(event.preventDefault).not.toHaveBeenCalled();
});
it('should prevent default using the slugify (default) behaviour as the compare function when urlify values is not equal', () => {
const slug = document.querySelector('#id_slug');
const title = 'Тестовий заголовок';
slug.setAttribute('value', title);
// apply the urlify method to the content to ensure the value before check is urlify
slug.dispatchEvent(new Event('blur'));
expect(slug.value).toEqual('testovij-zagolovok');
const event = new CustomEvent('custom:event', { detail: { value: title } });
event.preventDefault = jest.fn();
slug.dispatchEvent(event);
// slugify used for the compareAs value by default, so 'compare' fails
expect(event.preventDefault).toHaveBeenCalled();
});
it('should not prevent default using the slugify (default) behaviour as the compare function when urlify value is equal', () => {
const slug = document.querySelector('#id_slug');
const title = 'the-french-dispatch-a-love-letter-to-journalists';
slug.setAttribute('value', title);
// apply the urlify method to the content to ensure the value before check is urlify
slug.dispatchEvent(new Event('blur'));
expect(slug.value).toEqual(
'the-french-dispatch-a-love-letter-to-journalists',
);
const event = new CustomEvent('custom:event', { detail: { value: title } });
event.preventDefault = jest.fn();
slug.dispatchEvent(event);
// slugify used for the compareAs value by default, so 'compare' passes with the initial urlify value on blur
expect(event.preventDefault).not.toHaveBeenCalled();
});
it('should not prevent default using the urlify behaviour as the compare function when urlify value matches', () => {
const title = 'Тестовий заголовок';
const slug = document.querySelector('#id_slug');
slug.setAttribute('data-w-slug-compare-as-param', 'urlify');
slug.setAttribute('value', title);
// apply the urlify method to the content to ensure the value before check is urlify
slug.dispatchEvent(new Event('blur'));
expect(slug.value).toEqual('testovij-zagolovok');
const event = new CustomEvent('custom:event', {
detail: { compareAs: 'urlify', value: title },
});
event.preventDefault = jest.fn();
slug.dispatchEvent(event);
expect(event.preventDefault).not.toHaveBeenCalled();
});
it('should prevent default if the values are not the same', () => {
document.querySelector('#id_slug').setAttribute('value', 'title-alpha');

View File

@ -1,6 +1,8 @@
import { Controller } from '@hotwired/stimulus';
import { cleanForSlug } from '../utils/text';
type SlugMethods = 'slugify' | 'urlify';
/**
* Adds ability to slugify the value of an input element.
*
@ -15,24 +17,30 @@ export class SlugController extends Controller<HTMLInputElement> {
declare allowUnicodeValue: boolean;
/**
* Allow for a comparison value to be provided, if does not compare to the
* current value (once transformed), then the event's default will
* be prevented.
* Allow for a comparison value to be provided so that a dispatched event can be
* prevented. This provides a way for other events to interact with this controller
* to block further updates if a value is not in sync.
* By default it will compare to the slugify method, this can be overridden by providing
* either a Stimulus param value on the element or the event's detail.
*/
compare(
event: CustomEvent<{ value: string }> & { params?: { compareAs?: string } },
event: CustomEvent<{ compareAs?: SlugMethods; value: string }> & {
params?: { compareAs?: SlugMethods };
},
) {
// do not attempt to compare if the current field is empty
if (!this.element.value) {
return true;
}
const {
detail: { value = '' } = {},
params: { compareAs = 'slugify' } = {},
} = event;
const compareAs =
event.detail?.compareAs || event.params?.compareAs || 'slugify';
const compareValue = this[compareAs](
{ detail: { value: event.detail?.value || '' } },
true,
);
const compareValue = this[compareAs]({ detail: { value } }, true);
const currentValue = this.element.value;
const valuesAreSame = compareValue.trim() === currentValue.trim();
@ -50,7 +58,10 @@ export class SlugController extends Controller<HTMLInputElement> {
* If a custom event with detail.value is provided, that value will be used
* instead of the field's value.
*/
slugify(event: CustomEvent<{ value: string }>, ignoreUpdate = false) {
slugify(
event: CustomEvent<{ value: string }> | { detail: { value: string } },
ignoreUpdate = false,
) {
const unicodeSlugsEnabled = this.allowUnicodeValue;
const { value = this.element.value } = event?.detail || {};
const newValue = cleanForSlug(value.trim(), false, { unicodeSlugsEnabled });
@ -68,7 +79,10 @@ export class SlugController extends Controller<HTMLInputElement> {
* If a custom event with detail.value is provided, that value will be used
* instead of the field's value.
*/
urlify(event: CustomEvent<{ value: string }>, ignoreUpdate = false) {
urlify(
event: CustomEvent<{ value: string }> | { detail: { value: string } },
ignoreUpdate = false,
) {
const unicodeSlugsEnabled = this.allowUnicodeValue;
const { value = this.element.value } = event?.detail || {};
const newValue = cleanForSlug(value.trim(), true, { unicodeSlugsEnabled });