Add orientation to radio group (#635)

* add orientation to radio group; fixes #613

* fix timing issue that prevents value from being set sometimes

* gimme a break

* make radio button examples horizontal

---------

Co-authored-by: lindsaym-fa <dev@lindsaym.design>
This commit is contained in:
Cory LaViska
2025-01-28 14:23:39 -05:00
committed by GitHub
parent 0cb72adb28
commit 3ff8745910
8 changed files with 121 additions and 41 deletions

View File

@@ -9,7 +9,7 @@ icon: radio-group
Radio buttons are designed to be used with [radio groups](/docs/components/radio-group). When a radio button has focus, the arrow keys can be used to change the selected option just like standard radio controls.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio-group label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -23,7 +23,7 @@ Radio buttons are designed to be used with [radio groups](/docs/components/radio
To set the initial value and checked state, use the `value` attribute on the containing radio group.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio-group label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -35,7 +35,7 @@ To set the initial value and checked state, use the `value` attribute on the con
Use the `disabled` attribute to disable a radio button.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio-group label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2" disabled>Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -47,7 +47,7 @@ Use the `disabled` attribute to disable a radio button.
Use the `size` attribute to change a radio button's size.
```html {.example}
<wa-radio-group size="small" label="Select an option" name="a" value="1">
<wa-radio-group size="small" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -55,7 +55,7 @@ Use the `size` attribute to change a radio button's size.
<br />
<wa-radio-group size="medium" label="Select an option" name="a" value="1">
<wa-radio-group size="medium" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -63,7 +63,7 @@ Use the `size` attribute to change a radio button's size.
<br />
<wa-radio-group size="large" label="Select an option" name="a" value="1">
<wa-radio-group size="large" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -75,7 +75,7 @@ Use the `size` attribute to change a radio button's size.
Use the `pill` attribute to give radio buttons rounded edges.
```html {.example}
<wa-radio-group size="small" label="Select an option" name="a" value="1">
<wa-radio-group size="small" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button pill value="1">Option 1</wa-radio-button>
<wa-radio-button pill value="2">Option 2</wa-radio-button>
<wa-radio-button pill value="3">Option 3</wa-radio-button>
@@ -83,7 +83,7 @@ Use the `pill` attribute to give radio buttons rounded edges.
<br />
<wa-radio-group size="medium" label="Select an option" name="a" value="1">
<wa-radio-group size="medium" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button pill value="1">Option 1</wa-radio-button>
<wa-radio-button pill value="2">Option 2</wa-radio-button>
<wa-radio-button pill value="3">Option 3</wa-radio-button>
@@ -91,7 +91,7 @@ Use the `pill` attribute to give radio buttons rounded edges.
<br />
<wa-radio-group size="large" label="Select an option" name="a" value="1">
<wa-radio-group size="large" label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button pill value="1">Option 1</wa-radio-button>
<wa-radio-button pill value="2">Option 2</wa-radio-button>
<wa-radio-button pill value="3">Option 3</wa-radio-button>
@@ -103,7 +103,7 @@ Use the `pill` attribute to give radio buttons rounded edges.
Use the `prefix` and `suffix` slots to add icons.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio-group label="Select an option" orientation="horizontal" name="a" value="1">
<wa-radio-button value="1">
<wa-icon slot="prefix" name="archive" variant="solid"></wa-icon>
Option 1
@@ -127,7 +127,7 @@ Use the `prefix` and `suffix` slots to add icons.
You can omit button labels and use icons instead. Make sure to set a `label` attribute on each icon so screen readers will announce each option correctly.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="neutral">
<wa-radio-group label="Select an option" orientation="horizontal" name="a" value="neutral">
<wa-radio-button value="angry">
<wa-icon name="face-angry" variant="solid" label="Angry"></wa-icon>
</wa-radio-button>

View File

@@ -7,8 +7,8 @@ icon: radio-group
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2">Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -21,8 +21,8 @@ Add descriptive hint to a radio group with the `hint` attribute. For hints that
```html {.example}
<wa-radio-group label="Select an option" hint="Choose the most appropriate option." name="a" value="1">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2">Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -32,7 +32,28 @@ Add descriptive hint to a radio group with the `hint` attribute. For hints that
[Radio buttons](/docs/components/radio-button) offer an alternate way to display radio controls. In this case, an internal [button group](/docs/components/button-group) is used to group the buttons into a single, cohesive control.
```html {.example}
<wa-radio-group label="Select an option" hint="Select an option that makes you proud." name="a" value="1">
<wa-radio-group
label="Horizontal options"
hint="Select an option that makes you proud."
orientation="horizontal"
name="a"
value="1"
>
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
</wa-radio-group>
<br>
<wa-radio-group
label="Vertical options"
hint="Select an option that makes you proud."
orientation="vertical"
name="a"
value="1"
style="max-width: 300px;"
>
<wa-radio-button value="1">Option 1</wa-radio-button>
<wa-radio-button value="2">Option 2</wa-radio-button>
<wa-radio-button value="3">Option 3</wa-radio-button>
@@ -45,8 +66,26 @@ Radios and radio buttons can be disabled by adding the `disabled` attribute to t
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2" disabled>Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2" disabled>Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
### Orientation
The default orientation for radio items is `vertical`. Set the `orientation` to `horizontal` to items on the same row.
```html {.example}
<wa-radio-group
label="Select an option"
hint="Choose the most appropriate option."
orientation="horizontal"
name="a"
value="1"
>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -57,8 +96,8 @@ The size of [Radios](/docs/components/radio) and [Radio Buttons](/docs/component
```html preview
<wa-radio-group label="Select an option" size="medium" value="medium" class="radio-group-size">
<wa-radio value="small">Small</wa-radio><br>
<wa-radio value="medium">Medium</wa-radio><br>
<wa-radio value="small">Small</wa-radio>
<wa-radio value="medium">Medium</wa-radio>
<wa-radio value="large">Large</wa-radio>
</wa-radio-group>
@@ -82,8 +121,8 @@ Setting the `required` attribute to make selecting an option mandatory. If a val
```html {.example}
<form class="validation">
<wa-radio-group label="Select an option" name="a" required>
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2">Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
<br />
@@ -108,8 +147,8 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
```html {.example}
<form class="custom-validity">
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1">Not me</wa-radio><br>
<wa-radio value="2">Me neither</wa-radio><br>
<wa-radio value="1">Not me</wa-radio>
<wa-radio value="2">Me neither</wa-radio>
<wa-radio value="3">Choose me</wa-radio>
</wa-radio-group>
<br />
@@ -127,7 +166,7 @@ Use the `setCustomValidity()` method to set a custom validation message. This wi
});
// Update validity when a selection is made
form.addEventlistener('change', () => {
form.addEventListener('change', () => {
const isValid = radioGroup.value === '3';
radioGroup.setCustomValidity(isValid ? '' : errorMessage);
});

View File

@@ -11,8 +11,8 @@ Radios are designed to be used with [radio groups](/docs/components/radio-group)
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2">Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -29,8 +29,8 @@ To set the initial value and checked state, use the `value` attribute on the con
```html {.example}
<wa-radio-group label="Select an option" name="a" value="3">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2">Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2">Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -41,8 +41,8 @@ Use the `disabled` attribute to disable a radio.
```html {.example}
<wa-radio-group label="Select an option" name="a" value="1">
<wa-radio value="1">Option 1</wa-radio><br>
<wa-radio value="2" disabled>Option 2</wa-radio><br>
<wa-radio value="1">Option 1</wa-radio>
<wa-radio value="2" disabled>Option 2</wa-radio>
<wa-radio value="3">Option 3</wa-radio>
</wa-radio-group>
```
@@ -53,24 +53,24 @@ Add the `size` attribute to the [Radio Group](/docs/components/radio-group) to c
```html {.example}
<wa-radio-group size="small" value="1">
<wa-radio value="1">Small 1</wa-radio><br>
<wa-radio value="2">Small 2</wa-radio><br>
<wa-radio value="1">Small 1</wa-radio>
<wa-radio value="2">Small 2</wa-radio>
<wa-radio value="3">Small 3</wa-radio>
</wa-radio-group>
<br />
<wa-radio-group size="medium" value="1">
<wa-radio value="1">Medium 1</wa-radio><br>
<wa-radio value="2">Medium 2</wa-radio><br>
<wa-radio value="1">Medium 1</wa-radio>
<wa-radio value="2">Medium 2</wa-radio>
<wa-radio value="3">Medium 3</wa-radio>
</wa-radio-group>
<br />
<wa-radio-group size="large" value="1">
<wa-radio value="1">Large 1</wa-radio><br>
<wa-radio value="2">Large 2</wa-radio><br>
<wa-radio value="1">Large 1</wa-radio>
<wa-radio value="2">Large 2</wa-radio>
<wa-radio value="3">Large 3</wa-radio>
</wa-radio-group>
```

View File

@@ -20,6 +20,7 @@ During the alpha period, things might break! We take breaking changes very serio
- `wa-blur` => `blur` (this event will no longer bubble, use `focusout` for a bubbling version)
- `wa-focus` => `focus` (this event will no longer bubble)
- Added `.wa-callout` utility class
- Added the `orientation` attribute to `<wa-radio-group>` to support vertical and horizontal radio items
- Fixed a bug in `<wa-tab-group>` that prevented nested tab groups from working properly
- Fixed slot names for `show-password-icon` and `hide-password-icon` in `<wa-input>` to more intuitively represent their functions

View File

@@ -22,6 +22,26 @@
display: flex;
}
[part~='form-control-input'] {
display: flex;
flex-direction: column;
flex-wrap: wrap;
gap: var(--wa-space-s);
margin-block-end: var(--wa-space-xs);
}
/* Horizontal */
:host([orientation='horizontal']) [part~='form-control-input'] {
flex-direction: row;
gap: var(--wa-space-m);
}
/* When radio buttons are slotted */
:host([orientation]) .form-control--has-radio-buttons [part~='form-control-input'] {
gap: 0;
flex-wrap: nowrap;
}
/* Help text */
[part~='hint'] {
margin-block-start: var(--wa-space-2xs);

View File

@@ -9,6 +9,7 @@ import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-form-
import formControlStyles from '../../styles/shadow/form-control.css';
import buttonGroupStyles from '../../styles/utilities/button-group.css';
import sizeStyles from '../../styles/utilities/size.css';
import '../radio-button/radio-button.js';
import type WaRadioButton from '../radio-button/radio-button.js';
import '../radio/radio.js';
import type WaRadio from '../radio/radio.js';
@@ -20,7 +21,8 @@ import styles from './radio-group.css';
* @status stable
* @since 2.0
*
* @dependency wa-button-group
* @dependency wa-radio
* @dependency wa-radio-button
*
* @slot - The default slot where `<wa-radio>` or `<wa-radio-button>` elements are placed.
* @slot label - The radio group's label. Required for proper accessibility. Alternatively, you can use the `label`
@@ -34,6 +36,7 @@ import styles from './radio-group.css';
* @csspart form-control - The form control that wraps the label, input, and hint.
* @csspart form-control-label - The label's wrapper.
* @csspart form-control-input - The input's wrapper.
* @csspart radios - The wrapper than surrounds radio items, styled as a flex container by default.
* @csspart hint - The hint's wrapper.
* @csspart button-group - The button group that wraps radio buttons.
* @csspart button-group__base - The button group's `base` part.
@@ -63,6 +66,7 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
@query('slot:not([name])') defaultSlot: HTMLSlotElement;
@state() private hasButtonGroup = false;
@state() private hasRadioButtons = false;
/**
* The radio group's label. Required for proper accessibility. If you need to display HTML, use the `label` slot
@@ -76,6 +80,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
/** The name of the radio group, submitted as a name/value pair with form data. */
@property({ reflect: true }) name: string | null = null;
/** The orientation in which to show radio items. */
@property({ reflect: true }) orientation: 'horizontal' | 'vertical' = 'vertical';
private _value: string | null = null;
get value() {
@@ -173,6 +180,9 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
private async syncRadioElements() {
const radios = this.getAllRadios();
// Detect the presence of radio buttons
this.hasRadioButtons = radios.some(radio => radio.localName === 'wa-radio-button');
await Promise.all(
// Sync the checked state and size
radios.map(async radio => {
@@ -324,11 +334,13 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
'form-control--large': this.size === 'large',
'form-control--radio-group': true,
'form-control--has-label': hasLabel,
'form-control--has-radio-buttons': this.hasRadioButtons,
})}
role="radiogroup"
aria-labelledby="label"
aria-describedby="hint"
aria-errormessage="error-message"
aria-orientation=${this.orientation}
>
<label
part="form-control-label"
@@ -342,7 +354,10 @@ export default class WaRadioGroup extends WebAwesomeFormAssociatedElement {
<slot
part="form-control-input"
class=${classMap({ 'wa-button-group': this.hasButtonGroup })}
class=${classMap({
'wa-button-group': this.hasButtonGroup,
'wa-button-group-vertical': this.orientation === 'vertical',
})}
@slotchange=${this.syncRadioElements}
></slot>

View File

@@ -21,6 +21,10 @@ label:has(input[type='radio']),
-webkit-user-select: none;
}
:host {
width: fit-content;
}
/* Replace native radio styles */
input[type='radio'] {
appearance: none;

View File

@@ -20,7 +20,7 @@
}
/* Horizontal */
.wa-button-group:not([aria-orientation='vertical']) {
.wa-button-group:not([aria-orientation='vertical']):not(.wa-button-group-vertical) {
> :not(:first-child),
&::slotted(:not(:first-child)) {
border-start-start-radius: 0 !important;
@@ -40,7 +40,8 @@
}
/* Vertical */
.wa-button-group[aria-orientation='vertical'] {
.wa-button-group[aria-orientation='vertical'],
.wa-button-group-vertical {
flex-direction: column;
> :not(:first-child),