diff --git a/packages/calcite-components/src/components.d.ts b/packages/calcite-components/src/components.d.ts index 88ea0986b86..dd887407571 100644 --- a/packages/calcite-components/src/components.d.ts +++ b/packages/calcite-components/src/components.d.ts @@ -511,6 +511,10 @@ export namespace Components { */ "setFocus": () => Promise; } + /** + * Alerts are meant to provide a way to communicate urgent or important information to users, frequently as a result of an action they took in your app. Alerts are positioned + * at the bottom of the page. Multiple opened alerts will be added to a queue, allowing users to dismiss them in the order they are provided. + */ interface CalciteAlert { /** * This internal property, managed by the AlertManager, is used to inform the component if it is the active open Alert. @@ -729,6 +733,10 @@ export namespace Components { */ "toggleDisplay": BlockSectionToggleDisplay; } + /** + * Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission + */ interface CalciteButton { /** * Specifies the alignment of the component's elements. @@ -3149,6 +3157,11 @@ export namespace Components { */ "scale": Scale; } + /** + * Any attributes placed on component will propagate to the rendered child + * Passing a 'href' will render an anchor link, instead of a span. Role will be set to link, or link, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any link attributes for form submission + */ interface CalciteLink { /** * When `true`, interaction is prevented and the component is displayed with lower opacity. @@ -3778,6 +3791,12 @@ export namespace Components { */ "username": string; } + /** + * Notices are intended to be used to present users with important-but-not-crucial contextual tips or copy. Because + * notices are displayed inline, a common use case is displaying them on page-load to present users with short hints or contextual copy. + * They are optionally closable - useful for keeping track of whether or not a user has closed the notice. You can also choose not + * to display a notice on page load and set the "active" attribute as needed to contextually provide inline messaging to users. + */ interface CalciteNotice { /** * When `true`, a close button is added to the component. @@ -5096,6 +5115,9 @@ export namespace Components { */ "syncId": string; } + /** + * Tab-titles are optionally individually closable. + */ interface CalciteTabTitle { /** * This activates a tab in order for it and its associated tab-title be selected. @@ -6281,6 +6303,10 @@ declare global { "calciteAlertBeforeOpen": void; "calciteAlertOpen": void; } + /** + * Alerts are meant to provide a way to communicate urgent or important information to users, frequently as a result of an action they took in your app. Alerts are positioned + * at the bottom of the page. Multiple opened alerts will be added to a queue, allowing users to dismiss them in the order they are provided. + */ interface HTMLCalciteAlertElement extends Components.CalciteAlert, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteAlertElement, ev: CalciteAlertCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -6339,6 +6365,10 @@ declare global { prototype: HTMLCalciteBlockSectionElement; new (): HTMLCalciteBlockSectionElement; }; + /** + * Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission + */ interface HTMLCalciteButtonElement extends Components.CalciteButton, HTMLStencilElement { } var HTMLCalciteButtonElement: { @@ -6959,6 +6989,11 @@ declare global { prototype: HTMLCalciteLabelElement; new (): HTMLCalciteLabelElement; }; + /** + * Any attributes placed on component will propagate to the rendered child + * Passing a 'href' will render an anchor link, instead of a span. Role will be set to link, or link, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any link attributes for form submission + */ interface HTMLCalciteLinkElement extends Components.CalciteLink, HTMLStencilElement { } var HTMLCalciteLinkElement: { @@ -7132,6 +7167,12 @@ declare global { "calciteNoticeClose": void; "calciteNoticeOpen": void; } + /** + * Notices are intended to be used to present users with important-but-not-crucial contextual tips or copy. Because + * notices are displayed inline, a common use case is displaying them on page-load to present users with short hints or contextual copy. + * They are optionally closable - useful for keeping track of whether or not a user has closed the notice. You can also choose not + * to display a notice on page load and set the "active" attribute as needed to contextually provide inline messaging to users. + */ interface HTMLCalciteNoticeElement extends Components.CalciteNotice, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteNoticeElement, ev: CalciteNoticeCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -7580,6 +7621,9 @@ declare global { "calciteInternalTabTitleRegister": TabID; "calciteInternalTabIconChanged": void; } + /** + * Tab-titles are optionally individually closable. + */ interface HTMLCalciteTabTitleElement extends Components.CalciteTabTitle, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteTabTitleElement, ev: CalciteTabTitleCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; addEventListener(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; @@ -8268,6 +8312,10 @@ declare namespace LocalJSX { */ "scale"?: Scale; } + /** + * Alerts are meant to provide a way to communicate urgent or important information to users, frequently as a result of an action they took in your app. Alerts are positioned + * at the bottom of the page. Multiple opened alerts will be added to a queue, allowing users to dismiss them in the order they are provided. + */ interface CalciteAlert { /** * This internal property, managed by the AlertManager, is used to inform the component if it is the active open Alert. @@ -8514,6 +8562,10 @@ declare namespace LocalJSX { */ "toggleDisplay"?: BlockSectionToggleDisplay; } + /** + * Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission + */ interface CalciteButton { /** * Specifies the alignment of the component's elements. @@ -11081,6 +11133,11 @@ declare namespace LocalJSX { */ "scale"?: Scale; } + /** + * Any attributes placed on component will propagate to the rendered child + * Passing a 'href' will render an anchor link, instead of a span. Role will be set to link, or link, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any link attributes for form submission + */ interface CalciteLink { /** * When `true`, interaction is prevented and the component is displayed with lower opacity. @@ -11752,6 +11809,12 @@ declare namespace LocalJSX { */ "username"?: string; } + /** + * Notices are intended to be used to present users with important-but-not-crucial contextual tips or copy. Because + * notices are displayed inline, a common use case is displaying them on page-load to present users with short hints or contextual copy. + * They are optionally closable - useful for keeping track of whether or not a user has closed the notice. You can also choose not + * to display a notice on page load and set the "active" attribute as needed to contextually provide inline messaging to users. + */ interface CalciteNotice { /** * When `true`, a close button is added to the component. @@ -13124,6 +13187,9 @@ declare namespace LocalJSX { */ "syncId"?: string; } + /** + * Tab-titles are optionally individually closable. + */ interface CalciteTabTitle { "bordered"?: boolean; /** @@ -14045,10 +14111,18 @@ declare module "@stencil/core" { "calcite-action-group": LocalJSX.CalciteActionGroup & JSXBase.HTMLAttributes; "calcite-action-menu": LocalJSX.CalciteActionMenu & JSXBase.HTMLAttributes; "calcite-action-pad": LocalJSX.CalciteActionPad & JSXBase.HTMLAttributes; + /** + * Alerts are meant to provide a way to communicate urgent or important information to users, frequently as a result of an action they took in your app. Alerts are positioned + * at the bottom of the page. Multiple opened alerts will be added to a queue, allowing users to dismiss them in the order they are provided. + */ "calcite-alert": LocalJSX.CalciteAlert & JSXBase.HTMLAttributes; "calcite-avatar": LocalJSX.CalciteAvatar & JSXBase.HTMLAttributes; "calcite-block": LocalJSX.CalciteBlock & JSXBase.HTMLAttributes; "calcite-block-section": LocalJSX.CalciteBlockSection & JSXBase.HTMLAttributes; + /** + * Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission + */ "calcite-button": LocalJSX.CalciteButton & JSXBase.HTMLAttributes; "calcite-card": LocalJSX.CalciteCard & JSXBase.HTMLAttributes; "calcite-card-group": LocalJSX.CalciteCardGroup & JSXBase.HTMLAttributes; @@ -14087,6 +14161,11 @@ declare module "@stencil/core" { "calcite-input-time-picker": LocalJSX.CalciteInputTimePicker & JSXBase.HTMLAttributes; "calcite-input-time-zone": LocalJSX.CalciteInputTimeZone & JSXBase.HTMLAttributes; "calcite-label": LocalJSX.CalciteLabel & JSXBase.HTMLAttributes; + /** + * Any attributes placed on component will propagate to the rendered child + * Passing a 'href' will render an anchor link, instead of a span. Role will be set to link, or link, depending on this. + * It is the consumers responsibility to add aria information, rel, target, for links, and any link attributes for form submission + */ "calcite-link": LocalJSX.CalciteLink & JSXBase.HTMLAttributes; /** * A general purpose list that enables users to construct list items that conform to Calcite styling. @@ -14105,6 +14184,12 @@ declare module "@stencil/core" { "calcite-navigation": LocalJSX.CalciteNavigation & JSXBase.HTMLAttributes; "calcite-navigation-logo": LocalJSX.CalciteNavigationLogo & JSXBase.HTMLAttributes; "calcite-navigation-user": LocalJSX.CalciteNavigationUser & JSXBase.HTMLAttributes; + /** + * Notices are intended to be used to present users with important-but-not-crucial contextual tips or copy. Because + * notices are displayed inline, a common use case is displaying them on page-load to present users with short hints or contextual copy. + * They are optionally closable - useful for keeping track of whether or not a user has closed the notice. You can also choose not + * to display a notice on page load and set the "active" attribute as needed to contextually provide inline messaging to users. + */ "calcite-notice": LocalJSX.CalciteNotice & JSXBase.HTMLAttributes; "calcite-option": LocalJSX.CalciteOption & JSXBase.HTMLAttributes; "calcite-option-group": LocalJSX.CalciteOptionGroup & JSXBase.HTMLAttributes; @@ -14136,6 +14221,9 @@ declare module "@stencil/core" { "calcite-switch": LocalJSX.CalciteSwitch & JSXBase.HTMLAttributes; "calcite-tab": LocalJSX.CalciteTab & JSXBase.HTMLAttributes; "calcite-tab-nav": LocalJSX.CalciteTabNav & JSXBase.HTMLAttributes; + /** + * Tab-titles are optionally individually closable. + */ "calcite-tab-title": LocalJSX.CalciteTabTitle & JSXBase.HTMLAttributes; "calcite-table": LocalJSX.CalciteTable & JSXBase.HTMLAttributes; "calcite-table-cell": LocalJSX.CalciteTableCell & JSXBase.HTMLAttributes; diff --git a/packages/calcite-components/src/demos/validation.html b/packages/calcite-components/src/demos/validation.html index e48cfa7b4bb..4169d145b2b 100644 --- a/packages/calcite-components/src/demos/validation.html +++ b/packages/calcite-components/src/demos/validation.html @@ -48,16 +48,6 @@ padding: var(--calcite-spacing-md); } - #when { - display: flex; - } - - #whenDate, - #whenTime { - align-self: center; - width: 50%; - } - /* Progress bar */ #progress { position: fixed; @@ -89,10 +79,11 @@ padding: 0 var(--calcite-spacing-sm); } - /* custom styling of invalid elements */ + /* custom styling of invalid elements [status="invalid"] { --calcite-color-foreground-1: #f4c4d3; } + */ @@ -127,16 +118,16 @@

Form Validation

Your name - + > It's a pleasure to meet you! Let us know your name incase we have any further questions. @@ -157,24 +148,11 @@

Form Validation

- +
  • When did you hear about Calcite components? -
    - - -
    +
  • @@ -234,8 +212,8 @@

    Form Validation

    @@ -257,7 +235,6 @@

    Form Validation

    max="10" step="1" required - validation-message="Please select one of these options." > @@ -290,8 +267,8 @@

    Form Validation

    @@ -301,11 +278,7 @@

    Form Validation

  • How many stars would you give Calcite components? - +
  • @@ -353,16 +326,20 @@

    Form Validation

    { id: "fullName", patternMismatch: { - message: "First and last name are required.", + message: "First and last name are required using only letters.", + icon: "user", }, tooShort: { message: "Please enter at least 3 characters.", + icon: "measure", }, tooLong: { message: "Please enter no more than 69 characters.", + icon: "measure-line", }, valueMissing: { message: "Please enter your name.", + icon: "user", }, }, { @@ -407,27 +384,26 @@

    Form Validation

    if (SHOW_VALIDATION_MESSAGES_ON_BLUR) { el.status = "invalid"; } - return; } } } window.onload = () => { - if (SHOW_VALIDATION_MESSAGES_ON_BLUR) { - /* blur event listeners to check validation constraints of elements with custom messages */ - ELEMENT_CONSTRAINTS.forEach((data) => { - document - .querySelector(`#${data.id}`) - ?.addEventListener("blur", ({ target }) => checkElementConstraints(target, data)); - }); - } else { - /* use `calciteInvalid` if you only want to set custom messages on form submission */ - document.addEventListener("calciteInvalid", ({ target }) => { - const constraints = ELEMENT_CONSTRAINTS.find((x) => x.id === target.id); + /* blur event listeners to check validation constraints of elements with custom messages */ + ELEMENT_CONSTRAINTS.forEach((data) => { + document + .querySelector(`#${data.id}`) + ?.addEventListener("blur", ({ target }) => checkElementConstraints(target, data)); + }); + + /* use `calciteInvalid` if you only want to set custom messages on form submission */ + document.addEventListener("calciteInvalid", ({ target }) => { + const constraints = ELEMENT_CONSTRAINTS.find((x) => x.id === target.id); + if (constraints) { checkElementConstraints(target, constraints); - }); - } + } + }); // Mode Switcher document.getElementById("mode")?.addEventListener("calciteSwitchChange", () => { @@ -477,7 +453,7 @@

    Form Validation

    }); // Date picker - const datePicker = document.getElementById("whenDate"); + const datePicker = document.getElementById("when"); datePicker.maxAsDate = new Date(); datePicker.min = "2021-01-01"; diff --git a/packages/calcite-components/src/tests/commonTests/formAssociated.ts b/packages/calcite-components/src/tests/commonTests/formAssociated.ts index d3df0499321..ade373e249d 100644 --- a/packages/calcite-components/src/tests/commonTests/formAssociated.ts +++ b/packages/calcite-components/src/tests/commonTests/formAssociated.ts @@ -497,6 +497,6 @@ export function formAssociated( expect(await element.getProperty("status")).toBe(testProps?.status ?? "idle"); expect(await element.getProperty("validationMessage")).toBe(testProps?.message ?? ""); - expect(element.getAttribute("validation-icon")).toBe(testProps?.icon ?? null); + expect(await element.getProperty("validationIcon")).toBe(testProps?.icon ?? false); } } diff --git a/packages/calcite-components/src/utils/dom.ts b/packages/calcite-components/src/utils/dom.ts index 54ea6e550ee..1eaae78380b 100644 --- a/packages/calcite-components/src/utils/dom.ts +++ b/packages/calcite-components/src/utils/dom.ts @@ -444,7 +444,7 @@ export function setRequestedIcon( ): IconNameOrString | undefined { if (typeof iconValue === "string" && iconValue !== "") { return iconValue; - } else if (iconValue === "") { + } else if (iconValue === "" || iconValue === true) { return iconObject[matchedValue]; } } diff --git a/packages/calcite-components/src/utils/form.tsx b/packages/calcite-components/src/utils/form.tsx index 081d5cbe0ad..d5ca2953060 100644 --- a/packages/calcite-components/src/utils/form.tsx +++ b/packages/calcite-components/src/utils/form.tsx @@ -290,7 +290,11 @@ function invalidHandler(event: Event) { formComponent.status = "idle"; } - if ("validationIcon" in formComponent && !formComponent.validationIcon) { + // don't clear if an icon was specified by the user + if ( + "validationIcon" in formComponent && + (formComponent.validationIcon === "" || formComponent.validationIcon === true) + ) { formComponent.validationIcon = false; }