Skip to content

Commit b1745e4

Browse files
committed
feat: add radio button
1 parent 952dbe2 commit b1745e4

File tree

15 files changed

+346
-29
lines changed

15 files changed

+346
-29
lines changed

.changeset/tender-chairs-doubt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@scouterna/ui-react": patch
3+
"@scouterna/ui-webc": patch
4+
---
5+
6+
Align all input components

.changeset/wicked-books-peel.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@scouterna/ui-react": minor
3+
"@scouterna/ui-webc": minor
4+
---
5+
6+
Add radio button component
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { ScoutRadioButton } from "@scouterna/ui-react";
2+
import preview from "#.storybook/preview";
3+
4+
const meta = preview.meta({
5+
title: "Interaction/Radio Button",
6+
component: ScoutRadioButton,
7+
parameters: {
8+
layout: "centered",
9+
},
10+
});
11+
12+
export default meta;
13+
14+
export const BasicExample = meta.story({
15+
args: {
16+
name: "my-radio-group",
17+
},
18+
render: (args) => (
19+
<div style={{ display: "flex", gap: "1rem" }}>
20+
<ScoutRadioButton {...args} />
21+
<ScoutRadioButton {...args} />
22+
<ScoutRadioButton {...args} />
23+
</div>
24+
),
25+
});

packages/ui-react/lib/components/stencil-generated/components.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
/* eslint-disable */
99

10-
import { type ScoutCheckboxCustomEvent, type ScoutInputCustomEvent, type ScoutLinkCustomEvent, type ScoutSelectCustomEvent, type ScoutSwitchCustomEvent } from "@scouterna/ui-webc";
10+
import { type ScoutCheckboxCustomEvent, type ScoutInputCustomEvent, type ScoutLinkCustomEvent, type ScoutRadioButtonCustomEvent, type ScoutSelectCustomEvent, type ScoutSwitchCustomEvent } from "@scouterna/ui-webc";
1111
import { ScoutBottomBarItem as ScoutBottomBarItemElement, defineCustomElement as defineScoutBottomBarItem } from "@scouterna/ui-webc/dist/components/scout-bottom-bar-item.js";
1212
import { ScoutBottomBar as ScoutBottomBarElement, defineCustomElement as defineScoutBottomBar } from "@scouterna/ui-webc/dist/components/scout-bottom-bar.js";
1313
import { ScoutButton as ScoutButtonElement, defineCustomElement as defineScoutButton } from "@scouterna/ui-webc/dist/components/scout-button.js";
@@ -21,6 +21,7 @@ import { ScoutListViewItem as ScoutListViewItemElement, defineCustomElement as d
2121
import { ScoutListViewSubheader as ScoutListViewSubheaderElement, defineCustomElement as defineScoutListViewSubheader } from "@scouterna/ui-webc/dist/components/scout-list-view-subheader.js";
2222
import { ScoutListView as ScoutListViewElement, defineCustomElement as defineScoutListView } from "@scouterna/ui-webc/dist/components/scout-list-view.js";
2323
import { ScoutLoader as ScoutLoaderElement, defineCustomElement as defineScoutLoader } from "@scouterna/ui-webc/dist/components/scout-loader.js";
24+
import { ScoutRadioButton as ScoutRadioButtonElement, defineCustomElement as defineScoutRadioButton } from "@scouterna/ui-webc/dist/components/scout-radio-button.js";
2425
import { ScoutSelect as ScoutSelectElement, defineCustomElement as defineScoutSelect } from "@scouterna/ui-webc/dist/components/scout-select.js";
2526
import { ScoutStack as ScoutStackElement, defineCustomElement as defineScoutStack } from "@scouterna/ui-webc/dist/components/scout-stack.js";
2627
import { ScoutSwitch as ScoutSwitchElement, defineCustomElement as defineScoutSwitch } from "@scouterna/ui-webc/dist/components/scout-switch.js";
@@ -73,7 +74,7 @@ export const ScoutCard: StencilReactComponent<ScoutCardElement, ScoutCardEvents>
7374
});
7475

7576
export type ScoutCheckboxEvents = {
76-
onScoutCheckboxChecked: EventName<ScoutCheckboxCustomEvent<{
77+
onScoutChecked: EventName<ScoutCheckboxCustomEvent<{
7778
checked: boolean;
7879
element: HTMLInputElement;
7980
}>>,
@@ -86,7 +87,7 @@ export const ScoutCheckbox: StencilReactComponent<ScoutCheckboxElement, ScoutChe
8687
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
8788
react: React,
8889
events: {
89-
onScoutCheckboxChecked: 'scoutCheckboxChecked',
90+
onScoutChecked: 'scoutChecked',
9091
on_fieldId: '_fieldId'
9192
} as ScoutCheckboxEvents,
9293
defineCustomElement: defineScoutCheckbox
@@ -191,6 +192,26 @@ export const ScoutLoader: StencilReactComponent<ScoutLoaderElement, ScoutLoaderE
191192
defineCustomElement: defineScoutLoader
192193
});
193194

