Skip to content

Commit 41da4c3

Browse files
feat(checkbox): add required prop (#30157)
Issue number: resolves internal --------- <!-- Please do not submit updates to dependencies unless it fixes an issue. --> <!-- Please try to limit your pull request to one type (bugfix, feature, etc). Submit multiple pull requests if needed. --> ## What is the current behavior? - Currently, the screen reader do not announce the component as required when `required={true}`. ## What is the new behavior? - Added a new `required` prop to be used for accessibility purposes that adds the `required` attribute to checkbox's inner native input. ## Does this introduce a breaking change? - [ ] Yes - [x] No <!-- If this introduces a breaking change: 1. Describe the impact and migration path for existing applications below. 2. Update the BREAKING.md file with the breaking change. 3. Add "BREAKING CHANGE: [...]" to the commit description when merging. See https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer for more information. -->
1 parent 0b54983 commit 41da4c3

File tree

6 files changed

+51
-2
lines changed

6 files changed

+51
-2
lines changed

core/api.txt

+1
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ ion-checkbox,prop,justify,"end" | "space-between" | "start" | undefined,undefine
403403
ion-checkbox,prop,labelPlacement,"end" | "fixed" | "stacked" | "start",'start',false,false
404404
ion-checkbox,prop,mode,"ios" | "md",undefined,false,false
405405
ion-checkbox,prop,name,string,this.inputId,false,false
406+
ion-checkbox,prop,required,boolean,false,false,false
406407
ion-checkbox,prop,value,any,'on',false,false
407408
ion-checkbox,event,ionBlur,void,true
408409
ion-checkbox,event,ionChange,CheckboxChangeEventDetail<any>,true

core/src/components.d.ts

+8
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,10 @@ export namespace Components {
643643
* The name of the control, which is submitted with the form data.
644644
*/
645645
"name": string;
646+
/**
647+
* If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid.
648+
*/
649+
"required": boolean;
646650
"setFocus": () => Promise<void>;
647651
/**
648652
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
@@ -5447,6 +5451,10 @@ declare namespace LocalJSX {
54475451
* Emitted when the checkbox has focus.
54485452
*/
54495453
"onIonFocus"?: (event: IonCheckboxCustomEvent<void>) => void;
5454+
/**
5455+
* If true, screen readers will announce it as a required field. This property works only for accessibility purposes, it will not prevent the form from submitting if the value is invalid.
5456+
*/
5457+
"required"?: boolean;
54505458
/**
54515459
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
54525460
*/

core/src/components/checkbox/checkbox.tsx

+9
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,13 @@ export class Checkbox implements ComponentInterface {
9898
*/
9999
@Prop() alignment?: 'start' | 'center';
100100

101+
/**
102+
* If true, screen readers will announce it as a required field. This property
103+
* works only for accessibility purposes, it will not prevent the form from
104+
* submitting if the value is invalid.
105+
*/
106+
@Prop() required = false;
107+
101108
/**
102109
* Emitted when the checked property has changed as a result of a user action such as a click.
103110
*
@@ -182,6 +189,7 @@ export class Checkbox implements ComponentInterface {
182189
name,
183190
value,
184191
alignment,
192+
required,
185193
} = this;
186194
const mode = getIonMode(this);
187195
const path = getSVGPath(mode, indeterminate);
@@ -218,6 +226,7 @@ export class Checkbox implements ComponentInterface {
218226
onFocus={() => this.onFocus()}
219227
onBlur={() => this.onBlur()}
220228
ref={(focusEl) => (this.focusEl = focusEl)}
229+
required={required}
221230
{...inheritedAttributes}
222231
/>
223232
<div

core/src/components/checkbox/test/checkbox.spec.ts

+30
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,33 @@ describe('ion-checkbox: indeterminate', () => {
5454
expect(checkbox.getAttribute('aria-checked')).toBe('mixed');
5555
});
5656
});
57+
58+
describe('ion-checkbox: required', () => {
59+
it('should have a required attribute in inner input when true', async () => {
60+
const page = await newSpecPage({
61+
components: [Checkbox],
62+
html: `
63+
<ion-checkbox required="true">Checkbox</ion-checkbox>
64+
`,
65+
});
66+
67+
const checkbox = page.body.querySelector('ion-checkbox')!;
68+
const nativeInput = checkbox.shadowRoot?.querySelector('input[type=checkbox]')!;
69+
70+
expect(nativeInput.hasAttribute('required')).toBeTruthy();
71+
});
72+
73+
it('should not have a required attribute in inner input when false', async () => {
74+
const page = await newSpecPage({
75+
components: [Checkbox],
76+
html: `
77+
<ion-checkbox required="false">Checkbox</ion-checkbox>
78+
`,
79+
});
80+
81+
const checkbox = page.body.querySelector('ion-checkbox')!;
82+
const nativeInput = checkbox.shadowRoot?.querySelector('input[type=checkbox]')!;
83+
84+
expect(nativeInput.hasAttribute('required')).toBeFalsy();
85+
});
86+
});

packages/angular/src/directives/proxies.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -507,14 +507,14 @@ export declare interface IonCardTitle extends Components.IonCardTitle {}
507507

508508

509509
@ProxyCmp({
510-
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value']
510+
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value']
511511
})
512512
@Component({
513513
selector: 'ion-checkbox',
514514
changeDetection: ChangeDetectionStrategy.OnPush,
515515
template: '<ng-content></ng-content>',
516516
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
517-
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value'],
517+
inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value'],
518518
})
519519
export class IonCheckbox {
520520
protected el: HTMLIonCheckboxElement;

packages/vue/src/proxies.ts

+1
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer<JSX.IonCheckbox, JSX.Io
235235
'labelPlacement',
236236
'justify',
237237
'alignment',
238+
'required',
238239
'ionChange',
239240
'ionFocus',
240241
'ionBlur'

0 commit comments

Comments
 (0)