Browse Source

Add ability to set disabled element values

Add options for getFormValues, setFormValues, and clearFormValues to allow managing of values for disabled elements.
master
TheoryOfNekomata 1 year ago
parent
commit
0843513772
3 changed files with 66 additions and 12 deletions
  1. +0
    -4
      README.md
  2. +32
    -0
      cypress/integration/core.test.ts
  3. +34
    -8
      src/index.ts

+ 0
- 4
README.md View File

@@ -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 [usability table for `SubmitEvent.submitter`](https://caniuse.com/mdn-api_submitevent_submitter) to check if your target
browser is supported. 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). The sources are under the [MIT license](https://code.modal.sh/TheoryOfNekomata/formxtra/raw/branch/master/LICENSE).

+ 32
- 0
cypress/integration/core.test.ts View File

@@ -336,6 +336,7 @@ describe('misc', () => {
<body> <body>
<form> <form>
<input type="text" name="foobar" /> <input type="text" name="foobar" />
<input type="text" name="disabled" disabled />
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
</body> </body>
@@ -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', () => {
<input type="radio" name="foo" value="value1" checked /> <input type="radio" name="foo" value="value1" checked />
<input type="radio" name="foo" value="value2" /> <input type="radio" name="foo" value="value2" />
<input type="radio" name="foo" value="value3" /> <input type="radio" name="foo" value="value3" />
<input type="text" name="disabled" value="disabled" disabled />
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
</body> </body>
@@ -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', () => { describe('duplicates', () => {


+ 34
- 8
src/index.ts View File

@@ -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. * Determines if an element's value is included when its form is submitted.
* @param el - The element. * @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. * @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<string, unknown>; const namedEl = el as unknown as Record<string, unknown>;
return ( return (
typeof namedEl[ATTRIBUTE_NAME] === 'string' typeof namedEl[ATTRIBUTE_NAME] === 'string'
&& namedEl[ATTRIBUTE_NAME].length > 0 && namedEl[ATTRIBUTE_NAME].length > 0
&& namedEl[ATTRIBUTE_NAME] !== NAME_ATTRIBUTE_VALUE_ISINDEX && 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) && 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. * Options for getting form values.
*/ */
type GetFormValuesOptions = GetFieldValueOptions & {
type GetFormValuesOptions = FormValuesOptions & GetFieldValueOptions & {
/** /**
* The element that triggered the submission of the form. * 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. * Filters the form elements that can be processed.
* @param form - The form element. * @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. * @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<string | number, HTMLElement>; const formElements = form.elements as unknown as Record<string | number, HTMLElement>;
const allFormFieldElements = Object.entries<HTMLElement>(formElements); const allFormFieldElements = Object.entries<HTMLElement>(formElements);
return allFormFieldElements.filter(([k, el]) => ( return allFormFieldElements.filter(([k, el]) => (
@@ -1091,7 +1103,7 @@ const filterFieldElements = (form: HTMLFormElement) => {
!Number.isNaN(Number(k)) !Number.isNaN(Number(k))


// Only the enabled/read-only elements can be enumerated. // Only the enabled/read-only elements can be enumerated.
&& isElementValueIncludedInFormSubmit(el)
&& isElementValueIncludedInFormSubmit(el, includeDisabled)
)) as [string, HTMLElementWithName][]; )) as [string, HTMLElementWithName][];
}; };


@@ -1104,7 +1116,7 @@ const filterFieldElements = (form: HTMLFormElement) => {
export const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptions) => { export const getFormValues = (form: HTMLFormElement, options = {} as GetFormValuesOptions) => {
assertIsFormElement(form, 'getFormValues'); assertIsFormElement(form, 'getFormValues');


const fieldElements = filterFieldElements(form);
const fieldElements = filterFieldElements(form, Boolean(options.includeDisabled));
const fieldValues = fieldElements.reduce( const fieldValues = fieldElements.reduce(
(theFormValues, [, el]) => { (theFormValues, [, el]) => {
const fieldValue = getValue(el, options); 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 * 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. * may be passed to set values only to certain form fields.
* @param form - The form. * @param form - The form.
* @param values - The form values. * @param values - The form values.
* @param options - The options.
*/ */
export const setFormValues = ( export const setFormValues = (
form: HTMLFormElement, form: HTMLFormElement,
values: unknown, values: unknown,
options = {} as SetFormValuesOptions,
) => { ) => {
assertIsFormElement(form, 'getFormValues'); assertIsFormElement(form, 'getFormValues');
const valuesType = typeof values; const valuesType = typeof values;
@@ -1258,12 +1277,17 @@ export const setFormValues = (
} }


const objectValues = normalizeValues(values); 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 filteredFieldElements = fieldElements.filter(([, el]) => el.name in objectValues);
const elementsWithSameName = getElementsOfSameName(filteredFieldElements); const elementsWithSameName = getElementsOfSameName(filteredFieldElements);
doSetFormValues(filteredFieldElements, elementsWithSameName, objectValues); 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 * 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. * values may be passed to set values only to certain form fields.
@@ -1272,14 +1296,16 @@ export const setFormValues = (
* *
* @param form - The form. * @param form - The form.
* @param fieldNames - The field names to clear their corresponding element(s). * @param fieldNames - The field names to clear their corresponding element(s).
* @param options - The options.
*/ */
export const clearFormValues = ( export const clearFormValues = (
form: HTMLFormElement, form: HTMLFormElement,
fieldNames: string | string[], fieldNames: string | string[],
options = {} as ClearFormValuesOptions,
) => { ) => {
assertIsFormElement(form, 'clearFormValues'); assertIsFormElement(form, 'clearFormValues');
const fieldNamesNormalized = Array.isArray(fieldNames) ? fieldNames : [fieldNames]; const fieldNamesNormalized = Array.isArray(fieldNames) ? fieldNames : [fieldNames];
const fieldElements = filterFieldElements(form);
const fieldElements = filterFieldElements(form, Boolean(options.includeDisabled));
const filteredFieldElements = fieldElements.filter( const filteredFieldElements = fieldElements.filter(
([, el]) => fieldNamesNormalized.includes(el.name), ([, el]) => fieldNamesNormalized.includes(el.name),
); );


Loading…
Cancel
Save