diff --git a/packages/calcite-components/src/components/checkbox/checkbox.scss b/packages/calcite-components/src/components/checkbox/checkbox.scss
index 971b7d822ff..0d7d88f1f93 100644
--- a/packages/calcite-components/src/components/checkbox/checkbox.scss
+++ b/packages/calcite-components/src/components/checkbox/checkbox.scss
@@ -43,6 +43,16 @@
color: theme("backgroundColor.background");
}
}
+
+:host([status="invalid"]:not([checked])) {
+ .check-svg {
+ box-shadow: inset 0 0 0 1px var(--calcite-ui-danger);
+ }
+ .toggle:focus {
+ @apply focus-outset-danger;
+ }
+}
+
:host([checked]),
:host([indeterminate]) {
.check-svg {
diff --git a/packages/calcite-components/src/components/checkbox/checkbox.stories.ts b/packages/calcite-components/src/components/checkbox/checkbox.stories.ts
index c3fc522678d..2d171026c67 100644
--- a/packages/calcite-components/src/components/checkbox/checkbox.stories.ts
+++ b/packages/calcite-components/src/components/checkbox/checkbox.stories.ts
@@ -19,6 +19,7 @@ export const simple = (): string => html`
${boolean("disabled", false)}
${boolean("indeterminate", false)}
scale="${select("scale", ["s", "m", "l"], "m")}"
+ status="${select("status", ["idle", "invalid", "valid"], "idle")}"
>
${text("label", "Checkbox")}
diff --git a/packages/calcite-components/src/components/checkbox/checkbox.tsx b/packages/calcite-components/src/components/checkbox/checkbox.tsx
index 9f9f6cb89e4..7199fff7cdf 100644
--- a/packages/calcite-components/src/components/checkbox/checkbox.tsx
+++ b/packages/calcite-components/src/components/checkbox/checkbox.tsx
@@ -31,7 +31,7 @@ import {
setComponentLoaded,
setUpLoadableComponent,
} from "../../utils/loadable";
-import { Scale } from "../interfaces";
+import { Scale, Status } from "../interfaces";
@Component({
tag: "calcite-checkbox",
@@ -96,6 +96,9 @@ export class Checkbox
/** Specifies the size of the component. */
@Prop({ reflect: true }) scale: Scale = "m";
+ /** Specifies the status of the input field, which determines message and icons. */
+ @Prop({ reflect: true }) status: Status = "idle";
+
/** The component's value. */
@Prop() value: any;
diff --git a/packages/calcite-components/src/components/combobox/combobox.scss b/packages/calcite-components/src/components/combobox/combobox.scss
index 72c01847b71..1178f6c8ef1 100644
--- a/packages/calcite-components/src/components/combobox/combobox.scss
+++ b/packages/calcite-components/src/components/combobox/combobox.scss
@@ -66,6 +66,14 @@
@apply focus-inset;
}
+:host([status="invalid"]) .wrapper {
+ @apply border-color-danger;
+}
+
+:host([status="invalid"]:focus-within) .wrapper {
+ @apply focus-inset-danger;
+}
+
.wrapper--single {
padding-block: 0;
padding-inline: var(--calcite-combobox-item-spacing-unit-l);
diff --git a/packages/calcite-components/src/components/combobox/combobox.stories.ts b/packages/calcite-components/src/components/combobox/combobox.stories.ts
index 36ff8ec8f67..0619cb18bf8 100644
--- a/packages/calcite-components/src/components/combobox/combobox.stories.ts
+++ b/packages/calcite-components/src/components/combobox/combobox.stories.ts
@@ -23,6 +23,7 @@ export const single = (): string => html`
max-items="${number("max-items", 0)}"
placeholder="${text("placeholder", "placeholder")}"
scale="${select("scale", ["s", "m", "l"], "m")}"
+ status="${select("status", ["idle", "invalid", "valid"], "idle")}"
>
@@ -296,6 +297,7 @@ export const nestedItems = (): string => html`
${boolean("disabled", false)}
${boolean("allow-custom-values", false)}
max-items="${number("max-items", 0)}"
+ status="${select("status", ["idle", "invalid", "valid"], "idle")}"
>
diff --git a/packages/calcite-components/src/components/combobox/combobox.tsx b/packages/calcite-components/src/components/combobox/combobox.tsx
index 5ab414a4814..cabc7664d76 100644
--- a/packages/calcite-components/src/components/combobox/combobox.tsx
+++ b/packages/calcite-components/src/components/combobox/combobox.tsx
@@ -66,7 +66,7 @@ import {
T9nComponent,
updateMessages,
} from "../../utils/t9n";
-import { Scale, SelectionMode } from "../interfaces";
+import { Scale, SelectionMode, Status } from "../interfaces";
import { ComboboxMessages } from "./assets/combobox/t9n";
import { ComboboxChildElement, SelectionDisplay } from "./interfaces";
import { ComboboxChildSelector, ComboboxItem, ComboboxItemGroup, CSS } from "./resources";
@@ -224,6 +224,9 @@ export class Combobox
/** Specifies the size of the component. */
@Prop({ reflect: true }) scale: Scale = "m";
+ /** Specifies the status of the input field, which determines message and icons. */
+ @Prop({ reflect: true }) status: Status = "idle";
+
@Watch("selectionMode")
@Watch("scale")
handlePropsChange(): void {
diff --git a/packages/calcite-components/src/components/input-text/input-text.stories.ts b/packages/calcite-components/src/components/input-text/input-text.stories.ts
index db813e2eed8..6ee29465529 100644
--- a/packages/calcite-components/src/components/input-text/input-text.stories.ts
+++ b/packages/calcite-components/src/components/input-text/input-text.stories.ts
@@ -17,7 +17,6 @@ export const simple = (): string => html`
html`
${boolean("disabled", false)}
mode="${select("mode", ["offset", "name"], "offset")}"
scale="${select("scale", ["s", "m", "l"], "m")}"
+ status="${select("status", ["idle", "invalid", "valid"], "idle")}"
>
`;
diff --git a/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx b/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx
index d8fe3b51d29..789fa5bd6e7 100644
--- a/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx
+++ b/packages/calcite-components/src/components/input-time-zone/input-time-zone.tsx
@@ -20,7 +20,7 @@ import {
SupportedLocale,
} from "../../utils/locale";
import { TimeZoneItem, TimeZoneMode } from "./interfaces";
-import { Scale } from "../interfaces";
+import { Scale, Status } from "../interfaces";
import {
connectMessages,
disconnectMessages,
@@ -157,6 +157,9 @@ export class InputTimeZone
/** Specifies the size of the component. */
@Prop({ reflect: true }) scale: Scale = "m";
+ /** Specifies the status of the input field, which determines message and icons. */
+ @Prop({ reflect: true }) status: Status = "idle";
+
/**
* The component's value, where the value is the time zone offset or the difference, in minutes, between the selected time zone and UTC.
*
@@ -381,6 +384,7 @@ export class InputTimeZone
overlayPositioning={this.overlayPositioning}
scale={this.scale}
selectionMode="single-persist"
+ status={this.status}
// eslint-disable-next-line react/jsx-sort-props -- ref should be last so node attrs/props are in sync (see https://github.com/Esri/calcite-design-system/pull/6530)
ref={this.setComboboxRef}
>
diff --git a/packages/calcite-components/src/components/select/select.scss b/packages/calcite-components/src/components/select/select.scss
index 2eff717603c..6fc71700a79 100644
--- a/packages/calcite-components/src/components/select/select.scss
+++ b/packages/calcite-components/src/components/select/select.scss
@@ -105,6 +105,17 @@ select:disabled {
border-inline-width: theme("borderWidth.0") theme("borderWidth.DEFAULT");
}
+:host([status="invalid"]) {
+ select,
+ .icon-container {
+ @apply border-color-danger;
+ }
+ select:focus,
+ .icon-container:focus {
+ @apply focus-inset-danger;
+ }
+}
+
.select:focus ~ .icon-container {
@apply border-color-transparent;
}
diff --git a/packages/calcite-components/src/components/select/select.stories.ts b/packages/calcite-components/src/components/select/select.stories.ts
index 3960797fba1..38faf1f7878 100644
--- a/packages/calcite-components/src/components/select/select.stories.ts
+++ b/packages/calcite-components/src/components/select/select.stories.ts
@@ -6,7 +6,7 @@ import {
modesDarkDefault,
} from "../../../.storybook/utils";
import { html } from "../../../support/formatting";
-import { boolean, text } from "@storybook/addon-knobs";
+import { select, boolean, text } from "@storybook/addon-knobs";
import selectReadme from "../select/readme.md";
import optionReadme from "../option/readme.md";
import optionGroupReadme from "../option-group/readme.md";
@@ -27,6 +27,30 @@ const createSelectAttributes: (options?: { exceptions: string[] }) => Attributes
return this;
},
},
+ {
+ name: "status",
+ commit(): Attribute {
+ this.value = select("status", ["idle", "invalid", "valid"], "idle", group);
+ delete this.build;
+ return this;
+ },
+ },
+ {
+ name: "width",
+ commit(): Attribute {
+ this.value = select("width", ["auto", "full", "half"], "auto", group);
+ delete this.build;
+ return this;
+ },
+ },
+ {
+ name: "scale",
+ commit(): Attribute {
+ this.value = select("scale", ["s", "m", "l"], "m", group);
+ delete this.build;
+ return this;
+ },
+ },
],
exceptions
);
diff --git a/packages/calcite-components/src/components/select/select.tsx b/packages/calcite-components/src/components/select/select.tsx
index 0b72064f814..494f900798f 100644
--- a/packages/calcite-components/src/components/select/select.tsx
+++ b/packages/calcite-components/src/components/select/select.tsx
@@ -33,7 +33,7 @@ import {
setUpLoadableComponent,
} from "../../utils/loadable";
import { createObserver } from "../../utils/observers";
-import { Scale, Width } from "../interfaces";
+import { Scale, Status, Width } from "../interfaces";
import { CSS } from "./resources";
import { getIconScale } from "../../utils/component";
@@ -77,8 +77,7 @@ export class Select
*
* When not set, the component will be associated with its ancestor form element, if any.
*/
- @Prop({ reflect: true })
- form: string;
+ @Prop({ reflect: true }) form: string;
/**
* Accessible name for the component.
@@ -105,6 +104,9 @@ export class Select
*/
@Prop({ reflect: true }) scale: Scale = "m";
+ /** Specifies the status of the input field, which determines message and icons. */
+ @Prop({ reflect: true }) status: Status = "idle";
+
/** The component's `selectedOption` value. */
@Prop({ mutable: true }) value: string = null;
diff --git a/packages/calcite-components/src/components/text-area/text-area.scss b/packages/calcite-components/src/components/text-area/text-area.scss
index 281c15cfe57..d0882418ba9 100644
--- a/packages/calcite-components/src/components/text-area/text-area.scss
+++ b/packages/calcite-components/src/components/text-area/text-area.scss
@@ -140,6 +140,15 @@ textarea {
}
}
+:host([status="invalid"]) {
+ textarea {
+ @apply border-color-danger;
+ }
+ textarea:focus {
+ @apply focus-inset-danger;
+ }
+}
+
.readonly {
@apply bg-background font-medium;
}
diff --git a/packages/calcite-components/src/components/text-area/text-area.stories.ts b/packages/calcite-components/src/components/text-area/text-area.stories.ts
index 8a7b0bb52df..cb0cdda014a 100644
--- a/packages/calcite-components/src/components/text-area/text-area.stories.ts
+++ b/packages/calcite-components/src/components/text-area/text-area.stories.ts
@@ -14,6 +14,7 @@ export default {
export const simple = (): string => html`
Checkbox
+
+
+
+
unchecked invalid
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+