195+
export type ScoutRadioButtonEvents = {
196+
onScoutChecked: EventName<ScoutRadioButtonCustomEvent<{
197+
checked: boolean;
198+
element: HTMLInputElement;
199+
}>>,
200+
on_fieldId: EventName<CustomEvent<string>>
201+
};
202+
203+
export const ScoutRadioButton: StencilReactComponent<ScoutRadioButtonElement, ScoutRadioButtonEvents> = /*@__PURE__*/ createComponent<ScoutRadioButtonElement, ScoutRadioButtonEvents>({
204+
tagName: 'scout-radio-button',
205+
elementClass: ScoutRadioButtonElement,
206+
// @ts-ignore - ignore potential React type mismatches between the Stencil Output Target and your project.
207+
react: React,
208+
events: {
209+
onScoutChecked: 'scoutChecked',
210+
on_fieldId: '_fieldId'
211+
} as ScoutRadioButtonEvents,
212+
defineCustomElement: defineScoutRadioButton
213+
});
214+
194215
export type ScoutSelectEvents = {
195216
onScoutInputChange: EventName<ScoutSelectCustomEvent<{
196217
value: string;

packages/ui-webc/src/components.d.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ export namespace Components {
9191
*/
9292
"disabled": boolean;
9393
"label": string;
94+
"name": string;
95+
"value": string;
9496
}
9597
interface ScoutDivider {
9698
}
@@ -114,6 +116,7 @@ export namespace Components {
114116
* Input mode hints for devices with dynamic keyboards.
115117
*/
116118
"inputmode"?: InputMode;
119+
"name": string;
117120
/**
118121
* Regex pattern for input validation.
119122
*/
@@ -186,12 +189,30 @@ export namespace Components {
186189
"size"?: "xs" | "sm" | "base" | "lg" | "xl";
187190
"text"?: string;
188191
}
192+
interface ScoutRadioButton {
193+
/**
194+
* Use this prop if you need to connect your radio button with another element describing its use, other than the property label.
195+
*/
196+
"ariaLabelledby": string;
197+
/**
198+
* @default false
199+
*/
200+
"checked": boolean;
201+
/**
202+
* @default false
203+
*/
204+
"disabled": boolean;
205+
"label": string;
206+
"name": string;
207+
"value": string;
208+
}
189209
interface ScoutSelect {
190210
/**
191211
* Whether the select is disabled. Disabled selects are not editable, excluded from tab order and are not validated.
192212
* @default false
193213
*/
194214
"disabled": boolean;
215+
"name": string;
195216
/**
196217
* Custom validation function run on top of the implicit validation performed by the browser. Return a string with the validation message to mark the select as invalid, or null to mark it as valid.
197218
*/
@@ -255,6 +276,10 @@ export interface ScoutListViewItemCustomEvent<T> extends CustomEvent<T> {
255276
detail: T;
256277
target: HTMLScoutListViewItemElement;
257278
}
279+
export interface ScoutRadioButtonCustomEvent<T> extends CustomEvent<T> {
280+
detail: T;
281+
target: HTMLScoutRadioButtonElement;
282+
}
258283
export interface ScoutSelectCustomEvent<T> extends CustomEvent<T> {
259284
detail: T;
260285
target: HTMLScoutSelectElement;
@@ -325,7 +350,7 @@ declare global {
325350
new (): HTMLScoutCardElement;
326351
};
327352
interface HTMLScoutCheckboxElementEventMap {
328-
"scoutCheckboxChecked": {
353+
"scoutChecked": {
329354
checked: boolean;
330355
element: HTMLInputElement;
331356
};
@@ -431,6 +456,27 @@ declare global {
431456
prototype: HTMLScoutLoaderElement;
432457
new (): HTMLScoutLoaderElement;
433458
};
459+
interface HTMLScoutRadioButtonElementEventMap {
460+
"scoutChecked": {
461+
checked: boolean;
462+
element: HTMLInputElement;
463+
};
464+
"_fieldId": string;
465+
}
466+
interface HTMLScoutRadioButtonElement extends Components.ScoutRadioButton, HTMLStencilElement {
467+
addEventListener<K extends keyof HTMLScoutRadioButtonElementEventMap>(type: K, listener: (this: HTMLScoutRadioButtonElement, ev: ScoutRadioButtonCustomEvent<HTMLScoutRadioButtonElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
468+
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
469+
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
470+
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
471+
removeEventListener<K extends keyof HTMLScoutRadioButtonElementEventMap>(type: K, listener: (this: HTMLScoutRadioButtonElement, ev: ScoutRadioButtonCustomEvent<HTMLScoutRadioButtonElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
472+
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
473+
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
474+
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
475+
}
476+
var HTMLScoutRadioButtonElement: {
477+
prototype: HTMLScoutRadioButtonElement;
478+
new (): HTMLScoutRadioButtonElement;
479+
};
434480
interface HTMLScoutSelectElementEventMap {
435481
"scoutInputChange": {
436482
value: string;
@@ -494,6 +540,7 @@ declare global {
494540
"scout-list-view-item": HTMLScoutListViewItemElement;
495541
"scout-list-view-subheader": HTMLScoutListViewSubheaderElement;
496542
"scout-loader": HTMLScoutLoaderElement;
543+
"scout-radio-button": HTMLScoutRadioButtonElement;
497544
"scout-select": HTMLScoutSelectElement;
498545
"scout-stack": HTMLScoutStackElement;
499546
"scout-switch": HTMLScoutSwitchElement;
@@ -577,14 +624,16 @@ declare namespace LocalJSX {
577624
*/
578625
"disabled"?: boolean;
579626
"label"?: string;
580-
"onScoutCheckboxChecked"?: (event: ScoutCheckboxCustomEvent<{
627+
"name"?: string;
628+
"onScoutChecked"?: (event: ScoutCheckboxCustomEvent<{
581629
checked: boolean;
582630
element: HTMLInputElement;
583631
}>) => void;
584632
/**
585633
* Internal event used for form field association.
586634
*/
587635
"on_fieldId"?: (event: ScoutCheckboxCustomEvent<string>) => void;
636+
"value"?: string;
588637
}
589638
interface ScoutDivider {
590639
}
@@ -608,6 +657,7 @@ declare namespace LocalJSX {
608657
* Input mode hints for devices with dynamic keyboards.
609658
*/
610659
"inputmode"?: InputMode;
660+
"name"?: string;
611661
"onScoutBlur"?: (event: ScoutInputCustomEvent<void>) => void;
612662
"onScoutInputChange"?: (event: ScoutInputCustomEvent<{
613663
value: string;
@@ -694,12 +744,38 @@ declare namespace LocalJSX {
694744
"size"?: "xs" | "sm" | "base" | "lg" | "xl";
695745
"text"?: string;
696746
}
747+
interface ScoutRadioButton {
748+
/**
749+
* Use this prop if you need to connect your radio button with another element describing its use, other than the property label.
750+
*/
751+
"ariaLabelledby"?: string;
752+
/**
753+
* @default false
754+
*/
755+
"checked"?: boolean;
756+
/**
757+
* @default false
758+
*/
759+
"disabled"?: boolean;
760+
"label"?: string;
761+
"name"?: string;
762+
"onScoutChecked"?: (event: ScoutRadioButtonCustomEvent<{
763+
checked: boolean;
764+
element: HTMLInputElement;
765+
}>) => void;
766+
/**
767+
* Internal event used for form field association.
768+
*/
769+
"on_fieldId"?: (event: ScoutRadioButtonCustomEvent<string>) => void;
770+
"value"?: string;
771+
}
697772
interface ScoutSelect {
698773
/**
699774
* Whether the select is disabled. Disabled selects are not editable, excluded from tab order and are not validated.
700775
* @default false
701776
*/
702777
"disabled"?: boolean;
778+
"name"?: string;
703779
"onScoutBlur"?: (event: ScoutSelectCustomEvent<void>) => void;
704780
"onScoutInputChange"?: (event: ScoutSelectCustomEvent<{
705781
value: string;
@@ -769,6 +845,7 @@ declare namespace LocalJSX {
769845
"scout-list-view-item": ScoutListViewItem;
770846
"scout-list-view-subheader": ScoutListViewSubheader;
771847
"scout-loader": ScoutLoader;
848+
"scout-radio-button": ScoutRadioButton;
772849
"scout-select": ScoutSelect;
773850
"scout-stack": ScoutStack;
774851
"scout-switch": ScoutSwitch;
@@ -805,6 +882,7 @@ declare module "@stencil/core" {
805882
"scout-list-view-item": LocalJSX.ScoutListViewItem & JSXBase.HTMLAttributes<HTMLScoutListViewItemElement>;
806883
"scout-list-view-subheader": LocalJSX.ScoutListViewSubheader & JSXBase.HTMLAttributes<HTMLScoutListViewSubheaderElement>;
807884
"scout-loader": LocalJSX.ScoutLoader & JSXBase.HTMLAttributes<HTMLScoutLoaderElement>;
885+
"scout-radio-button": LocalJSX.ScoutRadioButton & JSXBase.HTMLAttributes<HTMLScoutRadioButtonElement>;
808886
"scout-select": LocalJSX.ScoutSelect & JSXBase.HTMLAttributes<HTMLScoutSelectElement>;
809887
"scout-stack": LocalJSX.ScoutStack & JSXBase.HTMLAttributes<HTMLScoutStackElement>;
810888
"scout-switch": LocalJSX.ScoutSwitch & JSXBase.HTMLAttributes<HTMLScoutSwitchElement>;

packages/ui-webc/src/components/checkbox/checkbox.css

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
.checkbox:checked:hover {
2626
background-color: var(--color-background-brand-hovered);
27-
border: 2px solid var(--color-background-brand-hovered);
27+
border-color: var(--color-background-brand-hovered);
2828
box-shadow: none;
2929
}
3030

@@ -67,12 +67,8 @@
6767

6868
label {
6969
display: flex;
70-
flex-direction: row-reverse;
7170
align-items: center;
71+
gap: var(--spacing-2);
7272
font: var(--type-label-base);
7373
color: var(--color-text-base);
7474
}
75-
76-
.inlineDivider {
77-
width: var(--spacing-2);
78-
}

packages/ui-webc/src/components/checkbox/checkbox.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ export class ScoutCheckbox {
2525

2626
@Prop() label: string;
2727

28+
@Prop() value: string;
29+
30+
@Prop() name: string;
31+
2832
@State() ariaId: string;
2933

30-
@Event() scoutCheckboxChecked: EventEmitter<{
34+
@Event() scoutChecked: EventEmitter<{
3135
checked: boolean;
3236
element: HTMLInputElement;
3337
}>;
@@ -41,38 +45,33 @@ export class ScoutCheckbox {
4145
this._fieldId.emit(this.ariaId);
4246
}
4347

44-
onClick(event: Event) {
48+
onChange(event: Event) {
4549
const checkbox = event.target as HTMLInputElement;
46-
console.log("checkbox", checkbox.checked);
4750

48-
this.scoutCheckboxChecked.emit({
51+
this.scoutChecked.emit({
4952
checked: checkbox.checked,
5053
element: checkbox,
5154
});
5255
}
53-
/*
54-
todo:
55-
- Wrap checkbox with label if used.
56-
- make sure it works with field nicely with label.
57-
*/
5856

5957
render() {
6058
const Tag = this.label?.length ? "label" : "div";
6159
return (
6260
<Tag>
63-
{this.label}
64-
<span class="inlineDivider"></span>
6561
<input
62+
id={this.ariaId}
63+
type="checkbox"
64+
value={this.value}
65+
name={this.name}
6666
class="checkbox"
67-
onChange={(event) => this.onClick(event)}
6867
style={{ "--icon-checkbox": `url(${checkIcon})` }}
69-
type="checkbox"
70-
id={this.ariaId}
7168
aria-labelledby={this.ariaLabelledby}
7269
aria-disabled={this.disabled}
7370
disabled={this.disabled}
7471
checked={this.checked}
72+
onChange={(event) => this.onChange(event)}
7573
/>
74+
{this.label}
7675
</Tag>
7776
);
7877
}

packages/ui-webc/src/components/checkbox/readme.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,16 @@
1111
| `checked` | `checked` | | `boolean` | `false` |
1212
| `disabled` | `disabled` | | `boolean` | `false` |
1313
| `label` | `label` | | `string` | `undefined` |
14+
| `name` | `name` | | `string` | `undefined` |
15+
| `value` | `value` | | `string` | `undefined` |
1416

1517

1618
## Events
1719

18-
| Event | Description | Type |
19-
| ---------------------- | ----------------------------------------------- | --------------------------------------------------------------- |
20-
| `_fieldId` | Internal event used for form field association. | `CustomEvent<string>` |
21-
| `scoutCheckboxChecked` | | `CustomEvent<{ checked: boolean; element: HTMLInputElement; }>` |
20+
| Event | Description | Type |
21+
| -------------- | ----------------------------------------------- | --------------------------------------------------------------- |
22+
| `_fieldId` | Internal event used for form field association. | `CustomEvent<string>` |
23+
| `scoutChecked` | | `CustomEvent<{ checked: boolean; element: HTMLInputElement; }>` |
2224

2325

2426
----------------------------------------------

0 commit comments

Comments
 (0)