diff --git a/src/liquid/components/ld-toggle/ld-toggle.tsx b/src/liquid/components/ld-toggle/ld-toggle.tsx index 4d5e85c147..4ba8912c0f 100644 --- a/src/liquid/components/ld-toggle/ld-toggle.tsx +++ b/src/liquid/components/ld-toggle/ld-toggle.tsx @@ -1,4 +1,4 @@ -import { Component, Element, h, Host, Method, Prop } from '@stencil/core' +import { Component, Element, h, Host, Method, Prop, Watch } from '@stencil/core' import { getClassNames } from '../../utils/getClassNames' /** @@ -18,6 +18,7 @@ import { getClassNames } from '../../utils/getClassNames' export class LdToggle implements InnerFocusable { @Element() element: HTMLElement private input: HTMLInputElement + private hiddenInput: HTMLInputElement private hasIcons: boolean /** Size of the toggle. */ @@ -26,6 +27,12 @@ export class LdToggle implements InnerFocusable { /** Disabled state of the toggle. */ @Prop() disabled: boolean + /** Used to specify the name of the control. */ + @Prop() name: string + + /** The input value. */ + @Prop() value: string + /** Alternative disabled state that keeps element focusable */ @Prop() ariaDisabled: string @@ -33,7 +40,7 @@ export class LdToggle implements InnerFocusable { @Prop({ mutable: true, reflect: true }) checked: boolean /** Set this property to `true` in order to mark the toggle as required. */ - @Prop() required: false + @Prop() required: boolean /** * Sets focus on the toggle @@ -45,10 +52,42 @@ export class LdToggle implements InnerFocusable { } } + @Watch('checked') + @Watch('name') + @Watch('required') + @Watch('value') + updateHiddenInput() { + if (this.hiddenInput) { + this.hiddenInput.checked = this.checked + this.hiddenInput.name = this.checked && this.name ? this.name : '' + this.hiddenInput.required = this.required + this.hiddenInput.value = this.value ?? (this.checked ? 'on' : '') + } + } + componentWillLoad() { this.hasIcons = !!this.element.querySelector('[slot="icon-start"]') || !!this.element.querySelector('[slot="icon-end"]') + + if (this.element.closest('form')) { + this.hiddenInput = document.createElement('input') + this.hiddenInput.required = this.required + this.hiddenInput.type = 'hidden' + + if (this.value || this.checked) { + this.hiddenInput.value = this.value ?? 'on' + } + if (this.checked) { + this.hiddenInput.checked = true + + if (this.name) { + this.hiddenInput.name = this.name + } + } + + this.element.appendChild(this.hiddenInput) + } } private handleBlur = (ev: FocusEvent) => { diff --git a/src/liquid/components/ld-toggle/readme.md b/src/liquid/components/ld-toggle/readme.md index 9d31428d93..126ae80adf 100644 --- a/src/liquid/components/ld-toggle/readme.md +++ b/src/liquid/components/ld-toggle/readme.md @@ -379,6 +379,7 @@ Please refer to the [ld-label](components/ld-label/) docs for more information o {% example %} +
I have read the terms of service.* @@ -392,7 +393,7 @@ Please refer to the [ld-label](components/ld-label/) docs for more information o You may unsubscribe at any given time.
- +
@@ -443,6 +444,7 @@ Please refer to the [ld-label](components/ld-label/) docs for more information o | `checked` | `checked` | The input value. | `boolean` | `undefined` | | `disabled` | `disabled` | Disabled state of the toggle. | `boolean` | `undefined` | | `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` | +| `name` | `name` | Used to specify the name of the control. | `string` | `undefined` | | `ref` | `ref` | reference to component | `any` | `undefined` | | `required` | `required` | Set this property to `true` in order to mark the toggle as required. | `boolean` | `undefined` | | `size` | `size` | Size of the toggle. | `"lg" \| "sm"` | `undefined` | diff --git a/src/liquid/components/ld-toggle/test/__snapshots__/ld-toggle.spec.ts.snap b/src/liquid/components/ld-toggle/test/__snapshots__/ld-toggle.spec.ts.snap index ec09b7826e..aed4872d36 100644 --- a/src/liquid/components/ld-toggle/test/__snapshots__/ld-toggle.spec.ts.snap +++ b/src/liquid/components/ld-toggle/test/__snapshots__/ld-toggle.spec.ts.snap @@ -1,5 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`ld-toggle creates hidden input field, if inside a form 1`] = ` + + + + + + + +`; + exports[`ld-toggle prevents input value changes with an aria-disabled attribute 1`] = ` diff --git a/src/liquid/components/ld-toggle/test/ld-toggle.spec.ts b/src/liquid/components/ld-toggle/test/ld-toggle.spec.ts index 50e4fbdc46..1d015f84db 100644 --- a/src/liquid/components/ld-toggle/test/ld-toggle.spec.ts +++ b/src/liquid/components/ld-toggle/test/ld-toggle.spec.ts @@ -118,4 +118,75 @@ describe('ld-toggle', () => { expect(input.focus).toHaveBeenCalled() }) + + it('creates hidden input field, if inside a form', async () => { + const { root } = await newSpecPage({ + components: [LdToggle], + html: `
`, + }) + expect(root).toMatchSnapshot() + }) + + it('sets initial state on hidden input', async () => { + const { root } = await newSpecPage({ + components: [LdToggle], + html: `
`, + }) + expect(root.querySelector('input')).toHaveProperty('checked', true) + expect(root.querySelector('input')).toHaveProperty('name', 'example') + expect(root.querySelector('input')).toHaveProperty('required', true) + expect(root.querySelector('input')).toHaveProperty('value', 'on') + }) + + it('updates hidden input field', async () => { + const { root, waitForChanges } = await newSpecPage({ + components: [LdToggle], + html: `
`, + }) + + root.setAttribute('name', 'test') + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('value', '') + expect(root.querySelector('input')).toHaveProperty('checked', false) + expect(root.querySelector('input')).toHaveProperty('required', false) + expect(root.querySelector('input')).toHaveProperty('name', '') + + root.dispatchEvent(new Event('click')) + root.setAttribute('required', '') + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('checked', true) + expect(root.querySelector('input')).toHaveProperty('required', true) + expect(root.querySelector('input')).toHaveProperty('value', 'on') + + root.setAttribute('name', '') + root.setAttribute('value', 'test') + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('value', 'test') + expect(root.querySelector('input')).toHaveProperty('checked', true) + expect(root.querySelector('input')).toHaveProperty('name', '') + + root.setAttribute('name', 'test') + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('value', 'test') + expect(root.querySelector('input')).toHaveProperty('checked', true) + expect(root.querySelector('input')).toHaveProperty('name', 'test') + + root.setAttribute('value', '') + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('value', '') + expect(root.querySelector('input')).toHaveProperty('checked', true) + expect(root.querySelector('input')).toHaveProperty('name', 'test') + + root.dispatchEvent(new Event('click')) + await waitForChanges() + + expect(root.querySelector('input')).toHaveProperty('value', '') + expect(root.querySelector('input')).toHaveProperty('checked', false) + expect(root.querySelector('input')).toHaveProperty('name', '') + }) })