Skip to content

Commit

Permalink
fix(ld-radio): prop forwarding
Browse files Browse the repository at this point in the history
  • Loading branch information
borisdiakur authored and renet committed Dec 2, 2021
1 parent e3274e6 commit 58daab5
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 49 deletions.
118 changes: 85 additions & 33 deletions src/liquid/components/ld-radio/ld-radio.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, Element, h, Host, Method, Prop, Watch } from '@stencil/core'
import { cloneAttributes } from '../../utils/cloneAttributes'
import { getClassNames } from '../../utils/getClassNames'

/**
* @virtualProp ref - reference to component
Expand All @@ -17,30 +18,45 @@ export class LdRadio implements InnerFocusable {
private input: HTMLInputElement
private hiddenInput: HTMLInputElement

/** Display mode. */
@Prop() mode?: 'highlight' | 'danger'
/** Hint for form autofill feature. */
@Prop({ mutable: true, reflect: true }) autocomplete?: string

/** Used to specify the name of the control. */
@Prop() name: string
/** Automatically focus the form control when the page is loaded. */
@Prop() autofocus?: boolean

/** The input value. */
@Prop() value: string

/** radio tone. Use `'dark'` on white backgrounds. Default is a light tone. */
@Prop() tone: 'dark'
@Prop({ mutable: true, reflect: true }) checked: boolean

/** Disabled state of the radio. */
@Prop() disabled: boolean

/** The input value. */
@Prop({ mutable: true, reflect: true }) checked: boolean
/** Associates the control with a form element. */
@Prop() form?: string

/** Set this property to `true` in order to mark the radio visually as invalid. */
@Prop() invalid: boolean

/** Value of the id attribute of the `<datalist>` of autocomplete options. */
@Prop() list?: string

/** Display mode. */
@Prop() mode?: 'highlight' | 'danger'

/** Used to specify the name of the control. */
@Prop() name: string

/** The value is not editable. */
@Prop() readonly?: boolean

/** Set this property to `true` in order to mark the checkbox as required. */
@Prop() required: boolean

/** radio tone. Use `'dark'` on white backgrounds. Default is a light tone. */
@Prop() tone: 'dark'

/** The input value. */
@Prop() value: string

