From 084351377226f8f03b7c0e71f49a613539b47bbd Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sat, 25 Mar 2023 09:49:07 +0800 Subject: [PATCH] Add ability to set disabled element values Add options for getFormValues, setFormValues, and clearFormValues to allow managing of values for disabled elements. --- README.md | 4 --- cypress/integration/core.test.ts | 32 ++++++++++++++++++++++++ src/index.ts | 42 ++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index f37eb4d..1f3a642 100644 --- a/README.md +++ b/README.md @@ -172,8 +172,4 @@ add values to the form (such as specifying which action to take for the rest of [usability table for `SubmitEvent.submitter`](https://caniuse.com/mdn-api_submitevent_submitter) to check if your target browser is supported. -Setting form values of disabled elements is not supported by design, as `formxtra` is made for submittable form -elements. If a disabled element's value needs to be changed somehow, the form instance is already available, and the -element to be looked up is accessible via `HTMLFormElement.elements`. - The sources are under the [MIT license](https://code.modal.sh/TheoryOfNekomata/formxtra/raw/branch/master/LICENSE). diff --git a/cypress/integration/core.test.ts b/cypress/integration/core.test.ts index cee2013..1c960fd 100644 --- a/cypress/integration/core.test.ts +++ b/cypress/integration/core.test.ts @@ -336,6 +336,7 @@ describe('misc', () => {
+
@@ -409,6 +410,23 @@ describe('misc', () => { }, }) }); + + it('should allow setting values for disabled elements', () => { + utils.test({ + querySubmitter: (cy: any) => cy.get('[type="submit"]'), + onSubmitted: (form: HTMLFormElement, submitter: any, search: any) => { + let isThrown = false; + try { + setFormValues(form, { foobar: 'baz', disabled: 'new value' }, { includeDisabled: true }); + } catch (e) { + isThrown = true; + } + + expect(isThrown).toBe(false); + expect(getFormValues(form, { includeDisabled: true })).toEqual({ foobar: 'baz', disabled: 'new value', }); + }, + }) + }); }); @@ -430,6 +448,7 @@ describe('misc', () => { + @@ -455,6 +474,19 @@ describe('misc', () => { }, }); }); + + it('should clear all values given disabled option', () => { + utils.test({ + querySubmitter: (cy: any) => cy.get('[type="submit"]'), + onSubmitted: (form: HTMLFormElement, submitter: any, search: any) => { + clearFormValues(form, ['foobar', 'bar', 'foo', 'disabled'], { includeDisabled: true }); + expect(getFormValues(form, { includeDisabled: true })).toEqual({ + foobar: ['', ''], baz: 'value3', bar: ['', ''], + disabled: '', + }); + }, + }); + }); }); describe('duplicates', () => { diff --git a/src/index.ts b/src/index.ts index 9c2b8de..dc0781c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1021,23 +1021,34 @@ const NAME_ATTRIBUTE_VALUE_ISINDEX = 'isindex' as const; /** * Determines if an element's value is included when its form is submitted. * @param el - The element. + * @param includeDisabled - Should we include disabled field elements? * @returns Value determining if the element's value is included when its form is submitted. */ -export const isElementValueIncludedInFormSubmit = (el: HTMLElement) => { +export const isElementValueIncludedInFormSubmit = (el: HTMLElement, includeDisabled = false) => { const namedEl = el as unknown as Record; return ( typeof namedEl[ATTRIBUTE_NAME] === 'string' && namedEl[ATTRIBUTE_NAME].length > 0 && namedEl[ATTRIBUTE_NAME] !== NAME_ATTRIBUTE_VALUE_ISINDEX - && !(ATTRIBUTE_DISABLED in namedEl && Boolean(namedEl[ATTRIBUTE_DISABLED])) + && (includeDisabled || !(ATTRIBUTE_DISABLED in namedEl && Boolean(namedEl[ATTRIBUTE_DISABLED]))) && isFieldElement(namedEl as unknown as HTMLElement) ); }; +/** + * Options for all form value functions. + */ +type FormValuesOptions = { + /** + * Should we include disabled field elements? + */ + includeDisabled?: true, +} + /** * Options for getting form values. */ -type GetFormValuesOptions = GetFieldValueOptions & { +type GetFormValuesOptions = FormValuesOptions & GetFieldValueOptions & { /** * The element that triggered the submission of the form. */ @@ -1081,9 +1092,10 @@ const assertIsFormElement = (maybeForm: unknown, context: string) => { /** * Filters the form elements that can be processed. * @param form - The form element. + * @param includeDisabled - Should we include disabled field elements? * @returns Array of key-value pairs for the field names and field elements. */ -const filterFieldElements = (form: HTMLFormElement) => { +const filterFieldElements = (form: HTMLFormElement, includeDisabled = false) => { const formElements = form.elements as unknown as Record; const allFormFieldElements = Object.entries(formElements); return allFormFieldElements.filter(([k, el]) => ( @@ -1091,7 +1103,7 @@ const filterFieldElements = (form: HTMLFormElement) => { !Number.isNaN(Number(k)) // Only the enabled/read-only elements can be enumerated. - && isElementValueIncludedInFormSubmit(el) + && isElementValueIncludedInFormSubmit(el, includeDisabled) )) as [string, HTMLElementWithName][]; }; @@ -1104,7 +1116,7 @@ const filterFieldElements = (form: HTMLFormElement) => { export const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptions) => { assertIsFormElement(form, 'getFormValues'); - const fieldElements = filterFieldElements(form); + const fieldElements = filterFieldElements(form, Boolean(options.includeDisabled)); const fieldValues = fieldElements.reduce( (theFormValues, [, el]) => { const fieldValue = getValue(el, options); @@ -1236,15 +1248,22 @@ const getElementsOfSameName = (fieldElementEntries: [string, HTMLElementWithName ) ); +/** + * Options for setting form values. + */ +type SetFormValuesOptions = FormValuesOptions; + /** * Sets the values of all the fields within the form through accessing the DOM nodes. Partial values * may be passed to set values only to certain form fields. * @param form - The form. * @param values - The form values. + * @param options - The options. */ export const setFormValues = ( form: HTMLFormElement, values: unknown, + options = {} as SetFormValuesOptions, ) => { assertIsFormElement(form, 'getFormValues'); const valuesType = typeof values; @@ -1258,12 +1277,17 @@ export const setFormValues = ( } const objectValues = normalizeValues(values); - const fieldElements = filterFieldElements(form); + const fieldElements = filterFieldElements(form, Boolean(options.includeDisabled)); const filteredFieldElements = fieldElements.filter(([, el]) => el.name in objectValues); const elementsWithSameName = getElementsOfSameName(filteredFieldElements); doSetFormValues(filteredFieldElements, elementsWithSameName, objectValues); }; +/** + * Options for clearing form values. + */ +type ClearFormValuesOptions = FormValuesOptions; + /** * Clears the values of all the fields within the form through accessing the DOM nodes. Partial * values may be passed to set values only to certain form fields. @@ -1272,14 +1296,16 @@ export const setFormValues = ( * * @param form - The form. * @param fieldNames - The field names to clear their corresponding element(s). + * @param options - The options. */ export const clearFormValues = ( form: HTMLFormElement, fieldNames: string | string[], + options = {} as ClearFormValuesOptions, ) => { assertIsFormElement(form, 'clearFormValues'); const fieldNamesNormalized = Array.isArray(fieldNames) ? fieldNames : [fieldNames]; - const fieldElements = filterFieldElements(form); + const fieldElements = filterFieldElements(form, Boolean(options.includeDisabled)); const filteredFieldElements = fieldElements.filter( ([, el]) => fieldNamesNormalized.includes(el.name), );