Skip to content

Commit

Permalink
Adds Radio and RadioGroup as new web components (#27113)
Browse files Browse the repository at this point in the history
* radio init

* styles radio

* reverts branch

* input spec init

* cleans up spec

* formatting

* updates component name to text input

* updates component name in spec

* radio init

* styles radio

* adds radio and radio group to index rollup

* merge

* cherry-pick

* fixes arguments passed to slottedRadioButtonsChanged

* yarn changew

* updates radio group logic

* cherry-pick

* updates styles

* cherry-pick

* merge

* adds radio group attributes

* removes console logs

* updates styles

* updates RadioGroup readme

* fixes jsdoc

* updats radio and radio group stories

* formats radio group story markup

* removes redundant attribute on RadioGroup story

* removes dead code

* updates radio styles

* updates radio group styles

* removes redundant attribute on radio group

* merge

* styles radio and radio group

* removes dead code

* updates css value to token

* exports RadioGroupOrientation namespaced type

* updates styles per design review

* updates focus styles

* reverts file

* reverts file

* mege

* adds back story args

* re exports RadioGroupOrientation from FAST

* removes redundant stack attribute

* testing local tokens for disabled styling

* radio: updates styles per review

* radio: updates design tokens & styles

* radio: adds storybook content

* radio init

* styles radio

* radio: merge

* merge

* cherry-pick

* fixes arguments passed to slottedRadioButtonsChanged

* yarn changew

* updates radio group logic

* cherry-pick

* updates styles

* cherry-pick

* merge

* adds radio group attributes

* removes console logs

* updates styles

* updates RadioGroup readme

* fixes jsdoc

* updats radio and radio group stories

* formats radio group story markup

* removes redundant attribute on RadioGroup story

* removes dead code

* updates radio styles

* updates radio group styles

* removes redundant attribute on radio group

* merge

* styles radio and radio group

* removes dead code

* updates css value to token

* exports RadioGroupOrientation namespaced type

* updates styles per design review

* updates focus styles

* reverts file

* reverts file

* mege

* adds back story args

* re exports RadioGroupOrientation from FAST

* removes redundant stack attribute

* testing local tokens for disabled styling

* radio: updates styles per review

* radio: updates design tokens & styles

* radio: adds storybook content

* radio: initiates css variables in css

* leverage css grid for spacing and remove js application for padding

* radio: adds export to root json

* radio: changes per review

* radio: removes pointer events from disabled radio item

* radio: updates styling

* radio: styles formatting

* radio: updates styles

* Update change/@fluentui-web-components-94ca1c7a-1462-4aa5-9458-41a54f17527e.json

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>

* removes redundant style

* radio: addresses pr feedback

* radio: updates disabled styles

* Update packages/web-components/src/radio/radio.stories.ts

Co-authored-by: Chris Holt <chhol@microsoft.com>

* radio: swaps args table value for const

* radio: removes duplicate style

* radio: optimizes styles

* radio: swaps js for css disabled styling solution

* radiogroup, radio: updates based on feedback

* radiogroup, radio: updates storybook content and README

* radiogroup, radio: updates docs

* radiogroup, radio: removes label-position from docs

* radiogroup, radio: updates docs

* Update packages/web-components/src/radio/README.md

Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>

* radiogroup, radio: addresses feedback

* radiogroup, radio: updates docs

* radiogroup, radio: addresses feedback

* radiogroup, radio: updates styles

* radiogroup, radio: removes whitespace

* radiogroup, radio: updates styles per review

* radiogroup, radio: formats files

* radiogroup, radio: format document

* radiogroup, radio: fixes import

---------

Co-authored-by: Chris Holt <chhol@microsoft.com>
Co-authored-by: Miroslav Stastny <mistastn@microsoft.com>
  • Loading branch information
3 people authored and radium-v committed May 6, 2024
1 parent c6bc0d3 commit f1f961d
Show file tree
Hide file tree
Showing 19 changed files with 616 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat(radio): add radio and radio-group web components",
"packageName": "@fluentui/web-components",
"email": "brianbrady@microsoft.com",
"dependentChangeType": "patch"
}
8 changes: 8 additions & 0 deletions packages/web-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@
"types": "./dist/esm/progress-bar/define.d.ts",
"default": "./dist/esm/progress-bar/define.js"
},
"./radio": {
"types": "./dist/esm/radio/define.d.ts",
"default": "./dist/esm/radio/define.js"
},
"./radio-group": {
"types": "./dist/esm/radio-group/define.d.ts",
"default": "./dist/esm/radio-group/define.js"
},
"./slider": {
"types": "./dist/esm/slider/define.d.ts",
"default": "./dist/esm/slider/define.js"
Expand Down
2 changes: 2 additions & 0 deletions packages/web-components/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export * from './menu-button/index.js';
export * from './menu-item/index.js';
export * from './menu-list/index.js';
export * from './progress-bar/index.js';
export * from './radio/index.js';
export * from './radio-group/index.js';
export * from './slider/index.js';
export * from './spinner/index.js';
export * from './switch/index.js';
Expand Down
36 changes: 19 additions & 17 deletions packages/web-components/src/radio-group/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Radio Group

> RadioGroup lets people select a single option from two or more Radio items. Use RadioGroup to present all available choices if there's enough space..
> RadioGroup lets users select a single option from two or more Radio items. Use RadioGroup to present all available choices if there's enough space..
<br />

Expand Down Expand Up @@ -36,13 +36,14 @@ Used anywhere an author might group a list of radio options.

### **Fields**

| Name | Privacy | Type | Default | Description |
| ------------- | ------- | ------------------------ | ------------ | ----------------------------------------------------------------------------------------------------- |
| `disabled` | public | `boolean` | `false` | Disables the radio group and child radios. |
| `name` | public | `string` | | The name of the radio group. Setting this value will set the name value for all child radio elements. |
| `value` | public | `string` | | The value of the checked radio. |
| `orientation` | public | `horizontal \| vertical` | `horizontal` | The orientation of the group |
| default slot | public | `HTMLElement[]` | | The default slot expecting Radio items |
| Name | Privacy | Type | Default | Description |
| ------------- | ------- | ------------------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `disabled` | public | `boolean` | `false` | Disables the radio group and child radios. |
| `name` | public | `string` | | The name of the radio group. Setting this value will set the name value for all child radio elements. |
| `value` | public | `string` | | The value of the checked radio. |
| `orientation` | public | `horizontal \| vertical` | `horizontal` | Determines whether radios in a radio group are rendered in a horizontal row or a vertical column. The default value is horizontal, which will render radios in a horizontal row with labels appearing inline. Setting orientation to vertical will render radios in a vertical column with labels appearing inline. |
| `stacked` | public | `boolean` | `false` | Determines whether the labels for radios appear inline or stacked when orientation is set to horizontal. The default value is false, which will display the labels inline. If stacked is set to true, the labels will appear under each radio in a horizontal row. |
| default slot | public | `HTMLElement[]` | | The default slot expecting Radio items. |

<br />

Expand All @@ -57,9 +58,9 @@ Used anywhere an author might group a list of radio options.

### **Events**

| Name | Type | Description |
| -------- | ---- | ---------------------------------------------------- |
| `change` | | Fires a custom 'change' event when the value changes |
| Name | Event Type | Target | Arguments | Description |
| -------- | ------------- | ---------------- | --------- | ----------------------------------------------------------------------------------------------------------------- |
| `change` | `CustomEvent` | `FASTRadioGroup` | none | Fired when the value of the RadioGroup changes (i.e., when a different radio button within the group is selected) |

<br />

Expand Down Expand Up @@ -97,9 +98,10 @@ Used anywhere an author might group a list of radio options.

### **WAI-ARIA Roles, States, and Properties**

| Attributes | value | Description |
| ----------------- | ----- | ---------------------------------------- |
| `aria-labelledby` | | used to associate a label with the group |
| Attributes | value | Description |
| ----------------- | -------------- | ---------------------------------------- |
| `aria-labelledby` | | used to associate a label with the group |
| `role` | `"radiogroup"` | used to define a group of radio buttons |

<br />
<hr />
Expand All @@ -122,6 +124,6 @@ Used anywhere an author might group a list of radio options.
<br />

**Property Mapping**
| Fluent UI React 9 | Fluent Web Components 3 | Description of difference |
|-------------------|------------------------ |---------------------------|
| `layout` | `orientation` | React implementation requires user to pass either `"horizontal"` or `"horizontal-stacked"` through `layout` prop. <br /> WC3 implementation requires user to either pass `"vertical"` or "`horizontal"` through `orientation` attribute.
| Fluent UI React 9 | Fluent Web Components | Description of difference |
|-------------------|-------------------------- |---------------------------|
| `layout` | `orientation` + `stacked` | React implementation requires user to pass either `"horizontal"` or `"horizontal-stacked"` through `layout` prop. <br /> WC3 implementation requires user to either pass `"vertical"` or "`horizontal"` through `orientation` attribute. Additionally, adding the `boolean` attribute `stacked` when the orientation is set to `horizontal` will create the `horizontal-stacked` layout available in FUIR9.
4 changes: 4 additions & 0 deletions packages/web-components/src/radio-group/define.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { definition } from './radio-group.definition.js';

definition.define(FluentDesignSystem.registry);
5 changes: 5 additions & 0 deletions packages/web-components/src/radio-group/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './radio-group.js';
export { definition as RadioGroupDefinition } from './radio-group.definition.js';
export { styles as RadioGroupStyles } from './radio-group.styles.js';
export { template as RadioGroupTemplate } from './radio-group.template.js';
export { RadioGroupOrientation } from '@microsoft/fast-foundation';
18 changes: 18 additions & 0 deletions packages/web-components/src/radio-group/radio-group.definition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FluentDesignSystem } from '../fluent-design-system.js';
import { RadioGroup } from './radio-group.js';
import { styles } from './radio-group.styles.js';
import { template } from './radio-group.template.js';

