Skip to content

Commit 0827a4c

Browse files
committed
feat(radio-group): add helperText and errorText properties
1 parent 41da4c3 commit 0827a4c

File tree

7 files changed

+150
-3
lines changed

7 files changed

+150
-3
lines changed

core/src/components.d.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2307,6 +2307,14 @@ export namespace Components {
23072307
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
23082308
*/
23092309
"compareWith"?: string | RadioGroupCompareFn | null;
2310+
/**
2311+
* The error text to display at the top of the radio group.
2312+
*/
2313+
"errorText"?: string;
2314+
/**
2315+
* The helper text to display at the top of the radio group.
2316+
*/
2317+
"helperText"?: string;
23102318
/**
23112319
* The name of the control, which is submitted with the form data.
23122320
*/
@@ -7079,6 +7087,14 @@ declare namespace LocalJSX {
70797087
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
70807088
*/
70817089
"compareWith"?: string | RadioGroupCompareFn | null;
7090+
/**
7091+
* The error text to display at the top of the radio group.
7092+
*/
7093+
"errorText"?: string;
7094+
/**
7095+
* The helper text to display at the top of the radio group.
7096+
*/
7097+
"helperText"?: string;
70827098
/**
70837099
* The name of the control, which is submitted with the form data.
70847100
*/
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@import "../../themes/ionic.globals.ios";
2+
@import "./radio-group";
3+
@import "../item/item.ios.vars";
4+
5+
// iOS Radio Group in List
6+
// --------------------------------------------------
7+
8+
// Add padding to the error and helper text when used in a
9+
// list to align them with the list header and item text.
10+
ion-list .supporting-text {
11+
@include padding-horizontal($item-ios-padding-start, $item-ios-padding-end);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@import "../../themes/ionic.globals.md";
2+
@import "./radio-group";
3+
@import "../item/item.md.vars";
4+
5+
// Material Design Radio Group in List
6+
// --------------------------------------------------
7+
8+
// Add padding to the error and helper text when used in a
9+
// list to align them with the list header and item text.
10+
ion-list .supporting-text {
11+
@include padding-horizontal($item-md-padding-start, $item-md-padding-end);
12+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
@import "../../themes/ionic.globals";
2+
3+
// Radio Group
4+
// --------------------------------------------------
5+
6+
ion-radio-group {
7+
display: block;
8+
}
9+
10+
// Radio Group: Supporting Text
11+
// --------------------------------------------------
12+
13+
.supporting-text {
14+
line-height: 1.5;
15+
}
16+
17+
/**
18+
* Error text should only be shown when .ion-invalid is
19+
* present on the radio group. Otherwise the helper text should
20+
* be shown.
21+
*/
22+
.error-text {
23+
display: none;
24+
25+
color: var(--ion-color-danger, #eb445a);
26+
}
27+
28+
.helper-text {
29+
display: block;
30+
31+
color: var(--ion-color-step-550, #737373);
32+
}
33+
34+
.ion-touched.ion-invalid .error-text {
35+
display: block;
36+
}
37+
38+
.ion-touched.ion-invalid .helper-text {
39+
display: none;
40+
}

core/src/components/radio-group/radio-group.tsx

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@ import type { RadioGroupChangeEventDetail, RadioGroupCompareFn } from './radio-g
88

99
@Component({
1010
tag: 'ion-radio-group',
11+
styleUrls: {
12+
ios: 'radio-group.ios.scss',
13+
md: 'radio-group.md.scss',
14+
},
1115
})
1216
export class RadioGroup implements ComponentInterface {
1317
private inputId = `ion-rg-${radioGroupIds++}`;
18+
private helperTextId = `${this.inputId}-helper-text`;
19+
private errorTextId = `${this.inputId}-error-text`;
1420
private labelId = `${this.inputId}-lbl`;
1521
private label?: HTMLIonLabelElement | null;
1622

@@ -39,6 +45,16 @@ export class RadioGroup implements ComponentInterface {
3945
*/
4046
@Prop({ mutable: true }) value?: any | null;
4147

48+
/**
49+
* The helper text to display at the top of the radio group.
50+
*/
51+
@Prop() helperText?: string;
52+
53+
/**
54+
* The error text to display at the top of the radio group.
55+
*/
56+
@Prop() errorText?: string;
57+
4258
@Watch('value')
4359
valueChanged(value: any | undefined) {
4460
this.setRadioTabindex(value);
@@ -224,13 +240,62 @@ export class RadioGroup implements ComponentInterface {
224240
radioToFocus?.setFocus();
225241
}
226242

243+
/**
244+
* Renders the helper text or error text values
245+
*/
246+
private renderHintText() {
247+
const { helperText, errorText, helperTextId, errorTextId } = this;
248+
249+
const hasHintText = !!helperText || !!errorText;
250+
if (!hasHintText) {
251+
return;
252+
}
253+
254+
return (
255+
<div class="supporting-text">
256+
<div id={helperTextId} class="helper-text">
257+
{helperText}
258+
</div>
259+
<div id={errorTextId} class="error-text">
260+
{errorText}
261+
</div>
262+
</div>
263+
);
264+
}
265+
266+
private getHintTextID(): string | undefined {
267+
const { el, helperText, errorText, helperTextId, errorTextId } = this;
268+
269+
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
270+
return errorTextId;
271+
}
272+
273+
if (helperText) {
274+
return helperTextId;
275+
}
276+
277+
return undefined;
278+
}
279+
227280
render() {
228281
const { label, labelId, el, name, value } = this;
229282
const mode = getIonMode(this);
230283

231284
renderHiddenInput(true, el, name, value, false);
232285

233-
return <Host role="radiogroup" aria-labelledby={label ? labelId : null} onClick={this.onClick} class={mode}></Host>;
286+
return (
287+
<Host
288+
role="radiogroup"
289+
aria-labelledby={label ? labelId : null}
290+
aria-describedby={this.getHintTextID()}
291+
aria-invalid={this.getHintTextID() === this.errorTextId}
292+
onClick={this.onClick}
293+
class={mode}
294+
>
295+
{this.renderHintText()}
296+
<slot></slot>
297+
</Host>
298+
);
234299
}
235300
}
236301

packages/angular/src/directives/proxies.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,14 +1639,14 @@ export declare interface IonRadio extends Components.IonRadio {
16391639

16401640

16411641
@ProxyCmp({
1642-
inputs: ['allowEmptySelection', 'compareWith', 'name', 'value']
1642+
inputs: ['allowEmptySelection', 'compareWith', 'errorText', 'helperText', 'name', 'value']
16431643
})
16441644
@Component({
16451645
selector: 'ion-radio-group',
16461646
changeDetection: ChangeDetectionStrategy.OnPush,
16471647
template: '<ng-content></ng-content>',
16481648
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
1649-
inputs: ['allowEmptySelection', 'compareWith', 'name', 'value'],
1649+
inputs: ['allowEmptySelection', 'compareWith', 'errorText', 'helperText', 'name', 'value'],
16501650
})
16511651
export class IonRadioGroup {
16521652
protected el: HTMLIonRadioGroupElement;

packages/vue/src/proxies.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,8 @@ export const IonRadioGroup = /*@__PURE__*/ defineContainer<JSX.IonRadioGroup, JS
696696
'compareWith',
697697
'name',
698698
'value',
699+
'helperText',
700+
'errorText',
699701
'ionChange',
700702
'ionValueChange'
701703
], [

0 commit comments

Comments
 (0)