瀏覽代碼

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 年之前
父節點
當前提交
0843513772
共有 3 個文件被更改,包括 66 次插入12 次删除
  1. +0
    -4
      README.md
  2. +32
    -0
      cypress/integration/core.test.ts
  3. +34
    -8
      src/index.ts

+ 0
- 4
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).

+ 32
- 0
cypress/integration/core.test.ts 查看文件

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


+ 34
- 8
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<string, unknown>;
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<string | number, HTMLElement>;
const allFormFieldElements = Object.entries<HTMLElement>(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),
);


Loading…
取消
儲存