/**
* The Fluent RadioGroup Element.
*
*
* @public
* @remarks
* HTML Element: \<fluent-radio-group\>
*/
export const definition = RadioGroup.compose({
name: `${FluentDesignSystem.prefix}-radio-group`,
template,
styles,
});
215 changes: 215 additions & 0 deletions packages/web-components/src/radio-group/radio-group.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import { html } from '@microsoft/fast-element';
import type { Args, Meta } from '@storybook/html';
import { RadioGroupOrientation } from '@microsoft/fast-foundation';
import { renderComponent } from '../helpers.stories.js';
import { RadioGroup as FluentRadioGroup } from './radio-group.js';
import './define.js';
import '../radio/define.js';

type RadioGroupStoryArgs = Args & FluentRadioGroup;
type RadioGroupStoryMeta = Meta<RadioGroupStoryArgs>;

const storyTemplate = html<RadioGroupStoryArgs>`
<fluent-radio-group
aria-labelledby="label-1"
?disabled=${x => x.disabled}
?stacked=${x => x.stacked}
orientation=${x => x.orientation}
name="radio-story"
>
<span id="label-1" slot="label">Favorite Fruit</span>
<fluent-radio ?checked="${x => x.checked}" value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`;

export default {
title: 'Components/RadioGroup',
args: {
disabled: false,
orientation: RadioGroupOrientation.horizontal,
},
argTypes: {
disabled: {
control: {
type: 'boolean',
},
table: {
type: {
summary: 'Sets disabled state on radio',
},
defaultValue: {
summary: 'false',
},
},
},
checked: {
control: {
type: 'boolean',
},
table: {
type: {
summary: 'Sets checked state on radio',
},
defaultValue: {
summary: 'false',
},
},
},
stacked: {
control: {
type: 'boolean',
},
table: {
type: {
summary: 'Creates a stacked layout for horizontal radio buttons',
},
defaultValue: {
summary: 'false',
},
},
},
orientation: {
control: {
type: 'select',
options: Object.values(RadioGroupOrientation),
},
defaultValue: RadioGroupOrientation.horizontal,
table: {
type: {
summary: 'Sets orientation of radio group',
},
defaultValue: {
summary: RadioGroupOrientation.horizontal,
},
},
},
change: {
action: 'change',
table: {
type: {
summary: 'Event that is fired when the selected radio button changes',
},
defaultValue: {
summary: null,
},
},
},
},
} as RadioGroupStoryMeta;

