Compare commits

...

32 Commits

Author SHA1 Message Date
giuliana
6d5592e63c Merge branch 'ROU-11112-toggle' into ROU-11112 2025-01-30 10:43:40 +00:00
giuliana
f94bcadc49 Merge branch 'ROU-11112-checkbox' into ROU-11112 2025-01-30 10:43:00 +00:00
giuliana
2dd8a18bb4 Merge branch 'ROU-11112-select' into ROU-11112 2025-01-30 10:40:59 +00:00
giuliana
e296707c94 chore(vue): update proxies file 2025-01-30 10:14:23 +00:00
giuliana
0a502a196f chore(vue): update proxies file 2025-01-30 10:12:20 +00:00
giuliana
e8971df5fe chore(vue): update proxies file 2025-01-30 09:53:38 +00:00
Giuliana Silva
8743a4f3be Merge branch 'feature-8.5' into ROU-11112-checkbox 2025-01-30 09:34:05 +00:00
Giuliana Silva
7f45146ce3 Merge branch 'feature-8.5' into ROU-11112-select 2025-01-30 09:34:01 +00:00
Giuliana Silva
8abf12676a Merge branch 'feature-8.5' into ROU-11112-toggle 2025-01-30 09:33:58 +00:00
Giuliana Silva
66210e60a8 Merge branch 'feature-8.5' into ROU-11112-checkbox 2025-01-24 18:11:23 +00:00
Giuliana Silva
b31b97648c Merge branch 'feature-8.5' into ROU-11112-select 2025-01-24 18:10:42 +00:00
Giuliana Silva
856a5a051a Merge branch 'feature-8.5' into ROU-11112-toggle 2025-01-24 18:09:00 +00:00
giuliana
dfae50cba4 test(toggle): remove unecessary import 2025-01-24 12:40:22 +00:00
giuliana
80d4fa63ec lint 2025-01-24 12:34:59 +00:00
giuliana
72494dbe31 lint 2025-01-24 12:32:15 +00:00
giuliana
d407de5d6c test(select): add required prop tests 2025-01-24 12:19:00 +00:00
giuliana
82eb743eab test(toggle): add required prop tests 2025-01-24 12:10:20 +00:00
giuliana
397c72aadd test(checkbox): add required prop tests 2025-01-24 12:05:41 +00:00
giuliana
b423ae7c8c feat(checkbox): change required prop description 2025-01-24 11:30:04 +00:00
giuliana
8ecb96e737 feat(toggle): change required prop description 2025-01-24 11:27:42 +00:00
giuliana
d1bc696f9f feat(select): change required prop description 2025-01-24 11:24:22 +00:00
giuliana
4af07f3c02 chore(): add files after build 2025-01-23 15:07:11 +00:00
giuliana
1cf303f384 chore(): add files after build 2025-01-23 15:06:11 +00:00
giuliana
e31e5fdca5 chore(): add files after build 2025-01-23 15:04:42 +00:00
giuliana
1d5cd4b6b3 feat(select): add required prop 2025-01-23 14:57:08 +00:00
giuliana
76fa00d021 feat(toggle): add required prop 2025-01-23 14:56:51 +00:00
giuliana
3f46aef949 feat(checkbox): add required prop 2025-01-23 14:56:33 +00:00
giuliana
934dd0f303 chore(): build project 2025-01-23 11:50:20 +00:00
giuliana
3bc976b8c6 feat(toggle): add required prop 2025-01-23 11:46:53 +00:00
giuliana
3f24a126bf feat(select): add required prop 2025-01-23 11:46:33 +00:00
giuliana
ed3788271b feat(radio-group): add required prop 2025-01-23 11:45:36 +00:00
giuliana
00961c6e4b feat(checkbox): add required prop 2025-01-23 11:44:36 +00:00
11 changed files with 175 additions and 12 deletions

View File

@@ -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
@@ -1346,6 +1347,7 @@ ion-radio-group,none
ion-radio-group,prop,allowEmptySelection,boolean,false,false,false
ion-radio-group,prop,compareWith,((currentValue: any, compareValue: any) => boolean) | null | string | undefined,undefined,false,false
ion-radio-group,prop,name,string,this.inputId,false,false
ion-radio-group,prop,required,boolean,false,false,false
ion-radio-group,prop,value,any,undefined,false,false
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail<any>,true
@@ -1631,6 +1633,7 @@ ion-select,prop,multiple,boolean,false,false,false
ion-select,prop,name,string,this.inputId,false,false
ion-select,prop,okText,string,'OK',false,false
ion-select,prop,placeholder,string | undefined,undefined,false,false
ion-select,prop,required,boolean,false,false,false
ion-select,prop,selectedText,null | string | undefined,undefined,false,false
ion-select,prop,shape,"round" | undefined,undefined,false,false
ion-select,prop,toggleIcon,string | undefined,undefined,false,false
@@ -1944,6 +1947,7 @@ ion-toggle,prop,justify,"end" | "space-between" | "start" | undefined,undefined,
ion-toggle,prop,labelPlacement,"end" | "fixed" | "stacked" | "start",'start',false,false
ion-toggle,prop,mode,"ios" | "md",undefined,false,false
ion-toggle,prop,name,string,this.inputId,false,false
ion-toggle,prop,required,boolean,false,false,false
ion-toggle,prop,value,null | string | undefined,'on',false,false
ion-toggle,event,ionBlur,void,true
ion-toggle,event,ionChange,ToggleChangeEventDetail<any>,true

