diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 13c2a0d5ad..47d41de9be 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -107,6 +107,7 @@ Changelog * Maintenance: Add better deprecation warnings to the `search.Query` & `search.QueryDailyHits` model, move final set of templates from the admin search module to the search promotions contrib module (LB (Ben) Johnston) * Maintenance: Add generic `InspectView` to `ModelViewSet` (Sage Abdullah) * Maintenance: Migrate select all on focus/click behavior to Stimulus, used on the image URL generator (Chiemezuo Akujobi) + * Maintenance: Add support for a `reset` method to support Stimulus driven dynamic field resets via the `w-action` controller (Chiemezuo Akujobi) 5.1.3 (xx.xx.20xx) - IN DEVELOPMENT ~~~~~~~~~~~~~~~~~~ diff --git a/client/src/controllers/ActionController.test.js b/client/src/controllers/ActionController.test.js index 01d0b32bd0..f9a4674b9e 100644 --- a/client/src/controllers/ActionController.test.js +++ b/client/src/controllers/ActionController.test.js @@ -199,4 +199,81 @@ describe('ActionController', () => { expect(textarea.selectionEnd).toBe(textarea.value.length); }); }); + + describe('reset method', () => { + const handleChangeEvent = jest.fn(); + document.addEventListener('change', handleChangeEvent); + + beforeEach(async () => { + jest.resetAllMocks(); + + await setup( + ``, + ); + }); + + it('should change value when existing value and new value are different', () => { + const input = document.getElementById('reset-test'); + + // Change the value to something else (via JS) + input.value = 'another input value'; + expect(handleChangeEvent).not.toHaveBeenCalled(); + + input.dispatchEvent( + new CustomEvent('some-event', { detail: { value: 'not the default' } }), + ); + + expect(input.value).toBe('not the default'); + expect(input.value).not.toBe('another input value'); + expect(handleChangeEvent).toHaveBeenCalled(); + }); + + it('should not change value when current value and new value are the same', () => { + expect(handleChangeEvent).not.toHaveBeenCalled(); + const input = document.getElementById('reset-test'); + + input.dispatchEvent( + new CustomEvent('some-event', { detail: { value: 'the default' } }), + ); + + expect(input.value).toBe('the default'); + expect(input.value).not.toBe('not the default'); + expect(handleChangeEvent).not.toHaveBeenCalled(); + }); + + it('should reset value to a new value supplied via custom event detail', () => { + expect(handleChangeEvent).not.toHaveBeenCalled(); + const input = document.getElementById('reset-test'); + + input.dispatchEvent( + new CustomEvent('some-event', { + detail: { value: 'a new value from custom event detail' }, + }), + ); + + expect(input.value).toBe('a new value from custom event detail'); + expect(input.value).not.toBe('the default'); + expect(handleChangeEvent).toHaveBeenCalled(); + }); + + it('should reset value to a new value supplied in action param', () => { + expect(handleChangeEvent).not.toHaveBeenCalled(); + const input = document.getElementById('reset-test'); + input.setAttribute( + 'data-w-action-value-param', + 'a new value from action params', + ); + + input.dispatchEvent(new CustomEvent('some-event')); + + expect(input.value).toBe('a new value from action params'); + expect(handleChangeEvent).toHaveBeenCalled(); + }); + }); }); diff --git a/client/src/controllers/ActionController.ts b/client/src/controllers/ActionController.ts index 8e17e92ee6..f6e7c4fff4 100644 --- a/client/src/controllers/ActionController.ts +++ b/client/src/controllers/ActionController.ts @@ -106,4 +106,31 @@ export class ActionController extends Controller< if (this.element instanceof HTMLButtonElement) return; this.element?.select(); } + + /** + * Reset the field to a supplied or the field's initial value (default). + * Only update if the value to change to is different from the current value. + */ + reset( + event: CustomEvent<{ value?: string }> & { params?: { value?: string } }, + ) { + const target = this.element; + const currentValue = target.value; + + const { value: newValue = '' } = { + value: target instanceof HTMLInputElement ? target.defaultValue : '', + ...event?.params, + ...event?.detail, + }; + + if (currentValue === newValue) return; + + target.value = newValue; + this.dispatch('change', { + bubbles: true, + cancelable: false, + prefix: '', + target, + }); + } } diff --git a/docs/releases/5.2.md b/docs/releases/5.2.md index 22e3d8cbe3..7156afa55e 100644 --- a/docs/releases/5.2.md +++ b/docs/releases/5.2.md @@ -129,6 +129,7 @@ depth: 1 * Add better deprecation warnings to the `search.Query` & `search.QueryDailyHits` model, move final set of templates from the admin search module to the search promotions contrib module (LB (Ben) Johnston) * Add generic `InspectView` to `ModelViewSet` (Sage Abdullah) * Migrate select all on focus/click behavior to Stimulus, used on the image URL generator (Chiemezuo Akujobi) + * Add support for a `reset` method to support Stimulus driven dynamic field resets via the `w-action` controller (Chiemezuo Akujobi) ## Upgrade considerations - changes affecting all projects