export const RadioGroup = renderComponent(storyTemplate).bind({});

export const RadioGroupLabelledby = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group aria-labelledby="label-2" name="radio-story">
<span id="label-2" slot="label">Favorite Fruit</span>
<fluent-radio value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupLayoutVertical = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group aria-labelledby="label-3" orientation="vertical" name="radio-story">
<span id="label-3" slot="label">Favorite Fruit</span>
<fluent-radio value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupLayoutHorizontal = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group aria-labelledby="label-4" orientation="horizontal" name="radio-story">
<span id="label-4" slot="label">Favorite Fruit</span>
<fluent-radio value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupLayoutHorizontalStacked = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group stacked aria-labelledby="label-5" orientation="horizontal" name="radio-story">
<span id="label-5" slot="label">Favorite Fruit</span>
<fluent-radio value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupDefaultChecked = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group aria-labelledby="label-6" orientation="horizontal" name="radio-story">
<span id="label-6" slot="label">Favorite Fruit</span>
<fluent-radio value="apple">Apple</fluent-radio>
<fluent-radio checked value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupDisabled = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group disabled aria-labelledby="label-7" name="radio-story">
<span id="label-7" slot="label">Favorite Fruit</span>
<fluent-radio checked value="apple">Apple</fluent-radio>
<fluent-radio checked value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