View File

@@ -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>`.
@@ -2303,6 +2307,10 @@ export namespace Components {
* The name of the control, which is submitted with the form data.
*/
"name": string;
/**
* If `true`, the user must fill in a value before submitting a form.
*/
"required": boolean;
"setFocus": () => Promise<void>;
/**
* the value of the radio group.
@@ -2808,6 +2816,10 @@ export namespace Components {
* The text to display when the select is empty.
*/
"placeholder"?: 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;
/**
* The text to display instead of the selected option's value.
*/
@@ -3280,6 +3292,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;
/**
* The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
*/
@@ -5435,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>`.
*/
@@ -7067,6 +7087,10 @@ declare namespace LocalJSX {
* Emitted when the `value` property has changed. This is used to ensure that `ion-radio` can respond to any value property changes from the group.
*/
"onIonValueChange"?: (event: IonRadioGroupCustomEvent<RadioGroupChangeEventDetail>) => void;
/**
* If `true`, the user must fill in a value before submitting a form.
*/
"required"?: boolean;
/**
* the value of the radio group.
*/
@@ -7640,6 +7664,10 @@ declare namespace LocalJSX {
* The text to display when the select is empty.
*/
"placeholder"?: 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;
/**
* The text to display instead of the selected option's value.
*/
@@ -8155,6 +8183,10 @@ declare namespace LocalJSX {
* Emitted when the toggle has focus.
*/
"onIonFocus"?: (event: IonToggleCustomEvent<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 toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
*/

View File

@@ -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}
/>
<div

View File

@@ -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();
});
});

View File

@@ -34,6 +34,11 @@ export class RadioGroup implements ComponentInterface {
*/
@Prop() name: string = this.inputId;
/**
* If `true`, the user must fill in a value before submitting a form.
*/
@Prop() required = false;
/**
* the value of the radio group.
*/
@@ -225,12 +230,12 @@ export class RadioGroup implements ComponentInterface {
}
render() {
const { label, labelId, el, name, value } = this;
const { label, labelId, el, name, value, required } = this;
const mode = getIonMode(this);
renderHiddenInput(true, el, name, value, false);
return <Host role="radiogroup" aria-labelledby={label ? labelId : null} onClick={this.onClick} class={mode}></Host>;
return <Host role="radiogroup" aria-labelledby={label ? labelId : null} onClick={this.onClick} class={mode} aria-required={`${required}`}></Host>;
}
}

View File

@@ -196,6 +196,13 @@ export class Select implements ComponentInterface {
*/
@Prop({ mutable: true }) value?: any | null;
/**
* 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 value has changed.
*
@@ -974,7 +981,7 @@ export class Select implements ComponentInterface {
}
private renderListbox() {
const { disabled, inputId, isExpanded } = this;
const { disabled, inputId, isExpanded, required } = this;
return (
<button
@@ -983,6 +990,7 @@ export class Select implements ComponentInterface {
aria-label={this.ariaLabel}
aria-haspopup="dialog"
aria-expanded={`${isExpanded}`}
aria-required={`${required}`}
onFocus={this.onFocus}
onBlur={this.onBlur}
ref={(focusEl) => (this.focusEl = focusEl)}

View File

@@ -125,3 +125,35 @@ describe('select: slot interactivity', () => {
expect(divSpy).toHaveBeenCalled();
});
});
describe('ion-select: required', () => {
it('should have a aria-required attribute as true in inner button', async () => {
const page = await newSpecPage({
components: [Select],
html: `
<ion-select required="true"></ion-select>
`,
});
const select = page.body.querySelector('ion-select')!;
const nativeButton = select.shadowRoot!.querySelector('button')!;
expect(nativeButton.getAttribute('aria-required')).toBe('true');
});
it('should not have a aria-required attribute as false in inner button', async () => {
const page = await newSpecPage({
components: [Select],
html: `
<ion-select required="false"></ion-select>
`,
});
const select = page.body.querySelector('ion-select')!;
const nativeButton = select.shadowRoot!.querySelector('button')!;
expect(nativeButton.getAttribute('aria-required')).toBe('false');
});
});

View File

@@ -75,3 +75,33 @@ describe('ion-toggle: disabled', () => {
expect(toggle.checked).toBe(false);
});
});
describe('ion-toggle: required', () => {
it('should have a required attribute in inner input when true', async () => {
const page = await newSpecPage({
components: [Toggle],
html: `
<ion-toggle required="true">Toggle</ion-toggle>
`,
});
const toggle = page.body.querySelector('ion-toggle')!;
const nativeInput = toggle.shadowRoot?.querySelector('input[role=switch]')!;
expect(nativeInput.hasAttribute('required')).toBeTruthy();
});
it('should not have a required attribute in inner input when false', async () => {
const page = await newSpecPage({
components: [Toggle],
html: `
<ion-toggle required="false">Toggle</ion-toggle>
`,
});
const toggle = page.body.querySelector('ion-toggle')!;
const nativeInput = toggle.shadowRoot?.querySelector('input[role=switch]')!;
expect(nativeInput.hasAttribute('required')).toBeFalsy();
});
});

View File

@@ -108,6 +108,13 @@ export class Toggle 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 user switches the toggle on or off.
*
@@ -290,7 +297,8 @@ export class Toggle implements ComponentInterface {
}
render() {
const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment } = this;
const { activated, color, checked, disabled, el, justify, labelPlacement, inputId, name, alignment, required } =
this;
const mode = getIonMode(this);
const value = this.getValue();
@@ -327,6 +335,7 @@ export class Toggle implements ComponentInterface {
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
ref={(focusEl) => (this.focusEl = focusEl)}
required={required}
{...this.inheritedAttributes}
/>
<div

View File

@@ -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;
@@ -1639,14 +1639,14 @@ export declare interface IonRadio extends Components.IonRadio {
@ProxyCmp({
inputs: ['allowEmptySelection', 'compareWith', 'name', 'value']
inputs: ['allowEmptySelection', 'compareWith', 'name', 'required', 'value']
})
@Component({
selector: 'ion-radio-group',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['allowEmptySelection', 'compareWith', 'name', 'value'],
inputs: ['allowEmptySelection', 'compareWith', 'name', 'required', 'value'],
})
export class IonRadioGroup {
protected el: HTMLIonRadioGroupElement;
@@ -2060,7 +2060,7 @@ export declare interface IonSegmentView extends Components.IonSegmentView {
@ProxyCmp({
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'],
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'required', 'selectedText', 'shape', 'toggleIcon', 'value'],
methods: ['open']
})
@Component({
@@ -2068,7 +2068,7 @@ export declare interface IonSegmentView extends Components.IonSegmentView {
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'selectedText', 'shape', 'toggleIcon', 'value'],
inputs: ['cancelText', 'color', 'compareWith', 'disabled', 'expandedIcon', 'fill', 'interface', 'interfaceOptions', 'justify', 'label', 'labelPlacement', 'mode', 'multiple', 'name', 'okText', 'placeholder', 'required', 'selectedText', 'shape', 'toggleIcon', 'value'],
})
export class IonSelect {
protected el: HTMLIonSelectElement;
@@ -2473,14 +2473,14 @@ Shorthand for ionToastDidDismiss.
@ProxyCmp({
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'value']
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value']
})
@Component({
selector: 'ion-toggle',
changeDetection: ChangeDetectionStrategy.OnPush,
template: '<ng-content></ng-content>',
// eslint-disable-next-line @angular-eslint/no-inputs-metadata-property
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'value'],
inputs: ['alignment', 'checked', 'color', 'disabled', 'enableOnOffLabels', 'justify', 'labelPlacement', 'mode', 'name', 'required', 'value'],
})
export class IonToggle {
protected el: HTMLIonToggleElement;

View File

@@ -235,6 +235,7 @@ export const IonCheckbox = /*@__PURE__*/ defineContainer<JSX.IonCheckbox, JSX.Io
'labelPlacement',
'justify',
'alignment',
'required',
'ionChange',
'ionFocus',
'ionBlur'
@@ -694,6 +695,7 @@ export const IonRadioGroup = /*@__PURE__*/ defineContainer<JSX.IonRadioGroup, JS
'allowEmptySelection',
'compareWith',
'name',
'required',
'value',
'ionChange',
'ionValueChange'
@@ -883,6 +885,7 @@ export const IonSelect = /*@__PURE__*/ defineContainer<JSX.IonSelect, JSX.IonSel
'expandedIcon',
'shape',
'value',
'required',
'ionChange',
'ionCancel',
'ionDismiss',
@@ -1016,6 +1019,7 @@ export const IonToggle = /*@__PURE__*/ defineContainer<JSX.IonToggle, JSX.IonTog
'labelPlacement',
'justify',
'alignment',
'required',
'ionChange',
'ionFocus',
'ionBlur'