From 0c6060eae799d65892ea51945e9c91ffa0710fe3 Mon Sep 17 00:00:00 2001 From: Jeremiah Hoyet Date: Mon, 22 Nov 2021 12:33:39 -0500 Subject: [PATCH 01/10] Add required attribute to radio group and radio --- docs/components/radio-group.md | 44 +++++++++++++++++++++++ src/components/radio-group/radio-group.ts | 23 ++++++++++++ src/components/radio/radio.ts | 4 +++ 3 files changed, 71 insertions(+) diff --git a/docs/components/radio-group.md b/docs/components/radio-group.md index 0f784721..e879eb85 100644 --- a/docs/components/radio-group.md +++ b/docs/components/radio-group.md @@ -49,4 +49,48 @@ const App = () => ( ); ``` + +### Using the required attribute + +Adding a `required` attribute to `sl-radio-group` will require at least one option to be selected. + +```html preview + + Option 1 + Option 2 + Option 3 + + +
+ +Validate Group + + +``` + +Alternatively, if any of the `sl-radio` elements has a `required` attribute, `sl-radio-group` mimics the behaviour of the browser and will require at least one selection from the group. + +```html preview + + Option 1 + Option 2 + Option 3 + + +
+ +Validate Group + + +``` [component-metadata:sl-radio-group] diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index f306ac82..a444471d 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -26,6 +26,9 @@ export default class SlRadioGroup extends LitElement { /** Shows the fieldset and legend that surrounds the radio group. */ @property({ type: Boolean, attribute: 'fieldset' }) fieldset = false; + /** Indicates that a selection is required. */ + @property({ type: Boolean, reflect: true }) required = false; + handleFocusIn() { // When tabbing into the fieldset, make sure it lands on the checked radio requestAnimationFrame(() => { @@ -39,6 +42,26 @@ export default class SlRadioGroup extends LitElement { }); } + reportValidity() { + const radios = [...(this.defaultSlot.assignedElements({ flatten: true }) as SlRadio[])]; + let isChecked = true; + + // Set required to true if any of the radio elements are required + this.required = this.required || radios.some(el => el.required); + + if (this.required && radios.length > 0) { + isChecked = radios.some(el => el.checked); + + if (!isChecked) { + radios[0].required = true; + // Trigger validity message on first input + radios[0].reportValidity(); + } + } + + return isChecked; + } + render() { return html`
Date: Mon, 22 Nov 2021 14:05:14 -0500 Subject: [PATCH 02/10] Remove required on sl-radio checks from sl-radio-group --- docs/components/radio-group.md | 36 +++++++++-------------- src/components/radio-group/radio-group.ts | 10 +++---- 2 files changed, 19 insertions(+), 27 deletions(-) diff --git a/docs/components/radio-group.md b/docs/components/radio-group.md index e879eb85..bd3a0ef2 100644 --- a/docs/components/radio-group.md +++ b/docs/components/radio-group.md @@ -55,7 +55,7 @@ const App = () => ( Adding a `required` attribute to `sl-radio-group` will require at least one option to be selected. ```html preview - + Option 1 Option 2 Option 3 @@ -63,34 +63,26 @@ Adding a `required` attribute to `sl-radio-group` will require at least one opti
-Validate Group +Validate Group ``` -Alternatively, if any of the `sl-radio` elements has a `required` attribute, `sl-radio-group` mimics the behaviour of the browser and will require at least one selection from the group. +```jsx react +import { SlRadio, SlRadioGroup } from '@shoelace-style/shoelace/dist/react'; -```html preview - - Option 1 - Option 2 - Option 3 - - -
- -Validate Group - - +const App = () => ( + + Option 1 + Option 2 + Option 3 + +); ``` + [component-metadata:sl-radio-group] diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index a444471d..2ab38862 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -46,16 +46,16 @@ export default class SlRadioGroup extends LitElement { const radios = [...(this.defaultSlot.assignedElements({ flatten: true }) as SlRadio[])]; let isChecked = true; - // Set required to true if any of the radio elements are required - this.required = this.required || radios.some(el => el.required); - if (this.required && radios.length > 0) { isChecked = radios.some(el => el.checked); if (!isChecked) { + // This is hacky... radios[0].required = true; - // Trigger validity message on first input - radios[0].reportValidity(); + + setTimeout(() => { + radios[0].reportValidity(); + }, 0); } } From dfaabeead23dbcdc004e8a2aefc50759707cef11 Mon Sep 17 00:00:00 2001 From: Jeremiah Hoyet Date: Mon, 22 Nov 2021 14:20:33 -0500 Subject: [PATCH 03/10] Clean up react example --- docs/components/radio-group.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/components/radio-group.md b/docs/components/radio-group.md index bd3a0ef2..e11734f4 100644 --- a/docs/components/radio-group.md +++ b/docs/components/radio-group.md @@ -60,9 +60,7 @@ Adding a `required` attribute to `sl-radio-group` will require at least one opti Option 2 Option 3
-
- Validate Group ``` diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index a43a78df..3c738943 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -17,20 +17,40 @@ import styles from './radio-group.styles'; @customElement('sl-radio-group') export default class SlRadioGroup extends LitElement { static styles = styles; + private _value: string; @query('slot:not([name])') defaultSlot: HTMLSlotElement; /** The radio group label. Required for proper accessibility. Alternatively, you can use the label slot. */ @property() label = ''; + set value(newValue) { + const index = this.getAllRadios().findIndex(el => el.value === newValue); + const oldValue = this._value; + + if (index > -1) { + this.checkRadioByIndex(index); + this._value = newValue; + this.requestUpdate('value', oldValue); + } else { + this._value = ''; + this.deselectAll(); + } + } + + /** The current value of the radio group. */ + @property() + get value() { + return this._value; + } + /** Shows the fieldset and legend that surrounds the radio group. */ @property({ type: Boolean, attribute: 'fieldset' }) fieldset = false; /** Indicates that a selection is required. */ @property({ type: Boolean, reflect: true }) required = false; - constructor() { - super(); + connectedCallback() { this.addEventListener('sl-change', this.syncRadioButtons); } @@ -39,7 +59,7 @@ export default class SlRadioGroup extends LitElement { } syncRadioButtons(event: CustomEvent) { - const currentRadio = ev.target; + const currentRadio = event.target; const radios = this.getAllRadios().filter(el => !el.disabled && el !== currentRadio); radios.forEach(el => { el.checked = false; @@ -63,6 +83,23 @@ export default class SlRadioGroup extends LitElement { return [...this.querySelectorAll('sl-radio')]; } + checkRadioByIndex(index: number): SlRadio[] { + const radios = this.deselectAll(); + + radios[index].focus(); + radios[index].checked = true; + this._value = radios[index].value; + + return radios; + } + + deselectAll(): SlRadio[] { + return this.getAllRadios().map(radio => { + radio.checked = false; + return radio; + }); + } + handleKeyDown(event: KeyboardEvent) { if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'].includes(event.key)) { const radios = this.getAllRadios().filter(radio => !radio.disabled); @@ -73,9 +110,7 @@ export default class SlRadioGroup extends LitElement { if (index < 0) index = radios.length - 1; if (index > radios.length - 1) index = 0; - this.getAllRadios().map(radio => (radio.checked = false)); - radios[index].focus(); - radios[index].checked = true; + this.checkRadioByIndex(index); event.preventDefault(); } From cfa800c1cd58242cb5e7005f325402b1fcddbd1c Mon Sep 17 00:00:00 2001 From: Jeremiah Hoyet Date: Mon, 22 Nov 2021 19:18:07 -0500 Subject: [PATCH 08/10] Fix getter --- src/components/radio-group/radio-group.ts | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index 3c738943..dfc5b282 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -24,6 +24,14 @@ export default class SlRadioGroup extends LitElement { /** The radio group label. Required for proper accessibility. Alternatively, you can use the label slot. */ @property() label = ''; + /** The current value of the radio group. */ + @property() + get value() { + if (!this._value) return this.getCurrentValue(); + + return this._value; + } + set value(newValue) { const index = this.getAllRadios().findIndex(el => el.value === newValue); const oldValue = this._value; @@ -38,12 +46,6 @@ export default class SlRadioGroup extends LitElement { } } - /** The current value of the radio group. */ - @property() - get value() { - return this._value; - } - /** Shows the fieldset and legend that surrounds the radio group. */ @property({ type: Boolean, attribute: 'fieldset' }) fieldset = false; @@ -66,6 +68,12 @@ export default class SlRadioGroup extends LitElement { }); } + getCurrentValue() { + const valRadio = this.getAllRadios().filter(el => el.checked); + this._value = valRadio.length === 1 ? valRadio[0].value : ''; + return this._value; + } + handleFocusIn() { // When tabbing into the fieldset, make sure it lands on the checked radio requestAnimationFrame(() => { From 3141590d28d416247e7d72fc4f09006b8a1256bd Mon Sep 17 00:00:00 2001 From: Jeremiah Hoyet Date: Mon, 22 Nov 2021 19:27:12 -0500 Subject: [PATCH 09/10] Update react example --- docs/components/radio-group.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/components/radio-group.md b/docs/components/radio-group.md index 957d125c..17241de5 100644 --- a/docs/components/radio-group.md +++ b/docs/components/radio-group.md @@ -80,6 +80,12 @@ const validateGroup = () => { const group = document.querySelector('sl-radio-group.required-radio-group'); group.reportValidity(); } + +const resetGroup = () => { + const group = document.querySelector('sl-radio-group.required-radio-group'); + group.value = ""; +} + const App = () => ( <> @@ -89,6 +95,7 @@ const App = () => (
validateGroup()}>Validate Group + resetGroup()}>Reset Group ); ``` From e9f7c5ed5aeb88a0be473884ed240a5ff2f594f8 Mon Sep 17 00:00:00 2001 From: Jeremiah Hoyet Date: Mon, 22 Nov 2021 19:30:51 -0500 Subject: [PATCH 10/10] Make the default the same as the getter default --- src/components/radio-group/radio-group.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/radio-group/radio-group.ts b/src/components/radio-group/radio-group.ts index dfc5b282..e3aca1ef 100644 --- a/src/components/radio-group/radio-group.ts +++ b/src/components/radio-group/radio-group.ts @@ -17,7 +17,7 @@ import styles from './radio-group.styles'; @customElement('sl-radio-group') export default class SlRadioGroup extends LitElement { static styles = styles; - private _value: string; + private _value: string = ''; @query('slot:not([name])') defaultSlot: HTMLSlotElement;