export const RadioGroupDisabledItem = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group aria-labelledby="label-8" name="radio-story">
<span id="label-8" slot="label">Favorite Fruit</span>
<fluent-radio id="baby" checked value="apple">Apple</fluent-radio>
<fluent-radio disabled value="pear">Pear</fluent-radio>
<fluent-radio value="banana">Banana</fluent-radio>
<fluent-radio value="orange">Orange</fluent-radio>
</fluent-radio-group>
`);

const getLabelContent = (): string | undefined => {
const radioGroup = document.querySelector('#radio-group-fruit') as FluentRadioGroup;

if (!radioGroup) return; // add a check to make sure radioGroup exists

const selectedRadio = radioGroup.value as string;

if (selectedRadio) {
return `Favorite fruit: ${selectedRadio.charAt(0).toUpperCase() + selectedRadio.slice(1)}`;
} else {
return 'Please select your favorite fruit';
}
};

const handleChange = (event: CustomEvent) => {
const radioGroup = document.querySelector('#radio-group-fruit') as FluentRadioGroup;

if (!radioGroup) return; // add a check to make sure radioGroup exists

const selectedRadio = radioGroup.value as string;
const labelElement = radioGroup.querySelector('[slot="label"]') as HTMLSpanElement;
if (selectedRadio) {
const labelContent = selectedRadio.charAt(0).toUpperCase() + selectedRadio.slice(1);
labelElement.textContent = `Favorite fruit: ${labelContent}`;
}
};

export const RadioGroupChangeEvent = renderComponent(html<RadioGroupStoryArgs>`
<fluent-radio-group
id="radio-group-fruit"
aria-labelledby="label-8"
name="radio-story"
@change="${(event: CustomEvent) => handleChange(event)}"
>
<span id="label-8" slot="label">${getLabelContent}</span>
<fluent-radio checked value="apple">Apple</fluent-radio>
<fluent-radio value="pear">Pear</fluent-radio>
<fluent-radio value="Banana">Banana</fluent-radio>
<fluent-radio value="Orange">Orange</fluent-radio>
</fluent-radio-group>
`);
Loading

0 comments on commit f1f961d

Please sign in to comment.