Skip to content

Commit

Permalink
feat(ld-toggle): hidden input inside form
Browse files Browse the repository at this point in the history
  • Loading branch information
renet authored and borisdiakur committed Oct 25, 2021
1 parent e2eb244 commit 43cb8e6
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 3 deletions.
43 changes: 41 additions & 2 deletions src/liquid/components/ld-toggle/ld-toggle.tsx
Original file line number Diff line number Diff line change
@@ -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'

/**
Expand All @@ -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. */
Expand All @@ -26,14 +27,20 @@ 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

/** The input value. */
@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
Expand All @@ -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) => {
Expand Down
4 changes: 3 additions & 1 deletion src/liquid/components/ld-toggle/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ Please refer to the [ld-label](components/ld-label/) docs for more information o

{% example %}

<form>
<div style="display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); width: 100%">
<ld-label position="right" size="m">
I have read the terms of service.*
Expand All @@ -392,7 +393,7 @@ Please refer to the [ld-label](components/ld-label/) docs for more information o
<ld-input-message mode="info">You may unsubscribe at any given time.</ld-input-message>
</ld-label>
</div>

</form>
<!-- CSS component -->

<div style="display: grid; gap: 2rem; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); width: 100%">
Expand Down Expand Up @@ -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` |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ld-toggle creates hidden input field, if inside a form 1`] = `
<ld-toggle class="ld-toggle">
<mock:shadow-root>
<input part="input focusable" type="checkbox">
<span class="ld-toggle__knob" part="knob"></span>
</mock:shadow-root>
<input type="hidden">
</ld-toggle>
`;
exports[`ld-toggle prevents input value changes with an aria-disabled attribute 1`] = `
<ld-toggle aria-disabled="true" class="ld-toggle">
<mock:shadow-root>
Expand Down
71 changes: 71 additions & 0 deletions src/liquid/components/ld-toggle/test/ld-toggle.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: `<form><ld-toggle /></form>`,
})
expect(root).toMatchSnapshot()
})

it('sets initial state on hidden input', async () => {
const { root } = await newSpecPage({
components: [LdToggle],
html: `<form><ld-toggle name="example" checked required /></form>`,
})
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: `<form><ld-toggle name="example" /></form>`,
})

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', '')
})
})

0 comments on commit 43cb8e6

Please sign in to comment.