Skip to content
1 change: 1 addition & 0 deletions core/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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<any>,true
Expand Down
8 changes: 8 additions & 0 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>;
/**
* 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>`.
Expand Down Expand Up @@ -5447,6 +5451,10 @@ declare namespace LocalJSX {
* Emitted when the checkbox has focus.
*/
"onIonFocus"?: (event: IonCheckboxCustomEvent<void>) => 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 `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
*/
Expand Down
9 changes: 9 additions & 0 deletions core/src/components/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -182,6 +189,7 @@ export class Checkbox implements ComponentInterface {
name,
value,
alignment,
required,
} = this;
const mode = getIonMode(this);
const path = getSVGPath(mode, indeterminate);
Expand Down Expand Up @@ -218,6 +226,7 @@ export class Checkbox implements ComponentInterface {
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
ref={(focusEl) => (this.focusEl = focusEl)}
required={required}
{...inheritedAttributes}
/>
<div
Expand Down
30 changes: 30 additions & 0 deletions core/src/components/checkbox/test/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,33 @@ describe('ion-checkbox: indeterminate', () => {
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: `
<ion-checkbox required="true">Checkbox</ion-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: `
<ion-checkbox required="false">Checkbox</ion-checkbox>
`,
});

const checkbox = page.body.querySelector('ion-checkbox')!;
const nativeInput = checkbox.shadowRoot?.querySelector('input[type=checkbox]')!;

expect(nativeInput.hasAttribute('required')).toBeFalsy();
});
});
4 changes: 2 additions & 2 deletions packages/angular/src/directives/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '<ng-content></ng-content>',
// 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;
Expand Down
1 change: 1 addition & 0 deletions packages/vue/src/proxies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer<JSX.IonCheckbox, JSX.Io
'labelPlacement',
'justify',
'alignment',
'required',
'ionChange',
'ionFocus',
'ionBlur'
Expand Down
Loading