/** Sets focus on the radio button. */
@Method()
async focusInner() {
Expand All @@ -50,25 +66,44 @@ export class LdRadio implements InnerFocusable {
}

@Watch('checked')
@Watch('form')
@Watch('name')
@Watch('required')
@Watch('value')
updateHiddenInput() {
const outerForm = this.el.closest('form')
if (!this.hiddenInput && this.name && (outerForm || this.form)) {
this.hiddenInput = document.createElement('input')
this.el.appendChild(this.hiddenInput)
}

if (this.hiddenInput) {
if (!this.name) {
this.hiddenInput.remove()
this.hiddenInput = undefined
return
}

this.hiddenInput.name = this.name
this.hiddenInput.checked = this.checked
this.hiddenInput.required = this.required

if (this.name) {
this.hiddenInput.name = this.name
} else {
this.hiddenInput.removeAttribute('name')
}

if (this.value) {
this.hiddenInput.value = this.value
} else {
this.hiddenInput.removeAttribute('value')
}

if (this.form) {
this.hiddenInput.setAttribute('form', this.form)
} else if (this.hiddenInput.getAttribute('form')) {
if (outerForm) {
this.hiddenInput.removeAttribute('form')
} else {
this.hiddenInput.remove()
this.hiddenInput = undefined
}
}
}
}

Expand Down Expand Up @@ -146,35 +181,52 @@ export class LdRadio implements InnerFocusable {
}

componentWillLoad() {
if (this.el.closest('form')) {
this.hiddenInput = document.createElement('input')
this.hiddenInput.required = this.required
this.hiddenInput.type = 'radio'
this.hiddenInput.style.visibility = 'hidden'
this.hiddenInput.style.position = 'absolute'
this.hiddenInput.style.pointerEvents = 'none'
this.hiddenInput.checked = this.checked
const outerForm = this.el.closest('form')

if (this.value) {
this.hiddenInput.value = this.value
}
if (outerForm && !this.autocomplete) {
this.autocomplete = outerForm.getAttribute('autocomplete')
}

if (outerForm || this.form) {
if (this.name) {
this.hiddenInput = document.createElement('input')
this.hiddenInput.required = this.required
this.hiddenInput.type = 'radio'
this.hiddenInput.style.visibility = 'hidden'
this.hiddenInput.style.position = 'absolute'
this.hiddenInput.style.pointerEvents = 'none'
this.hiddenInput.checked = this.checked
this.hiddenInput.name = this.name

if (this.form) {
this.hiddenInput.setAttribute('form', this.form)
}

if (this.value) {
this.hiddenInput.value = this.value
}

this.el.appendChild(this.hiddenInput)
}
}
}

this.el.appendChild(this.hiddenInput)
componentDidLoad() {
if (this.autofocus) {
this.focusInner()
}
}

render() {
let cl = 'ld-radio'
if (this.mode) cl += ` ld-radio--${this.mode}`
if (this.tone) cl += ` ld-radio--${this.tone}`
if (this.invalid) cl += ' ld-radio--invalid'
const cl = [
'ld-radio',
this.mode && `ld-radio--${this.mode}`,
this.tone && `ld-radio--${this.tone}`,
this.invalid && 'ld-radio--invalid',
]

return (
<Host part="root" class={cl} onClick={this.handleClick}>
<Host part="root" class={getClassNames(cl)} onClick={this.handleClick}>
<input
part="input focusable"
onBlur={this.handleBlur}
Expand Down
29 changes: 17 additions & 12 deletions src/liquid/components/ld-radio/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,18 +359,23 @@ The `ld-radio` Web Component provides a low level API for integrating the compon

## Properties

| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | --------------------------------------------------------------------------- | ------------------------- | ----------- |
| `checked` | `checked` | The input value. | `boolean` | `undefined` |
| `disabled` | `disabled` | Disabled state of the radio. | `boolean` | `undefined` |
| `invalid` | `invalid` | Set this property to `true` in order to mark the radio visually as invalid. | `boolean` | `undefined` |
| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` |
| `mode` | `mode` | Display mode. | `"danger" \| "highlight"` | `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 checkbox as required. | `boolean` | `undefined` |
| `tone` | `tone` | radio tone. Use `'dark'` on white backgrounds. Default is a light tone. | `"dark"` | `undefined` |
| `value` | `value` | The input value. | `string` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------- | -------------- | --------------------------------------------------------------------------- | ------------------------- | ----------- |
| `autocomplete` | `autocomplete` | Hint for form autofill feature. | `string` | `undefined` |
| `autofocus` | `autofocus` | Automatically focus the form control when the page is loaded. | `boolean` | `undefined` |
| `checked` | `checked` | The input value. | `boolean` | `undefined` |
| `disabled` | `disabled` | Disabled state of the radio. | `boolean` | `undefined` |
| `form` | `form` | Associates the control with a form element. | `string` | `undefined` |
| `invalid` | `invalid` | Set this property to `true` in order to mark the radio visually as invalid. | `boolean` | `undefined` |
| `key` | `key` | for tracking the node's identity when working with lists | `string \| number` | `undefined` |
| `list` | `list` | Value of the id attribute of the `<datalist>` of autocomplete options. | `string` | `undefined` |
| `mode` | `mode` | Display mode. | `"danger" \| "highlight"` | `undefined` |
| `name` | `name` | Used to specify the name of the control. | `string` | `undefined` |
| `readonly` | `readonly` | The value is not editable. | `boolean` | `undefined` |
| `ref` | `ref` | reference to component | `any` | `undefined` |
| `required` | `required` | Set this property to `true` in order to mark the checkbox as required. | `boolean` | `undefined` |
| `tone` | `tone` | radio tone. Use `'dark'` on white backgrounds. Default is a light tone. | `"dark"` | `undefined` |
| `value` | `value` | The input value. | `string` | `undefined` |


## Methods
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ld-radio creates hidden input field, if inside a form 1`] = `
<ld-radio class="ld-radio" part="root">
<ld-radio class="ld-radio" name="example" part="root">
<mock:shadow-root>
<input part="input focusable" tabindex="-1" type="radio">
<div class="ld-radio__dot" part="dot"></div>
<div class="ld-radio__box" part="box"></div>
</mock:shadow-root>
<input type="radio" style="visibility: hidden; position: absolute; pointer-events: none;">
<input name="example" type="radio" style="visibility: hidden; position: absolute; pointer-events: none;">
</ld-radio>
`;
Expand Down
20 changes: 18 additions & 2 deletions src/liquid/components/ld-radio/test/ld-radio.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ describe('ld-radio', () => {
it('creates hidden input field, if inside a form', async () => {
const page = await newSpecPage({
components: [LdRadio],
html: `<form><ld-radio /></form>`,
html: `<form><ld-radio name="example" /></form>`,
})
const ldRadio = page.root
expect(ldRadio).toMatchSnapshot()
Expand Down Expand Up @@ -329,7 +329,12 @@ describe('ld-radio', () => {
ldRadio.removeAttribute('name')
await waitForChanges()

expect(ldRadio.querySelector('input').getAttribute('name')).toEqual(null)
expect(ldRadio.querySelector('input')).toEqual(null)

ldRadio.setAttribute('name', 'test')
await waitForChanges()

expect(ldRadio.querySelector('input')).toHaveProperty('name', 'test')

ldRadio.dispatchEvent(new Event('click'))
await waitForChanges()
Expand All @@ -351,4 +356,15 @@ describe('ld-radio', () => {

expect(ldRadio.querySelector('input').getAttribute('value')).toEqual(null)
})

it('uses hidden input field with referenced form', async () => {
const { root, waitForChanges } = await newSpecPage({
components: [LdRadio],
html: '<ld-radio name="example" form="yolo" />',
})
const ldRadio = root
await waitForChanges()

expect(ldRadio.querySelector('input')).toHaveProperty('name', 'example')
})
})

0 comments on commit 58daab5

Please sign in to comment.