Skip to content

Commit

Permalink
fix(input, combobox, input-date-picker, input-number, input-text, inp…
Browse files Browse the repository at this point in the history
…ut-time-picker, radio-button-group, segmented-control, select, text-area): provide clear field error messaging for AT (#9880)

**Related Issue:**
[#7792](#7792)

## Summary
Update components using validation to provide clear error messaging for
AT when fields are in error state.
  • Loading branch information
aPreciado88 authored Aug 15, 2024
1 parent 7e2764f commit 46ad7d2
Show file tree
Hide file tree
Showing 21 changed files with 95 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import { Validation } from "../functional/Validation";
import { IconNameOrString } from "../icon/interfaces";
import { ComboboxMessages } from "./assets/combobox/t9n";
import { ComboboxChildElement, SelectionDisplay } from "./interfaces";
import { ComboboxChildSelector, ComboboxItem, ComboboxItemGroup, CSS } from "./resources";
import { ComboboxChildSelector, ComboboxItem, ComboboxItemGroup, CSS, IDS } from "./resources";
import {
getItemAncestors,
getItemChildren,
Expand Down Expand Up @@ -1634,8 +1634,10 @@ export class Combobox
aria-activedescendant={this.activeDescendant}
aria-autocomplete="list"
aria-controls={`${listboxUidPrefix}${guid}`}
aria-errormessage={IDS.validationMessage}
aria-expanded={toAriaBoolean(open)}
aria-haspopup="listbox"
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
aria-owns={`${listboxUidPrefix}${guid}`}
class={{
Expand Down Expand Up @@ -1811,6 +1813,7 @@ export class Combobox
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ export const CSS = {
placeholderIcon: "placeholder-icon",
selectedIcon: "selected-icon",
};

export const IDS = {
validationMessage: "comboboxValidationMessage",
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface ValidationProps extends JSXBase.HTMLAttributes {
scale: Scale;
status: Status;
icon?: IconNameOrString | boolean;
id?: string;
message: string;
}

Expand All @@ -17,11 +18,12 @@ export const CSS = {
export const Validation: FunctionalComponent<ValidationProps> = ({
scale,
status,
id,
icon,
message,
}) => (
<div class={CSS.validationContainer}>
<calcite-input-message icon={icon} scale={scale} status={status}>
<calcite-input-message aria-live="polite" icon={icon} id={id} scale={scale} status={status}>
{message}
</calcite-input-message>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ import { syncHiddenFormInput } from "../input/common/input";
import { isBrowser } from "../../utils/browser";
import { normalizeToCurrentCentury, isTwoDigitYear } from "./utils";
import { InputDatePickerMessages } from "./assets/input-date-picker/t9n";
import { CSS } from "./resources";
import { CSS, IDS } from "./resources";

@Component({
tag: "calcite-input-date-picker",
Expand Down Expand Up @@ -569,8 +569,10 @@ export class InputDatePicker
aria-autocomplete="none"
aria-controls={this.dialogId}
aria-describedby={this.placeholderTextId}
aria-errormessage={IDS.validationMessage}
aria-expanded={toAriaBoolean(this.open)}
aria-haspopup="dialog"
aria-invalid={this.status === "invalid"}
class={{
[CSS.input]: true,
[CSS.inputNoBottomBorder]: this.layout === "vertical" && this.range,
Expand Down Expand Up @@ -662,8 +664,10 @@ export class InputDatePicker
<calcite-input-text
aria-autocomplete="none"
aria-controls={this.dialogId}
aria-errormessage={IDS.validationMessage}
aria-expanded={toAriaBoolean(this.open)}
aria-haspopup="dialog"
aria-invalid={this.status === "invalid"}
class={{
[CSS.input]: true,
[CSS.inputBorderTopColorOne]: this.layout === "vertical" && this.range,
Expand All @@ -690,6 +694,7 @@ export class InputDatePicker
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ export const CSS = {
verticalArrowContainer: "vertical-arrow-container",
chevronIcon: "chevron-icon",
};

export const IDS = {
validationMessage: "inputDatePickerValidationMessage",
};
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ import {
TextualInputComponent,
} from "../input/common/input";
import { IconNameOrString } from "../icon/interfaces";
import { CSS, SLOTS } from "./resources";
import { CSS, IDS, SLOTS } from "./resources";
import { InputNumberMessages } from "./assets/input-number/t9n";

/**
Expand Down Expand Up @@ -1091,6 +1091,8 @@ export class InputNumber

const childEl = (
<input
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
autocomplete={this.autocomplete}
autofocus={this.el.autofocus ? true : null}
Expand Down Expand Up @@ -1145,6 +1147,7 @@ export class InputNumber
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const CSS = {
numberButtonItem: "number-button-item",
};

export const IDS = {
validationMessage: "inputNumberValidationMessage",
};

export const SLOTS = {
action: "action",
};
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import { getIconScale } from "../../utils/component";
import { Validation } from "../functional/Validation";
import { syncHiddenFormInput, TextualInputComponent } from "../input/common/input";
import { IconNameOrString } from "../icon/interfaces";
import { CSS, SLOTS } from "./resources";
import { CSS, IDS, SLOTS } from "./resources";
import { InputTextMessages } from "./assets/input-text/t9n";

/**
Expand Down Expand Up @@ -673,6 +673,8 @@ export class InputText

const childEl = (
<input
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
autocomplete={this.autocomplete}
autofocus={this.el.autofocus ? true : null}
Expand Down Expand Up @@ -725,6 +727,7 @@ export class InputText
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export const CSS = {
resizeIconWrapper: "resize-icon-wrapper",
};

export const IDS = {
validationMessage: "inputTextValidationMessage",
};

export const SLOTS = {
action: "action",
};
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import { Validation } from "../functional/Validation";
import { focusFirstTabbable } from "../../utils/dom";
import { IconNameOrString } from "../icon/interfaces";
import { syncHiddenFormInput } from "../input/common/input";
import { CSS } from "./resources";
import { CSS, IDS } from "./resources";
import { InputTimePickerMessages } from "./assets/input-time-picker/t9n";

// some bundlers (e.g., Webpack) need dynamic import paths to be static
Expand Down Expand Up @@ -1015,7 +1015,9 @@ export class InputTimePicker
<div class="input-wrapper" onClick={this.onInputWrapperClick}>
<calcite-input-text
aria-autocomplete="none"
aria-errormessage={IDS.validationMessage}
aria-haspopup="dialog"
aria-invalid={this.status === "invalid"}
disabled={disabled}
icon="clock"
id={this.referenceElementId}
Expand Down Expand Up @@ -1062,6 +1064,7 @@ export class InputTimePicker
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const CSS = {
toggleIcon: "toggle-icon",
};

export const IDS = {
validationMessage: "inputTimePickerValidationMessage",
};
7 changes: 6 additions & 1 deletion packages/calcite-components/src/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import { Validation } from "../functional/Validation";
import { IconNameOrString } from "../icon/interfaces";
import { InputMessages } from "./assets/input/t9n";
import { InputPlacement, NumberNudgeDirection, SetValueOrigin } from "./interfaces";
import { CSS, INPUT_TYPE_ICONS, SLOTS } from "./resources";
import { CSS, IDS, INPUT_TYPE_ICONS, SLOTS } from "./resources";
import { NumericInputComponent, syncHiddenFormInput, TextualInputComponent } from "./common/input";

/**
Expand Down Expand Up @@ -1182,6 +1182,8 @@ export class Input
this.type === "number" ? (
<input
accept={this.accept}
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
autocomplete={this.autocomplete}
autofocus={autofocus}
Expand Down Expand Up @@ -1213,6 +1215,8 @@ export class Input
? [
<this.childElType
accept={this.accept}
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
autocomplete={this.autocomplete}
autofocus={autofocus}
Expand Down Expand Up @@ -1289,6 +1293,7 @@ export class Input
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
4 changes: 4 additions & 0 deletions packages/calcite-components/src/components/input/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export const CSS = {
numberButtonItem: "number-button-item",
};

export const IDS = {
validationMessage: "inputValidationMessage",
};

export const INPUT_TYPE_ICONS = {
tel: "phone",
password: "lock",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
} from "../../utils/loadable";
import { Validation } from "../functional/Validation";
import { IconNameOrString } from "../icon/interfaces";
import { CSS } from "./resources";
import { CSS, IDS } from "./resources";

/**
* @slot - A slot for adding `calcite-radio-button`s.
Expand Down Expand Up @@ -207,12 +207,17 @@ export class RadioButtonGroup implements LoadableComponent {
render(): VNode {
return (
<Host role="radiogroup">
<div class={CSS.itemWrapper}>
<div
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
class={CSS.itemWrapper}
>
<slot />
</div>
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const CSS = {
itemWrapper: "item-wrapper",
};

export const IDS = {
validationMessage: "radioButtonGroupValidationMessage",
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export const CSS = {
itemWrapper: "item-wrapper",
};

export const IDS = {
validationMessage: "segmentedControlValidationMessage",
};
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { createObserver } from "../../utils/observers";
import { Validation } from "../functional/Validation";
import { IconNameOrString } from "../icon/interfaces";
import { isBrowser } from "../../utils/browser";
import { CSS } from "./resources";
import { CSS, IDS } from "./resources";

/**
* @slot - A slot for adding `calcite-segmented-control-item`s.
Expand Down Expand Up @@ -202,7 +202,11 @@ export class SegmentedControl
render(): VNode {
return (
<Host onClick={this.handleClick} role="radiogroup">
<div class={CSS.itemWrapper}>
<div
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
class={CSS.itemWrapper}
>
<InteractiveContainer disabled={this.disabled}>
<slot />
<HiddenFormInputSlot component={this} />
Expand All @@ -211,6 +215,7 @@ export class SegmentedControl
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export const CSS = {
select: "select",
wrapper: "wrapper",
};

export const IDS = {
validationMessage: "selectValidationMessage",
};
5 changes: 4 additions & 1 deletion packages/calcite-components/src/components/select/select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { Scale, Status, Width } from "../interfaces";
import { getIconScale } from "../../utils/component";
import { Validation } from "../functional/Validation";
import { IconNameOrString } from "../icon/interfaces";
import { CSS } from "./resources";
import { CSS, IDS } from "./resources";

type OptionOrGroup = HTMLCalciteOptionElement | HTMLCalciteOptionGroupElement;
type NativeOptionOrGroup = HTMLOptionElement | HTMLOptGroupElement;
Expand Down Expand Up @@ -421,6 +421,8 @@ export class Select
<InteractiveContainer disabled={disabled}>
<div class={CSS.wrapper}>
<select
aria-errormessage={IDS.validationMessage}
aria-invalid={this.status === "invalid"}
aria-label={getLabelText(this)}
class={CSS.select}
disabled={disabled}
Expand All @@ -435,6 +437,7 @@ export class Select
{this.validationMessage && this.status === "invalid" ? (
<Validation
icon={this.validationIcon}
id={IDS.validationMessage}
message={this.validationMessage}
scale={this.scale}
status={this.status}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const CSS = {
footerEndSlotOnly: "footer--end-only",
};

export const IDS = {
validationMessage: "textAreaValidationMessage",
};

export const SLOTS = {
footerStart: "footer-start",
footerEnd: "footer-end",
Expand Down
Loading

0 comments on commit 46ad7d2

Please sign in to comment.