diff --git a/core/api.txt b/core/api.txt index f895ecd5422..14736c3556d 100644 --- a/core/api.txt +++ b/core/api.txt @@ -403,6 +403,7 @@ ion-checkbox,prop,justify,"end" | "space-between" | "start" | undefined,undefine ion-checkbox,prop,labelPlacement,"end" | "fixed" | "stacked" | "start",'start',false,false ion-checkbox,prop,mode,"ios" | "md",undefined,false,false ion-checkbox,prop,name,string,this.inputId,false,false +ion-checkbox,prop,required,boolean,false,false,false ion-checkbox,prop,value,any,'on',false,false ion-checkbox,event,ionBlur,void,true ion-checkbox,event,ionChange,CheckboxChangeEventDetail,true diff --git a/core/src/components.d.ts b/core/src/components.d.ts index 1fe676da80f..b7675233d18 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -643,6 +643,10 @@ export namespace Components { * The name of the control, which is submitted with the form data. */ "name": string; + /** + * 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. + */ + "required": boolean; "setFocus": () => Promise; /** * 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 ``, it's only used when the checkbox participates in a native `
`. @@ -5447,6 +5451,10 @@ declare namespace LocalJSX { * Emitted when the checkbox has focus. */ "onIonFocus"?: (event: IonCheckboxCustomEvent) => void; + /** + * 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. + */ + "required"?: boolean; /** * 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 ``, it's only used when the checkbox participates in a native ``. */ diff --git a/core/src/components/checkbox/checkbox.tsx b/core/src/components/checkbox/checkbox.tsx index 78206b9fd16..2e05e366f70 100644 --- a/core/src/components/checkbox/checkbox.tsx +++ b/core/src/components/checkbox/checkbox.tsx @@ -98,6 +98,13 @@ export class Checkbox implements ComponentInterface { */ @Prop() alignment?: 'start' | 'center'; + /** + * 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. + */ + @Prop() required = false; + /** * Emitted when the checked property has changed as a result of a user action such as a click. * @@ -182,6 +189,7 @@ export class Checkbox implements ComponentInterface { name, value, alignment, + required, } = this; const mode = getIonMode(this); const path = getSVGPath(mode, indeterminate); @@ -218,6 +226,7 @@ export class Checkbox implements ComponentInterface { onFocus={() => this.onFocus()} onBlur={() => this.onBlur()} ref={(focusEl) => (this.focusEl = focusEl)} + required={required} {...inheritedAttributes} />
{ expect(checkbox.getAttribute('aria-checked')).toBe('mixed'); }); }); + +describe('ion-checkbox: required', () => { + it('should have a required attribute in inner input when true', async () => { + const page = await newSpecPage({ + components: [Checkbox], + html: ` + Checkbox + `, + }); + + const checkbox = page.body.querySelector('ion-checkbox')!; + const nativeInput = checkbox.shadowRoot?.querySelector('input[type=checkbox]')!; + + expect(nativeInput.hasAttribute('required')).toBeTruthy(); + }); + + it('should not have a required attribute in inner input when false', async () => { + const page = await newSpecPage({ + components: [Checkbox], + html: ` + Checkbox + `, + }); + + const checkbox = page.body.querySelector('ion-checkbox')!; + const nativeInput = checkbox.shadowRoot?.querySelector('input[type=checkbox]')!; + + expect(nativeInput.hasAttribute('required')).toBeFalsy(); + }); +}); diff --git a/packages/angular/src/directives/proxies.ts b/packages/angular/src/directives/proxies.ts index e4e2486a9b8..9d6d23b5c17 100644 --- a/packages/angular/src/directives/proxies.ts +++ b/packages/angular/src/directives/proxies.ts @@ -507,14 +507,14 @@ export declare interface IonCardTitle extends Components.IonCardTitle {} @ProxyCmp({ - inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value'] + inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value'] }) @Component({ selector: 'ion-checkbox', changeDetection: ChangeDetectionStrategy.OnPush, template: '', // eslint-disable-next-line @angular-eslint/no-inputs-metadata-property - inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'value'], + inputs: ['alignment', 'checked', 'color', 'disabled', 'indeterminate', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value'], }) export class IonCheckbox { protected el: HTMLIonCheckboxElement; diff --git a/packages/vue/src/proxies.ts b/packages/vue/src/proxies.ts index 7f2dc87b884..a866edde10a 100644 --- a/packages/vue/src/proxies.ts +++ b/packages/vue/src/proxies.ts @@ -235,6 +235,7 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer