diff --git a/packages/web-components/fast-foundation/docs/api-report.md b/packages/web-components/fast-foundation/docs/api-report.md index 956b79c7152..00fbea5e275 100644 --- a/packages/web-components/fast-foundation/docs/api-report.md +++ b/packages/web-components/fast-foundation/docs/api-report.md @@ -924,19 +924,19 @@ export class FASTCheckbox extends FASTCheckbox_base { // (undocumented) checked: boolean; checkedAttribute: boolean; - // (undocumented) - protected checkedAttributeChanged(prev: boolean | undefined, next: boolean): void; + // @internal + checkedAttributeChanged(prev: boolean | undefined, next: boolean): void; // (undocumented) checkedChanged(prev: boolean | undefined, next: boolean): void; // @internal - clickHandler: (e: Event) => void; + clickHandler(e: Event): void; currentChecked: boolean; // (undocumented) currentCheckedChanged(prev: boolean | undefined, next: boolean): void; // (undocumented) defaultChecked: boolean; - // (undocumented) - defaultCheckedChanged(): void; + // @internal + defaultCheckedChanged(prev: boolean | undefined, next: boolean): void; // @internal (undocumented) defaultSlottedNodes: Node[]; protected dirtyChecked: boolean; @@ -946,19 +946,20 @@ export class FASTCheckbox extends FASTCheckbox_base { // (undocumented) static formControlValidators: Validator[]; indeterminate: boolean | undefined; - // (undocumented) + // @internal indeterminateChanged(prev: boolean | undefined, next: boolean | undefined): void; initialValue: string; initialValueChanged(previous: string, next: string): void; // @internal - keypressHandler: (e: KeyboardEvent) => void; + keypressHandler(e: KeyboardEvent): void; name: string; required: boolean; // (undocumented) + requiredChanged(): void; + // (undocumented) resetFormControl(): void; // (undocumented) shouldFormValueUpdate(): boolean; - // (undocumented) value: string; // (undocumented) valueChanged(previous: string, next: string): void; diff --git a/packages/web-components/fast-foundation/src/checkbox/README.md b/packages/web-components/fast-foundation/src/checkbox/README.md index 3e66a014106..a00ca3d15e1 100644 --- a/packages/web-components/fast-foundation/src/checkbox/README.md +++ b/packages/web-components/fast-foundation/src/checkbox/README.md @@ -124,47 +124,45 @@ export const myCheckbox = Checkbox.compose({ | Name | Privacy | Type | Default | Description | Inherited From | | ------------------ | --------- | ---------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | +| `ariaChecked` | public | `string or null` | | | | +| `checked` | public | `boolean` | `false` | | | | `checkedAttribute` | public | `boolean` | `false` | The initial state of the checkbox. | | -| `defaultChecked` | public | `boolean` | | | | -| `initialValue` | public | `string` | `"on"` | The initial value of the form. This value sets the \`value\` property only when the \`value\` property has not been explicitly set. | | -| `value` | public | `string` | | | | | `currentChecked` | public | `boolean` | | The current checkedness of the element. This property serves as a mechanism to set the \`checked\` property through both property assignment and the .setAttribute() method. This is useful for setting the field's checkedness in UI libraries that bind data through the .setAttribute() API and don't support IDL attribute binding. | | +| `defaultChecked` | public | `boolean` | | | | | `dirtyChecked` | protected | `boolean` | `false` | Tracks whether the "checked" property has been changed. This is necessary to provide consistent behavior with normal input checkboxes | | -| `checked` | public | `boolean` | `false` | | | | `disabled` | public | `boolean` | `false` | Sets the element's disabled state. A disabled element will not be included during form submission. | | -| `ariaChecked` | public | `string or null` | | | | | `indeterminate` | public | `boolean or undefined` | | The indeterminate state of the control | | +| `initialValue` | public | `string` | `"on"` | The initial value of the form. This value sets the \`value\` property only when the \`value\` property has not been explicitly set. | | | `name` | public | `string` | | The name of the element. This element's value will be surfaced during form submission under the provided name. | | | `required` | public | `boolean` | `false` | The required state of the element. If true, the element will be required to complete the form. | | +| `value` | public | `string` | | The value of the element. This element's value will be surfaced during form submission under the provided name. | | #### Methods -| Name | Privacy | Description | Parameters | Return | Inherited From | -| ------------------------- | --------- | --------------------------------------------------- | -------------------------------------------------------- | --------- | -------------- | -| `checkedAttributeChanged` | protected | | `prev: boolean or undefined, next: boolean` | `void` | | -| `defaultCheckedChanged` | public | | | `void` | | -| `initialValueChanged` | public | Invoked when the \`initialValue\` property changes. | `previous: string, next: string` | `void` | | -| `valueChanged` | public | | `previous: string, next: string` | `void` | | -| `currentCheckedChanged` | public | | `prev: boolean or undefined, next: boolean` | | | -| `checkedChanged` | public | | `prev: boolean or undefined, next: boolean` | `void` | | -| `shouldFormValueUpdate` | | | | `boolean` | | -| `resetFormControl` | | | | `void` | | -| `indeterminateChanged` | public | | `prev: boolean or undefined, next: boolean or undefined` | `void` | | +| Name | Privacy | Description | Parameters | Return | Inherited From | +| ----------------------- | ------- | --------------------------------------------------- | ------------------------------------------- | --------- | -------------- | +| `checkedChanged` | public | | `prev: boolean or undefined, next: boolean` | `void` | | +| `currentCheckedChanged` | public | | `prev: boolean or undefined, next: boolean` | | | +| `initialValueChanged` | public | Invoked when the \`initialValue\` property changes. | `previous: string, next: string` | `void` | | +| `requiredChanged` | public | | | `void` | | +| `valueChanged` | public | | `previous: string, next: string` | `void` | | +| `resetFormControl` | | | | `void` | | +| `shouldFormValueUpdate` | | | | `boolean` | | #### Events -| Name | Type | Description | Inherited From | -| -------- | ---- | ---------------------------------------------------------- | -------------- | -| `change` | | Emits a custom change event when the checked state changes | | +| Name | Type | Description | Inherited From | +| -------- | ---- | --------------------------------------------------------------------------------------------------------------- | -------------- | +| `change` | | Emits a custom change event when the checked state changes {@inheritDoc @open-wc/form-control#FormControlMixin} | | #### Attributes | Name | Field | Inherited From | | ----------------- | ---------------- | -------------- | | `checked` | checkedAttribute | | -| `value` | initialValue | | | `current-checked` | currentChecked | | | | disabled | | +| `value` | initialValue | | | `name` | name | | | | required | | diff --git a/packages/web-components/fast-foundation/src/checkbox/checkbox.pw.spec.ts b/packages/web-components/fast-foundation/src/checkbox/checkbox.pw.spec.ts index a4414970bea..51457b9b597 100644 --- a/packages/web-components/fast-foundation/src/checkbox/checkbox.pw.spec.ts +++ b/packages/web-components/fast-foundation/src/checkbox/checkbox.pw.spec.ts @@ -252,6 +252,7 @@ test.describe("Checkbox", () => { ); // Playwright doesn't yet see our components as input elements + await expect(element).toHaveJSProperty("dirtyValue", false); await expect(element).toHaveJSProperty("value", initialValue); }); @@ -363,7 +364,7 @@ test.describe("Checkbox", () => { await expect(element).toHaveJSProperty("checked", false); }); - test("should set its checked property to true if the checked attribute is set", async () => { + test("should set its `checked` and `defaultChecked` property to true if the checked attribute is set", async () => { await root.evaluate(node => { node.innerHTML = /* html */ `
@@ -373,18 +374,21 @@ test.describe("Checkbox", () => { }); await expect(element).toHaveJSProperty("checked", false); + await expect(element).toHaveJSProperty("defaultChecked", false); await element.evaluate((node: FASTCheckbox) => { node.setAttribute("checked", ""); }); await expect(element).toHaveJSProperty("checked", true); + await expect(element).toHaveJSProperty("defaultChecked", true); await form.evaluate((node: HTMLFormElement) => { node.reset(); }); await expect(element).toHaveJSProperty("checked", true); + await expect(element).toHaveJSProperty("defaultChecked", true); }); test("should put the control into a clean state, where checked attribute modifications change the checked property prior to user or programmatic interaction", async () => { @@ -415,4 +419,81 @@ test.describe("Checkbox", () => { expect(await element.evaluate((node: FASTCheckbox) => node.value)).toBeTruthy(); }); + + test("should communicate if `checked` to the parent form", async () => { + await root.evaluate(node => { + node.innerHTML = /* html */ ` + + +
+ `; + }); + + const form = page.locator("form"); + + expect( + await form.evaluate((node: HTMLFormElement) => { + const formData = new FormData(node); + + return formData.get("test"); + }) + ).toBe("on"); + }); + + test("should not communicate when `disabled` and `checked` to the parent form", async () => { + await root.evaluate(node => { + node.innerHTML = /* html */ ` +
+ +
+ `; + }); + + const form = page.locator("form"); + + expect( + await form.evaluate((node: HTMLFormElement) => { + const formData = new FormData(node); + + return formData.get("test"); + }) + ).toBe(null); + }); + + test("should align the `checked` property and `current-checked` and `aria-checked` attribute changes", async () => { + await root.evaluate(node => { + node.innerHTML = /* html */ ` + + `; + }); + + const element = page.locator("fast-checkbox"); + + await expect(element).toHaveJSProperty("checked", false); + + await expect(element).toHaveAttribute("current-checked", "false"); + + await expect(element).toHaveAttribute("aria-checked", "false"); + + await element.evaluate((node: FASTCheckbox) => { + node.setAttribute("current-checked", "true"); + }); + + await expect(element).toHaveJSProperty("checked", true); + + await expect(element).toHaveAttribute("current-checked", "true"); + + await expect(element).toHaveAttribute("aria-checked", "true"); + + await element.evaluate((node: FASTCheckbox) => { + node.setAttribute("current-checked", "false"); + }); + + await expect(element).toHaveJSProperty("checked", false); + + await expect(element).toHaveAttribute("current-checked", "false"); + + await expect(element).toHaveAttribute("aria-checked", "false"); + }); + });