diff --git a/.github/ISSUE_TEMPLATE/accessibility.yml b/.github/ISSUE_TEMPLATE/accessibility.yml index b61e1b801e2..df412feb731 100644 --- a/.github/ISSUE_TEMPLATE/accessibility.yml +++ b/.github/ISSUE_TEMPLATE/accessibility.yml @@ -124,6 +124,7 @@ body: - ArcGIS Business/Community Analyst - ArcGIS Charts - ArcGIS Dashboards + - ArcGIS Data Pipelines - ArcGIS Developer Experience - ArcGIS Enterprise - ArcGIS Excalibur diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 6e54505bcba..8b586de9285 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -118,6 +118,7 @@ body: - ArcGIS Business/Community Analyst - ArcGIS Charts - ArcGIS Dashboards + - ArcGIS Data Pipelines - ArcGIS Developer Experience - ArcGIS Enterprise - ArcGIS Excalibur diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index dadcc31880a..6c3b0d080ea 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -87,6 +87,7 @@ body: - ArcGIS Business/Community Analyst - ArcGIS Charts - ArcGIS Dashboards + - ArcGIS Data Pipelines - ArcGIS Developer Experience - ArcGIS Enterprise - ArcGIS Excalibur diff --git a/.github/ISSUE_TEMPLATE/new-component.yml b/.github/ISSUE_TEMPLATE/new-component.yml index d2b985eb1a1..87deb0299b7 100644 --- a/.github/ISSUE_TEMPLATE/new-component.yml +++ b/.github/ISSUE_TEMPLATE/new-component.yml @@ -71,6 +71,7 @@ body: - ArcGIS Business/Community Analyst - ArcGIS Charts - ArcGIS Dashboards + - ArcGIS Data Pipelines - ArcGIS Developer Experience - ArcGIS Enterprise - ArcGIS Excalibur diff --git a/.github/workflows/pr-semantic.yml b/.github/workflows/pr-semantic.yml index 1ce2dc0e78a..6faa2e2fe09 100644 --- a/.github/workflows/pr-semantic.yml +++ b/.github/workflows/pr-semantic.yml @@ -9,3 +9,20 @@ jobs: - uses: amannn/action-semantic-pull-request@v5.5.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + # default https://github.com/commitizen/conventional-commit-types/blob/master/index.json + feat + fix + docs + style + refactor + perf + test + build + ci + chore + revert + + # custom + deprecate diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6293266be1e..2c938123c00 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -236,6 +236,7 @@ Contributions must adhere to **one** of the following conventions: - **`refactor`**: A change that neither fixes a bug or adds a feature ๐Ÿ” - **`revert`**: Reverts a previous commit โ†ช๏ธ - **`test`**: Improves test coverage in updating a test or adding a new, or missing test ๐Ÿงช +- **`deprecate`**: Documentation only changes for a deprecation ๐Ÿ‘Ž ### Scope of change diff --git a/packages/calcite-components/src/assets/styles/_host.scss b/packages/calcite-components/src/assets/styles/_host.scss index cde73f0c6ad..f6a231bb787 100644 --- a/packages/calcite-components/src/assets/styles/_host.scss +++ b/packages/calcite-components/src/assets/styles/_host.scss @@ -1,10 +1,11 @@ %component-host { /* Base ":host" styles for the component */ box-sizing: border-box; - * { - box-sizing: border-box; - } background-color: var(--calcite-color-foreground-1); color: var(--calcite-color-text-2); font-size: var(--calcite-font-size--1); + + * { + box-sizing: border-box; + } } diff --git a/packages/calcite-components/src/components.d.ts b/packages/calcite-components/src/components.d.ts index 68d4f03e98e..45c00f705de 100644 --- a/packages/calcite-components/src/components.d.ts +++ b/packages/calcite-components/src/components.d.ts @@ -402,10 +402,18 @@ export namespace Components { * @deprecated Use the `layout` property on the component's parent instead. */ "layout": Extract<"horizontal" | "vertical" | "grid", Layout>; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements": FlipPlacement[]; /** * When `true`, the `calcite-action-menu` is open. */ "menuOpen": boolean; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement": LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -648,6 +656,14 @@ export namespace Components { * When `true`, a busy indicator is displayed. */ "loading": boolean; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements": FlipPlacement[]; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement": LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -3312,6 +3328,10 @@ export namespace Components { * Used to specify the aria-setsize attribute to define the number of items in the current set of list for accessibility. */ "setSize": number; + /** + * When `true`, the component's content appears inactive. + */ + "unavailable": boolean; /** * The component's value. */ @@ -3872,10 +3892,18 @@ export namespace Components { * When `true`, a busy indicator is displayed. */ "loading": boolean; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements": FlipPlacement[]; /** * When `true`, the action menu items in the `header-menu-actions` slot are open. */ "menuOpen": boolean; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement": LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -7104,7 +7132,7 @@ declare global { new (): HTMLCalciteListItemElement; }; interface HTMLCalciteListItemGroupElementEventMap { - "calciteInternalListItemGroupDefaultSlotChange": DragEvent; + "calciteInternalListItemGroupDefaultSlotChange": void; } interface HTMLCalciteListItemGroupElement extends Components.CalciteListItemGroup, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteListItemGroupElement, ev: CalciteListItemGroupCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -7850,7 +7878,7 @@ declare global { new (): HTMLCalciteTileSelectGroupElement; }; interface HTMLCalciteTimePickerElementEventMap { - "calciteInternalTimePickerChange": string; + "calciteInternalTimePickerChange": void; } interface HTMLCalciteTimePickerElement extends Components.CalciteTimePicker, HTMLStencilElement { addEventListener(type: K, listener: (this: HTMLCalciteTimePickerElement, ev: CalciteTimePickerCustomEvent) => any, options?: boolean | AddEventListenerOptions): void; @@ -8330,10 +8358,18 @@ declare namespace LocalJSX { * @deprecated Use the `layout` property on the component's parent instead. */ "layout"?: Extract<"horizontal" | "vertical" | "grid", Layout>; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements"?: FlipPlacement[]; /** * When `true`, the `calcite-action-menu` is open. */ "menuOpen"?: boolean; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement"?: LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -8583,6 +8619,14 @@ declare namespace LocalJSX { * When `true`, a busy indicator is displayed. */ "loading"?: boolean; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements"?: FlipPlacement[]; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement"?: LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -11440,6 +11484,10 @@ declare namespace LocalJSX { * Used to specify the aria-setsize attribute to define the number of items in the current set of list for accessibility. */ "setSize"?: number; + /** + * When `true`, the component's content appears inactive. + */ + "unavailable"?: boolean; /** * The component's value. */ @@ -11461,7 +11509,7 @@ declare namespace LocalJSX { /** * Fires when changes occur in the default slot, notifying parent lists of the changes. */ - "onCalciteInternalListItemGroupDefaultSlotChange"?: (event: CalciteListItemGroupCustomEvent) => void; + "onCalciteInternalListItemGroupDefaultSlotChange"?: (event: CalciteListItemGroupCustomEvent) => void; } interface CalciteLoader { /** @@ -11996,10 +12044,18 @@ declare namespace LocalJSX { * When `true`, a busy indicator is displayed. */ "loading"?: boolean; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + "menuFlipPlacements"?: FlipPlacement[]; /** * When `true`, the action menu items in the `header-menu-actions` slot are open. */ "menuOpen"?: boolean; + /** + * Determines where the action menu will be positioned. + */ + "menuPlacement"?: LogicalPlacement; /** * Use this property to override individual strings used by the component. */ @@ -13813,7 +13869,7 @@ declare namespace LocalJSX { * Specifies the Unicode numeral system used by the component for localization. */ "numberingSystem"?: NumberingSystem; - "onCalciteInternalTimePickerChange"?: (event: CalciteTimePickerCustomEvent) => void; + "onCalciteInternalTimePickerChange"?: (event: CalciteTimePickerCustomEvent) => void; /** * Specifies the size of the component. */ diff --git a/packages/calcite-components/src/components/accordion-item/accordion-item.scss b/packages/calcite-components/src/components/accordion-item/accordion-item.scss index 69f139c4cd7..1575cd8bac9 100644 --- a/packages/calcite-components/src/components/accordion-item/accordion-item.scss +++ b/packages/calcite-components/src/components/accordion-item/accordion-item.scss @@ -3,20 +3,20 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-accordion-border-color: [Deprecate] Use --calcite-accordion-item-border-color. Specifies the component's border color. + * @prop --calcite-accordion-border-color: [Deprecate] Use `--calcite-accordion-item-border-color`. Specifies the component's border color. * @prop --calcite-accordion-item-background-color: Specifies the component's background color. * @prop --calcite-accordion-item-border-color: Specifies the component's border color. * @prop --calcite-accordion-item-content-space: Specifies the component's padding. - * @prop --calcite-accordion-item-end-icon-color: Specifies the component's end icon color. Fallback to --calcite-accordion-item-icon-color or current color. + * @prop --calcite-accordion-item-end-icon-color: Specifies the component's `iconEnd` color. Fallback to `--calcite-accordion-item-icon-color` or current color. * @prop --calcite-accordion-item-expand-icon-color: Specifies the component's expand icon color. - * @prop --calcite-accordion-item-header-background-color: Specifies the background color of the component's header. - * @prop --calcite-accordion-item-heading-text-color: Specifies the component's heading text color. + * @prop --calcite-accordion-item-header-background-color: Specifies the component's `heading` background color. + * @prop --calcite-accordion-item-heading-text-color: Specifies the component's `heading` text color. * @prop --calcite-accordion-item-icon-color: Specifies the component's default icon color. - * @prop --calcite-accordion-item-start-icon-color: Specifies the component's start icon color. Fallback to --calcite-accordion-item-icon-color or current color. + * @prop --calcite-accordion-item-start-icon-color: Specifies the component's `iconStart` color. Fallback to `--calcite-accordion-item-icon-color` or current color. * @prop --calcite-accordion-item-text-color: Specifies the component's text color. - * @prop --calcite-accordion-text-color-hover: [Deprecated] Use --calcite-accordion-item-text-color-hover. Specifies the component's main text color on hover. - * @prop --calcite-accordion-text-color-pressed: [Deprecated] Use --calcite-accordion-item-text-color-press. Specifies the component's main text color when pressed. - * @prop --calcite-accordion-text-color: [Deprecated] Use --calcite-accordion-item-text-color. Specifies the component's text color. + * @prop --calcite-accordion-text-color-hover: [Deprecated] Use `--calcite-accordion-item-text-color-hover`. Specifies the component's main text color on hover. + * @prop --calcite-accordion-text-color-pressed: [Deprecated] Use `--calcite-accordion-item-text-color-press`. Specifies the component's main text color when pressed. + * @prop --calcite-accordion-text-color: [Deprecated] Use `--calcite-accordion-item-text-color`. Specifies the component's text color. */ %icon-position { diff --git a/packages/calcite-components/src/components/action-bar/action-bar.scss b/packages/calcite-components/src/components/action-bar/action-bar.scss index 3ebe406552a..fcd10ee8cbd 100755 --- a/packages/calcite-components/src/components/action-bar/action-bar.scss +++ b/packages/calcite-components/src/components/action-bar/action-bar.scss @@ -3,7 +3,7 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-action-bar-expanded-max-width: Specifies the maximum width of the component when it's `layout` is `"vertical"`. + * @prop --calcite-action-bar-expanded-max-width: When `layout` is `"vertical"`, specifies the component's maximum width. * @prop --calcite-action-bar-items-space: Specifies the space between slotted components in the component. */ diff --git a/packages/calcite-components/src/components/action-group/action-group.e2e.ts b/packages/calcite-components/src/components/action-group/action-group.e2e.ts index 288761da844..c2ba175fd86 100755 --- a/packages/calcite-components/src/components/action-group/action-group.e2e.ts +++ b/packages/calcite-components/src/components/action-group/action-group.e2e.ts @@ -1,5 +1,16 @@ import { newE2EPage } from "@stencil/core/testing"; -import { accessible, defaults, focusable, hidden, renders, slots, t9n, themed } from "../../tests/commonTests"; +import { + accessible, + defaults, + focusable, + handlesActionMenuPlacements, + hidden, + reflects, + renders, + slots, + t9n, + themed, +} from "../../tests/commonTests"; import { html } from "../../../support/formatting"; import { CSS, SLOTS } from "./resources"; @@ -19,6 +30,23 @@ describe("calcite-action-group", () => { propertyName: "overlayPositioning", defaultValue: "absolute", }, + { + propertyName: "menuPlacement", + defaultValue: undefined, + }, + { + propertyName: "menuFlipPlacements", + defaultValue: undefined, + }, + ]); + }); + + describe("reflects", () => { + reflects("calcite-action-group", [ + { + propertyName: "menuPlacement", + value: "bottom", + }, ]); }); @@ -42,6 +70,15 @@ describe("calcite-action-group", () => { slots("calcite-action-group", SLOTS); }); + describe("handles action-menu placement and flipPlacements", () => { + handlesActionMenuPlacements(html` + + + + + `); + }); + it("should honor scale of expand icon", async () => { const page = await newE2EPage({ html: actionGroupHTML }); const menu = await page.find(`calcite-action-group >>> calcite-action-menu`); diff --git a/packages/calcite-components/src/components/action-group/action-group.scss b/packages/calcite-components/src/components/action-group/action-group.scss index 53e584deddf..b8fb7617d8a 100755 --- a/packages/calcite-components/src/components/action-group/action-group.scss +++ b/packages/calcite-components/src/components/action-group/action-group.scss @@ -4,10 +4,10 @@ * These properties can be overridden using the component's tag as selector. * * @prop --calcite-action-background-color: Specifies the component's background color. - * @prop --calcite-action-group-border-color: Specifies the component's border color when used in a calcite-action-bar or calcite-action-menu. - * @prop --calcite-action-group-columns: Specifies the component's grid-template-columns when the `layout` property is `"grid"`. - * @prop --calcite-action-group-gap: Specifies the component's gap when the `layout` property is `"grid" and padding`. - * @prop --calcite-action-group-padding: [Deprecated] Use --calcite-action-group-gap. Specifies the component's padding. + * @prop --calcite-action-group-border-color: Specifies the component's border color when used in a `calcite-action-bar` or `calcite-action-menu`. + * @prop --calcite-action-group-columns: When `layout` is `"grid"`, specifies the component's grid-template-columns. + * @prop --calcite-action-group-gap: When `layout` is `"grid"`, specifies the component's gap. + * @prop --calcite-action-group-padding: [Deprecated] Use `--calcite-action-group-gap`. Specifies the component's padding. * */ diff --git a/packages/calcite-components/src/components/action-group/action-group.tsx b/packages/calcite-components/src/components/action-group/action-group.tsx index d86bbc9bf19..3d99adab64e 100755 --- a/packages/calcite-components/src/components/action-group/action-group.tsx +++ b/packages/calcite-components/src/components/action-group/action-group.tsx @@ -21,7 +21,7 @@ import { } from "../../utils/t9n"; import { SLOTS as ACTION_MENU_SLOTS } from "../action-menu/resources"; import { Layout, Scale } from "../interfaces"; -import { OverlayPositioning } from "../../utils/floating-ui"; +import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui"; import { slotChangeHasAssignedElement } from "../../utils/dom"; import { Columns } from "./interfaces"; import { ActionGroupMessages } from "./assets/action-group/t9n"; @@ -95,6 +95,16 @@ export class ActionGroup */ @Prop({ reflect: true }) scale: Scale; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + @Prop() menuFlipPlacements: FlipPlacement[]; + + /** + * Determines where the action menu will be positioned. + */ + @Prop({ reflect: true }) menuPlacement: LogicalPlacement; + /** * Made into a prop for testing purposes only * @@ -178,19 +188,30 @@ export class ActionGroup // -------------------------------------------------------------------------- renderMenu(): VNode { - const { expanded, menuOpen, scale, layout, messages, overlayPositioning, hasMenuActions } = - this; + const { + expanded, + menuOpen, + scale, + layout, + messages, + overlayPositioning, + hasMenuActions, + menuFlipPlacements, + menuPlacement, + } = this; return ( diff --git a/packages/calcite-components/src/components/button/button.e2e.ts b/packages/calcite-components/src/components/button/button.e2e.ts index 7ac027ca14c..f3b128559f0 100644 --- a/packages/calcite-components/src/components/button/button.e2e.ts +++ b/packages/calcite-components/src/components/button/button.e2e.ts @@ -94,7 +94,7 @@ describe("calcite-button", () => { hidden("calcite-button"); }); - it("renders child element as disabled or aria-disabled", async () => { + it("renders child element as disabled", async () => { const page = await newE2EPage(); await page.setContent(`Continue`); @@ -105,7 +105,6 @@ describe("calcite-button", () => { expect(elementAsLink).toBeNull(); expect(await elementAsButton.getProperty("disabled")).toBe(true); - expect(await elementAsButton.getProperty("ariaDisabled")).toBe(null); const element = await page.find("calcite-button"); element.setProperty("href", "#anchor"); @@ -118,7 +117,6 @@ describe("calcite-button", () => { expect(elementAsLink).not.toBeNull(); expect(await elementAsLink.getProperty("disabled")).toBe(undefined); - expect(await elementAsLink.getProperty("ariaDisabled")).toBe("true"); }); it("renders as a button with default props", async () => { @@ -535,12 +533,12 @@ describe("calcite-button", () => { buttonEl = await page.find("calcite-button >>> button"); await buttonEl.focus(); await page.waitForChanges(); - buttonFocusStyle = await buttonEl.getComputedStyle(":focus"); + buttonFocusStyle = await buttonEl.getComputedStyle(); expect(buttonFocusStyle.getPropertyValue("background-color")).toEqual("rgba(0, 0, 0, 0.04)"); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(":hover"); + buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(0, 0, 0, 0.04)"); }); }); @@ -553,12 +551,12 @@ describe("calcite-button", () => { buttonEl = await page.find("calcite-button >>> button"); await buttonEl.focus(); await page.waitForChanges(); - buttonFocusStyle = await buttonEl.getComputedStyle(":focus"); + buttonFocusStyle = await buttonEl.getComputedStyle(); expect(buttonFocusStyle.getPropertyValue("background-color")).toEqual("rgba(255, 255, 255, 0.04)"); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(":hover"); + buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(255, 255, 255, 0.04)"); }); }); @@ -577,12 +575,12 @@ describe("calcite-button", () => { buttonEl = await page.find("calcite-button >>> button"); await buttonEl.focus(); await page.waitForChanges(); - buttonFocusStyle = await buttonEl.getComputedStyle(":focus"); + buttonFocusStyle = await buttonEl.getComputedStyle(); expect(buttonFocusStyle.getPropertyValue("background-color")).toEqual(overrideStyle); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(":hover"); + buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual(overrideStyle); }); }); diff --git a/packages/calcite-components/src/components/button/button.tsx b/packages/calcite-components/src/components/button/button.tsx index 0055426a824..d5fa047691e 100644 --- a/packages/calcite-components/src/components/button/button.tsx +++ b/packages/calcite-components/src/components/button/button.tsx @@ -33,9 +33,9 @@ import { updateMessages, } from "../../utils/t9n"; import { Appearance, FlipContext, Kind, Scale, Width } from "../interfaces"; -import { toAriaBoolean } from "../../utils/dom"; import { IconNameOrString } from "../icon/interfaces"; import { isBrowser } from "../../utils/browser"; +import { toAriaBoolean } from "../../utils/dom"; import { ButtonMessages } from "./assets/button/t9n"; import { ButtonAlignment } from "./interfaces"; import { CSS } from "./resources"; @@ -266,8 +266,8 @@ export class Button return ( -
+
(this.slotRefEl = el as HTMLSlotElement)} diff --git a/packages/calcite-components/src/components/card/card.e2e.ts b/packages/calcite-components/src/components/card/card.e2e.ts index 0fd65751855..6cb6167f492 100644 --- a/packages/calcite-components/src/components/card/card.e2e.ts +++ b/packages/calcite-components/src/components/card/card.e2e.ts @@ -76,26 +76,22 @@ describe("calcite-card", () => { describe("when a card is selectable (deprecated)", () => { it("should update the card's selected state when its checkbox is clicked", async () => { const page = await newE2EPage(); - await page.setContent(` -
+ await page.setContent(html`

ArcGIS Online: Gallery and Organization pages

A great example of a study description that might wrap to a line or two, but isn't overly verbose.
-
`); const card = await page.find("calcite-card"); const checkbox = await page.find(`calcite-card >>> .${CSS.checkboxWrapperDeprecated} calcite-checkbox`); const cardSelectSpy = await card.spyOnEvent("calciteCardSelect"); - const clickSpy = await card.spyOnEvent("calciteCardSelect"); await checkbox.click(); await page.waitForChanges(); expect(cardSelectSpy).toHaveReceivedEventTimes(1); - expect(clickSpy).toHaveReceivedEventTimes(1); expect(await checkbox.getProperty("checked")).toBe(true); expect(await card.getProperty("selected")).toBe(true); }); diff --git a/packages/calcite-components/src/components/card/card.scss b/packages/calcite-components/src/components/card/card.scss index ae58ceedd19..90370319493 100644 --- a/packages/calcite-components/src/components/card/card.scss +++ b/packages/calcite-components/src/components/card/card.scss @@ -3,21 +3,21 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-card-accent-color-selected: Specifies the accent color of the component when `selected`. - * @prop --calcite-card-background-color: Specifies the background color of the component. - * @prop --calcite-card-border-color: Specifies the border color of the component. - * @prop --calcite-card-corner-radius: Specifies the corner radius of the component. - * @prop --calcite-card-selection-background-color-active: [Deprecated] Use --calcite-card-selection-background-color-press. Specifies the background color of the component's selection element when active. - * @prop --calcite-card-selection-background-color-hover: Specifies the background color of the component's selection element when hovered. - * @prop --calcite-card-selection-background-color-press: Specifies the background color of the component's selection element when active. - * @prop --calcite-card-selection-background-color-selected: [Deprecated] Use --calcite-card-background-color. Specifies the icon color of the component's selection element when `selected`. - * @prop --calcite-card-selection-background-color: [Deprecated] Use --calcite-card-background-color. Specifies the background color of the component's selection element. - * @prop --calcite-card-selection-color-hover: Specifies the color of the component's selection element when hovered or focused. - * @prop --calcite-card-selection-color: Specifies the color of the component's selection element. - * @prop --calcite-card-selection-icon-color-hover: [Deprecated] Use --calcite-card-selection-color-hover. Specifies the icon color of the component's selection element when hovered. - * @prop --calcite-card-selection-icon-color-selected: [Deprecated] Use --calcite-card-accent-color-selected. Specifies the icon color of the component's selection element when `selected`. - * @prop --calcite-card-selection-icon-color: [Deprecated] Use --calcite-card-selection-color. Specifies the icon color of the component's selection element. - * @prop --calcite-card-shadow: Specifies the shadow of the component. + * @prop --calcite-card-accent-color-selected: Specifies the component's accent color when `selected`. + * @prop --calcite-card-background-color: Specifies the component's background color. + * @prop --calcite-card-border-color: Specifies the component's border color. + * @prop --calcite-card-corner-radius: Specifies the component's corner radius. + * @prop --calcite-card-selection-background-color-active: [Deprecated] Use `--calcite-card-selection-background-color-press`. Specifies the component's selection element background color when active. + * @prop --calcite-card-selection-background-color-hover: Specifies the component's selection element background color when hovered. + * @prop --calcite-card-selection-background-color-press: Specifies the component's selection element background color when active. + * @prop --calcite-card-selection-background-color-selected: [Deprecated] Use `--calcite-card-background-color`. Specifies the component's selection element icon color when `selected`. + * @prop --calcite-card-selection-background-color: [Deprecated] Use `--calcite-card-background-color`. Specifies the component's selection element background color. + * @prop --calcite-card-selection-color-hover: Specifies the component's selection element color when hovered or focused. + * @prop --calcite-card-selection-color: Specifies the component's selection element color. + * @prop --calcite-card-selection-icon-color-hover: [Deprecated] Use `--calcite-card-selection-color-hover`. Specifies the component's selection element icon color when hovered. + * @prop --calcite-card-selection-icon-color-selected: [Deprecated] Use `--calcite-card-accent-color-selected`. Specifies the component's selection element icon color when `selected`. + * @prop --calcite-card-selection-icon-color: [Deprecated] Use `--calcite-card-selection-color`. Specifies the component's selection element icon color. + * @prop --calcite-card-shadow: Specifies the component's shadow. * */ diff --git a/packages/calcite-components/src/components/card/card.tsx b/packages/calcite-components/src/components/card/card.tsx index 61a79410a7c..7753d93f2e1 100644 --- a/packages/calcite-components/src/components/card/card.tsx +++ b/packages/calcite-components/src/components/card/card.tsx @@ -374,7 +374,6 @@ export class Card
-
+
(this.slotRefEl = el as HTMLSlotElement)} diff --git a/packages/calcite-components/src/components/chip/chip.e2e.ts b/packages/calcite-components/src/components/chip/chip.e2e.ts index 4035db3dd65..2a3447c1ca6 100644 --- a/packages/calcite-components/src/components/chip/chip.e2e.ts +++ b/packages/calcite-components/src/components/chip/chip.e2e.ts @@ -158,12 +158,12 @@ describe("calcite-chip", () => { chipCloseButton = await page.find("calcite-chip >>> button"); await chipCloseButton.focus(); await page.waitForChanges(); - chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(":focus"); + chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonFocusStyle.getPropertyValue("background-color")).toEqual("rgba(0, 0, 0, 0.04)"); await chipCloseButton.hover(); await page.waitForChanges(); - chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(":hover"); + chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(0, 0, 0, 0.04)"); }); }); @@ -176,12 +176,12 @@ describe("calcite-chip", () => { chipCloseButton = await page.find("calcite-chip >>> button"); await chipCloseButton.focus(); await page.waitForChanges(); - chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(":focus"); + chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonFocusStyle.getPropertyValue("background-color")).toEqual("rgba(255, 255, 255, 0.04)"); await chipCloseButton.hover(); await page.waitForChanges(); - chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(":hover"); + chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(255, 255, 255, 0.04)"); }); }); @@ -200,12 +200,12 @@ describe("calcite-chip", () => { chipCloseButton = await page.find("calcite-chip >>> button"); await chipCloseButton.focus(); await page.waitForChanges(); - chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(":focus"); + chipCloseButtonFocusStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonFocusStyle.getPropertyValue("background-color")).toEqual(overrideStyle); await chipCloseButton.hover(); await page.waitForChanges(); - chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(":hover"); + chipCloseButtonHoverStyle = await chipCloseButton.getComputedStyle(); expect(chipCloseButtonHoverStyle.getPropertyValue("background-color")).toEqual(overrideStyle); }); diff --git a/packages/calcite-components/src/components/chip/chip.scss b/packages/calcite-components/src/components/chip/chip.scss index 1390e67fa25..e1ca4d68f02 100644 --- a/packages/calcite-components/src/components/chip/chip.scss +++ b/packages/calcite-components/src/components/chip/chip.scss @@ -3,14 +3,14 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-chip-background-color: Specifies the background color of the component. - * @prop --calcite-chip-border-color: Specifies the border color of the component. - * @prop --calcite-chip-corner-radius: Specifies the corner radius of the component. - * @prop --calcite-chip-text-color: Specifies the text color of the component. - * @prop --calcite-chip-icon-color: Specifies the icon color of the component. - * @prop --calcite-chip-close-icon-color: Specifies the icon color of the close element of the component. - * @prop --calcite-chip-select-icon-color: Specifies the icon color of the selection element of the component. - * @prop --calcite-chip-select-icon-color-pressed: Specifies the icon color of the selection element of the component when active. + * @prop --calcite-chip-background-color: Specifies the component's background color. + * @prop --calcite-chip-border-color: Specifies the component's border color. + * @prop --calcite-chip-corner-radius: Specifies the component's corner radius. + * @prop --calcite-chip-text-color: Specifies the component's text color. + * @prop --calcite-chip-icon-color: Specifies the component's icon color. + * @prop --calcite-chip-close-icon-color: Specifies the component's close element icon color. + * @prop --calcite-chip-select-icon-color: Specifies the component's selection element icon color. + * @prop --calcite-chip-select-icon-color-pressed: Specifies the component's selection element icon color when active. * */ diff --git a/packages/calcite-components/src/components/chip/chip.tsx b/packages/calcite-components/src/components/chip/chip.tsx index 55a1b47ff47..e4c6452813f 100644 --- a/packages/calcite-components/src/components/chip/chip.tsx +++ b/packages/calcite-components/src/components/chip/chip.tsx @@ -413,7 +413,6 @@ export class Chip ? toAriaBoolean(this.selected) : undefined } - aria-disabled={disableInteraction ? toAriaBoolean(disabled) : undefined} aria-label={this.label} class={{ [CSS.container]: true, diff --git a/packages/calcite-components/src/components/combobox/combobox.e2e.ts b/packages/calcite-components/src/components/combobox/combobox.e2e.ts index eec01ad1e14..df30e2bd620 100644 --- a/packages/calcite-components/src/components/combobox/combobox.e2e.ts +++ b/packages/calcite-components/src/components/combobox/combobox.e2e.ts @@ -747,6 +747,43 @@ describe("calcite-combobox", () => { expect(await combobox.getProperty("open")).toBe(true); }); + it("single-persist-selection mode correctly selects different items with the same value", async () => { + const page = await newE2EPage(); + await page.setContent(html` + + + + + `); + + const combobox = await page.find("calcite-combobox"); + + const firstOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await firstOpenEvent; + + const item1 = await combobox.find("calcite-combobox-item[heading=one]"); + const item2 = await combobox.find("calcite-combobox-item[heading=two]"); + + await item1.click(); + await page.waitForChanges(); + expect(await combobox.getProperty("value")).toBe("one"); + expect(await item1.getProperty("selected")).toBe(true); + expect(await item2.getProperty("selected")).toBe(false); + expect(await combobox.getProperty("open")).toBe(false); + + const secondOpenEvent = page.waitForEvent("calciteComboboxOpen"); + await combobox.click(); + await secondOpenEvent; + + await item2.click(); + await page.waitForChanges(); + expect(await combobox.getProperty("value")).toBe("one"); + expect(await item1.getProperty("selected")).toBe(false); + expect(await item2.getProperty("selected")).toBe(true); + expect(await combobox.getProperty("open")).toBe(false); + }); + it("multiple-selection mode allows toggling selection once the selected item is selected", async () => { const page = await newE2EPage(); await page.setContent(html` diff --git a/packages/calcite-components/src/components/combobox/combobox.tsx b/packages/calcite-components/src/components/combobox/combobox.tsx index 0dc62ec5656..5d585c20731 100644 --- a/packages/calcite-components/src/components/combobox/combobox.tsx +++ b/packages/calcite-components/src/components/combobox/combobox.tsx @@ -776,7 +776,8 @@ export class Combobox break; case "Enter": if (this.open && this.activeItemIndex > -1) { - this.toggleSelection(this.filteredItems[this.activeItemIndex]); + const item = this.filteredItems[this.activeItemIndex]; + this.toggleSelection(item, !item.selected); event.preventDefault(); } else if (this.activeChipIndex > -1) { this.removeActiveChip(); @@ -1132,10 +1133,13 @@ export class Combobox private emitComboboxChange = debounce(this.internalComboboxChangeEvent, 0); - toggleSelection(item: HTMLCalciteComboboxItemElement, value = !item.selected): void { + toggleSelection(item: HTMLCalciteComboboxItemElement, value: boolean): void { if ( !item || - (this.selectionMode === "single-persist" && item.selected && item.value === this.value) + (this.selectionMode === "single-persist" && + item.selected && + item.value === this.value && + !value) ) { return; } diff --git a/packages/calcite-components/src/components/date-picker-day/date-picker-day.tsx b/packages/calcite-components/src/components/date-picker-day/date-picker-day.tsx index b048610ac8a..9c3e465c6d6 100644 --- a/packages/calcite-components/src/components/date-picker-day/date-picker-day.tsx +++ b/packages/calcite-components/src/components/date-picker-day/date-picker-day.tsx @@ -181,7 +181,6 @@ export class DatePickerDay implements InteractiveComponent, LoadableComponent { return ( { shadowSelector: `.${CSS.dialog}`, targetProp: "insetBlockStart", }, + "--calcite-dialog-background-color": { + shadowSelector: `.${CSS.panel}`, + targetProp: "--calcite-panel-background-color", + }, }, ); diff --git a/packages/calcite-components/src/components/dialog/dialog.scss b/packages/calcite-components/src/components/dialog/dialog.scss index 0b5de775088..0700e9981a2 100644 --- a/packages/calcite-components/src/components/dialog/dialog.scss +++ b/packages/calcite-components/src/components/dialog/dialog.scss @@ -15,6 +15,7 @@ * @prop --calcite-dialog-border-color: Specifies the component's border color. * @prop --calcite-dialog-offset-x: Specifies the horizontal offset of the component. * @prop --calcite-dialog-offset-y: Specifies the vertical offset of the component. + * @prop --calcite-dialog-background-color: Specifies the background color of the component. */ :host { @@ -117,6 +118,7 @@ calcite-panel { --calcite-panel-content-space: var(--calcite-dialog-content-space, var(--calcite-internal-dialog-content-padding)); --calcite-panel-footer-padding: var(--calcite-dialog-footer-space); --calcite-panel-header-border-block-end: var(--calcite-border-width-sm) solid var(--calcite-dialog-border-color); + --calcite-panel-background-color: var(--calcite-dialog-background-color); } .dialog { diff --git a/packages/calcite-components/src/components/filter/filter.e2e.ts b/packages/calcite-components/src/components/filter/filter.e2e.ts index a0b9e9c5b7a..fc90fb5311c 100644 --- a/packages/calcite-components/src/components/filter/filter.e2e.ts +++ b/packages/calcite-components/src/components/filter/filter.e2e.ts @@ -222,6 +222,14 @@ describe("calcite-filter", () => { assertMatchingItems(await filter.getProperty("filteredItems"), ["jon"]); expect(filterChangeSpy).toHaveReceivedEventTimes(1); + + await page.$eval("calcite-filter", (filter: HTMLCalciteFilterElement): void => { + filter.items = []; + }); + await page.waitForTimeout(DEBOUNCE.filter); + await page.waitForChanges(); + assertMatchingItems(await filter.getProperty("filteredItems"), []); + expect(filterChangeSpy).toHaveReceivedEventTimes(1); }); it("searches recursively in items and works and matches on a partial string ignoring case", async () => { diff --git a/packages/calcite-components/src/components/filter/filter.tsx b/packages/calcite-components/src/components/filter/filter.tsx index ba26a158e8f..3900e735204 100644 --- a/packages/calcite-components/src/components/filter/filter.tsx +++ b/packages/calcite-components/src/components/filter/filter.tsx @@ -167,9 +167,7 @@ export class Filter async componentWillLoad(): Promise { setUpLoadableComponent(this); - if (this.items.length) { - this.updateFiltered(filter(this.items, this.value, this.filterProps)); - } + this.updateFiltered(filter(this.items ?? [], this.value, this.filterProps)); await setUpMessages(this); } @@ -230,8 +228,7 @@ export class Filter private filterDebounced = debounce( (value: string, emit = false, onFilter?: () => void): void => - this.items.length && - this.updateFiltered(filter(this.items, value, this.filterProps), emit, onFilter), + this.updateFiltered(filter(this.items ?? [], value, this.filterProps), emit, onFilter), DEBOUNCE.filter, ); diff --git a/packages/calcite-components/src/components/functional/Heading.spec.tsx b/packages/calcite-components/src/components/functional/Heading.spec.tsx index 888bec6d0de..4dc1befea3d 100644 --- a/packages/calcite-components/src/components/functional/Heading.spec.tsx +++ b/packages/calcite-components/src/components/functional/Heading.spec.tsx @@ -1,5 +1,4 @@ -import { h } from "@stencil/core"; -import { newSpecPage } from "@stencil/core/testing"; +import { h, VNode } from "@stencil/core"; import { constrainHeadingLevel, Heading } from "./Heading"; describe("constrainHeadingLevel", () => { @@ -13,26 +12,55 @@ describe("constrainHeadingLevel", () => { }); }); -describe("Heading", () => { - it("should render", async () => { - const page = await newSpecPage({ - components: [], - template: () => ( - - My Heading - +/** + * simple VNode assertion util to help get rid of newSpecPage usage + * + * @param vnode + * @param expected + * @param expected.tag + * @param expected.attrs + * @param expected.children + */ +function assertVNode( + vnode: VNode, + expected: { + tag: string; + attrs: Record; + children: (VNode | string)[]; + }, +) { + expect(vnode).toEqual( + expect.objectContaining({ + $tag$: expected.tag, + $attrs$: expect.objectContaining(expected.attrs), + $children$: expected.children.map((child) => + typeof child === "string" + ? expect.objectContaining({ $text$: child }) + : expect.objectContaining(child), ), - }); + }), + ); +} - expect(page.root).toEqualHtml(`

My Heading

`); +describe("Heading", () => { + it("should render", async () => { + assertVNode( + + My Heading + , + { + tag: "h1", + attrs: { class: "test" }, + children: ["My Heading"], + }, + ); }); it("should render a div", async () => { - const page = await newSpecPage({ - components: [], - template: () => My Heading, + assertVNode(My Heading, { + tag: "div", + attrs: { class: "test" }, + children: ["My Heading"], }); - - expect(page.root).toEqualHtml(`
My Heading
`); }); }); diff --git a/packages/calcite-components/src/components/handle/handle.tsx b/packages/calcite-components/src/components/handle/handle.tsx index 90e1cfeccc7..090cc48f730 100644 --- a/packages/calcite-components/src/components/handle/handle.tsx +++ b/packages/calcite-components/src/components/handle/handle.tsx @@ -25,7 +25,11 @@ import { T9nComponent, updateMessages, } from "../../utils/t9n"; -import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive"; +import { + InteractiveComponent, + InteractiveContainer, + updateHostInteraction, +} from "../../utils/interactive"; import { HandleMessages } from "./assets/handle/t9n"; import { HandleChange, HandleNudge } from "./interfaces"; import { CSS, ICONS, SUBSTITUTIONS } from "./resources"; @@ -294,24 +298,26 @@ export class Handle implements LoadableComponent, T9nComponent, InteractiveCompo render(): VNode { return ( - // Needs to be a span because of https://github.com/SortableJS/Sortable/issues/1486 - { - this.handleButton = el; - }} - // role of radio is being applied to allow space key to select in screen readers - role="radio" - tabIndex={this.disabled ? null : 0} - title={this.getTooltip()} - > - - + + { + this.handleButton = el; + }} + // role of radio is being applied to allow space key to select in screen readers + role="radio" + tabIndex={this.disabled ? null : 0} + title={this.getTooltip()} + > + + + ); } } diff --git a/packages/calcite-components/src/components/icon/icon.scss b/packages/calcite-components/src/components/icon/icon.scss index be0af200edd..82649d010cd 100644 --- a/packages/calcite-components/src/components/icon/icon.scss +++ b/packages/calcite-components/src/components/icon/icon.scss @@ -3,8 +3,8 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-ui-icon-color: [Deprecated] Use --calcite-icon-color. Specifies the component's color. Defaults to `currentColor`. - * @prop --calcite-icon-color: Specifies the component's color. Defaults to `currentColor`. + * @prop --calcite-ui-icon-color: [Deprecated] Use `--calcite-icon-color`. Specifies the component's color. Defaults to current color. + * @prop --calcite-icon-color: Specifies the component's color. Defaults to current color. */ :host { diff --git a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json index 34e09cd6deb..57d539d8575 100644 --- a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json +++ b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages.json @@ -255,7 +255,7 @@ "Asia/Famagusta": "Famagusta", "Asia/Gaza": "Gaza", "Asia/Hebron": "Hebron", - "Asia/Ho_Chi_Minh": "Ho Chi Minh", + "Asia/Ho_Chi_Minh": "Ho Chi Minh City", "Asia/Hong_Kong": "Hong Kong", "Asia/Hovd": "Hovd", "Asia/Irkutsk": "Irkutsk", diff --git a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json index 34e09cd6deb..57d539d8575 100644 --- a/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json +++ b/packages/calcite-components/src/components/input-time-zone/assets/input-time-zone/t9n/messages_en.json @@ -255,7 +255,7 @@ "Asia/Famagusta": "Famagusta", "Asia/Gaza": "Gaza", "Asia/Hebron": "Hebron", - "Asia/Ho_Chi_Minh": "Ho Chi Minh", + "Asia/Ho_Chi_Minh": "Ho Chi Minh City", "Asia/Hong_Kong": "Hong Kong", "Asia/Hovd": "Hovd", "Asia/Irkutsk": "Irkutsk", diff --git a/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts b/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts index 0bcfd22545e..41b46006fe6 100644 --- a/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts +++ b/packages/calcite-components/src/components/input-time-zone/input-time-zone.e2e.ts @@ -551,10 +551,11 @@ describe("calcite-input-time-zone", () => { await page.waitForTimeout(DEBOUNCE.filter); const selectedTimeZoneItem = await page.find("calcite-input-time-zone >>> calcite-combobox-item[selected]"); + const itemMetadata = await selectedTimeZoneItem.getProperty("metadata"); const expectedTimeZoneItem = testTimeZoneItems[3]; expect(await input.getProperty("value")).toBe(`${expectedTimeZoneItem.offset}`); - expect(await selectedTimeZoneItem.getProperty("value")).toMatch(expectedTimeZoneItem.name); + expect(itemMetadata.filterValue).toContain(expectedTimeZoneItem.name); }); }); }); 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 f0ff733dcb6..73f8e61b552 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 @@ -267,9 +267,6 @@ export class InputTimeZone } this.selectedTimeZoneItem = timeZoneItem; - requestAnimationFrame(() => { - this.overrideSelectedLabelForRegion(this.open); - }); } /** @@ -370,19 +367,16 @@ export class InputTimeZone * @private */ private overrideSelectedLabelForRegion(open: boolean): void { - if (this.mode !== "region" || !this.selectedTimeZoneItem || !this.comboboxEl?.selectedItems) { + if (this.mode !== "region" || !this.selectedTimeZoneItem) { return; } const { label, metadata } = this.selectedTimeZoneItem; - requestAnimationFrame(() => { - const itemLabel = - !metadata.country || open - ? label - : getSelectedRegionTimeZoneLabel(label, metadata.country, this.messages); - this.comboboxEl.selectedItems[0].textLabel = itemLabel; - }); + this.comboboxEl.selectedItems[0].textLabel = + !metadata.country || open + ? label + : getSelectedRegionTimeZoneLabel(label, metadata.country, this.messages); } private onComboboxBeforeClose = (event: CustomEvent): void => { @@ -409,7 +403,7 @@ export class InputTimeZone return; } - const selected = this.findTimeZoneItemByLabel(selectedItem.textLabel); + const selected = this.findTimeZoneItemByLabel(selectedItem.getAttribute("data-label")); const selectedValue = `${selected.value}`; if (this.value === selectedValue && selected.label === this.selectedTimeZoneItem.label) { @@ -519,12 +513,12 @@ export class InputTimeZone componentDidLoad(): void { setComponentLoaded(this); - this.overrideSelectedLabelForRegion(this.open); this.openChanged(); } componentDidRender(): void { updateHostInteraction(this); + this.overrideSelectedLabelForRegion(this.open); } render(): VNode { @@ -574,15 +568,16 @@ export class InputTimeZone return this.timeZoneItems.map((group) => { const selected = this.selectedTimeZoneItem === group; - const { label, value } = group; + const { label, metadata, value } = group; return ( ); }); @@ -593,20 +588,20 @@ export class InputTimeZone {items.map((item) => { const selected = this.selectedTimeZoneItem === item; - const { label, value } = item; + const { label, metadata, value } = item; return ( - {item.metadata.offset} + {metadata.offset} ); diff --git a/packages/calcite-components/src/components/input-time-zone/interfaces.d.ts b/packages/calcite-components/src/components/input-time-zone/interfaces.d.ts index 2b13fdc0c0a..ecdd6d18d3c 100644 --- a/packages/calcite-components/src/components/input-time-zone/interfaces.d.ts +++ b/packages/calcite-components/src/components/input-time-zone/interfaces.d.ts @@ -22,10 +22,10 @@ export type TimeZoneMode = "offset" | "name" | "region"; export interface TimeZoneItem { label: string; value: T; - filterValue: string | string[]; - metadata?: { - offset?: string; + metadata: { country?: string; + filterValue: string | string[]; + offset?: string; }; } diff --git a/packages/calcite-components/src/components/input-time-zone/utils.ts b/packages/calcite-components/src/components/input-time-zone/utils.ts index da92beb981d..5d3084a7427 100644 --- a/packages/calcite-components/src/components/input-time-zone/utils.ts +++ b/packages/calcite-components/src/components/input-time-zone/utils.ts @@ -68,7 +68,9 @@ export async function createTimeZoneItems( return { label, value, - filterValue: timeZone, + metadata: { + filterValue: timeZone, + }, }; }) .filter((group) => !!group) @@ -93,29 +95,49 @@ export async function createTimeZoneItems( return groups .map(({ label: region, tzs }) => { + tzs.sort((timeZoneA, timeZoneB) => { + const labeledTimeZoneA = getTimeZoneLabel(timeZoneA, messages); + const labeledTimeZoneB = getTimeZoneLabel(timeZoneB, messages); + const gmtTimeZoneString = "Etc/GMT"; + + if (timeZoneA.startsWith(gmtTimeZoneString) && timeZoneB.startsWith(gmtTimeZoneString)) { + // we use the IANA timezone for simpler and consistent sorting across locales + const offsetStringA = timeZoneA.substring(gmtTimeZoneString.length); + const offsetStringB = timeZoneB.substring(gmtTimeZoneString.length); + + const offsetA = offsetStringA === "" ? 0 : parseInt(offsetStringA); + const offsetB = offsetStringB === "" ? 0 : parseInt(offsetStringB); + + return offsetB - offsetA; + } + + return labeledTimeZoneA.localeCompare(labeledTimeZoneB); + }); + return { label: getMessageOrKeyFallback(messages, region), items: tzs.map((timeZone) => { const decimalOffset = timeZoneOffsetToDecimal( getTimeZoneShortOffset(timeZone, effectiveLocale, referenceDateInMs), ); - const filterValue = - toUserFriendlyName(timeZone) + - (region === globalLabel - ? // we add the global label as global group items do not have a unifying region/name - getTimeZoneLabel(globalLabel, messages) - : ""); const label = getTimeZoneLabel(timeZone, messages); + const filterValue = + region === globalLabel + ? // we rely on the label for search since GMT items have their signs inverted (see https://en.wikipedia.org/wiki/Tz_database#Area) + // in addition to the label we also add "Global" and "Etc" to allow searching for these items + `${getTimeZoneLabel(globalLabel, messages)} Etc` + : toUserFriendlyName(timeZone); + const countryCode = getCountry(timeZone); const country = getMessageOrKeyFallback(messages, countryCode); return { label, value: timeZone, - filterValue, metadata: { - offset: decimalOffset, country: country === label ? undefined : country, + filterValue, + offset: decimalOffset, }, }; }), @@ -172,7 +194,9 @@ export async function createTimeZoneItems( return { label, value, - filterValue: tzs.map((tz) => toUserFriendlyName(tz)), + metadata: { + filterValue: tzs.map((tz) => toUserFriendlyName(tz)), + }, }; }) .filter((group) => !!group) diff --git a/packages/calcite-components/src/components/list-item/list-item.e2e.ts b/packages/calcite-components/src/components/list-item/list-item.e2e.ts index f053f7b303f..c99dc7039dd 100755 --- a/packages/calcite-components/src/components/list-item/list-item.e2e.ts +++ b/packages/calcite-components/src/components/list-item/list-item.e2e.ts @@ -1,5 +1,5 @@ import { newE2EPage } from "@stencil/core/testing"; -import { defaults, disabled, focusable, hidden, renders, slots } from "../../tests/commonTests"; +import { defaults, disabled, focusable, hidden, reflects, renders, slots } from "../../tests/commonTests"; import { html } from "../../../support/formatting"; import { CSS, SLOTS } from "./resources"; @@ -56,6 +56,19 @@ describe("calcite-list-item", () => { propertyName: "filterHidden", defaultValue: false, }, + { + propertyName: "unavailable", + defaultValue: false, + }, + ]); + }); + + describe("reflects", () => { + reflects("calcite-list-item", [ + { + propertyName: "unavailable", + value: true, + }, ]); }); @@ -75,6 +88,20 @@ describe("calcite-list-item", () => { expect(await page.find(`calcite-list-item >>> .${CSS.containerHover}`)).not.toBeNull(); }); + it("adds unavailable class", async () => { + const page = await newE2EPage(); + await page.setContent(``); + await page.waitForChanges(); + + expect(await page.find(`calcite-list-item >>> .${CSS.contentContainerUnavailable}`)).toBeNull(); + + const item = await page.find("calcite-list-item"); + item.setProperty("unavailable", true); + await page.waitForChanges(); + + expect(await page.find(`calcite-list-item >>> .${CSS.contentContainerUnavailable}`)).not.toBeNull(); + }); + it("renders dragHandle when property is true", async () => { const page = await newE2EPage(); await page.setContent(``); diff --git a/packages/calcite-components/src/components/list-item/list-item.scss b/packages/calcite-components/src/components/list-item/list-item.scss index 2c40a7a9b2e..b2e7935a91e 100755 --- a/packages/calcite-components/src/components/list-item/list-item.scss +++ b/packages/calcite-components/src/components/list-item/list-item.scss @@ -75,6 +75,10 @@ p-0; } +.content-container--unavailable { + @apply opacity-disabled; +} + tr, td { @apply focus-base; diff --git a/packages/calcite-components/src/components/list-item/list-item.tsx b/packages/calcite-components/src/components/list-item/list-item.tsx index 6485bd03225..660b100eb8a 100644 --- a/packages/calcite-components/src/components/list-item/list-item.tsx +++ b/packages/calcite-components/src/components/list-item/list-item.tsx @@ -196,6 +196,11 @@ export class ListItem this.calciteInternalListItemSelect.emit(); } + /** + * When `true`, the component's content appears inactive. + */ + @Prop({ reflect: true }) unavailable = false; + /** * The component's value. */ @@ -613,7 +618,7 @@ export class ListItem } renderContentContainer(): VNode { - const { description, label, selectionMode, hasCustomContent } = this; + const { description, label, selectionMode, hasCustomContent, unavailable } = this; const hasCenterContent = hasCustomContent || !!label || !!description; const content = [ this.renderContentStart(), @@ -627,6 +632,7 @@ export class ListItem aria-label={label} class={{ [CSS.contentContainer]: true, + [CSS.contentContainerUnavailable]: unavailable, [CSS.contentContainerSelectable]: selectionMode !== "none", [CSS.contentContainerHasCenterContent]: hasCenterContent, }} diff --git a/packages/calcite-components/src/components/list-item/resources.ts b/packages/calcite-components/src/components/list-item/resources.ts index 64609366ca1..115ba5b315d 100644 --- a/packages/calcite-components/src/components/list-item/resources.ts +++ b/packages/calcite-components/src/components/list-item/resources.ts @@ -7,6 +7,7 @@ export const CSS = { containerBorderSelected: "container--border-selected", containerBorderUnselected: "container--border-unselected", contentContainer: "content-container", + contentContainerUnavailable: "content-container--unavailable", contentContainerSelectable: "content-container--selectable", contentContainerHasCenterContent: "content-container--has-center-content", nestedContainer: "nested-container", diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 95e2df2fbd5..6ff0683defd 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -441,6 +441,53 @@ describe("calcite-list", () => { expect(selectedItemValues[1]).toBe("three"); }); + it("updating items after filtering", async () => { + const matchingFont = "Courier"; + + const page = await newE2EPage(); + await page.setContent(html` + + + + + + `); + await page.waitForChanges(); + + const list = await page.find("calcite-list"); + let visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + + expect(visibleItems).toHaveLength(3); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list1"); + }); + + list.setProperty("filterText", matchingFont); + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + expect(visibleItems).toHaveLength(2); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list1"); + }); + + list.innerHTML = html` + + + + `; + await page.waitForChanges(); + await page.waitForTimeout(DEBOUNCE.filter); + + expect(await list.getProperty("filterText")).toBe(matchingFont); + visibleItems = await page.findAll("calcite-list-item:not([filter-hidden])"); + expect(visibleItems).toHaveLength(2); + visibleItems.forEach(async (item) => { + expect(await item.getProperty("description")).toBe("list2"); + }); + }); + it("filters initially", async () => { const page = await newE2EPage(); await page.setContent(html` diff --git a/packages/calcite-components/src/components/list/list.stories.ts b/packages/calcite-components/src/components/list/list.stories.ts index 00c1c921963..138720e2c21 100644 --- a/packages/calcite-components/src/components/list/list.stories.ts +++ b/packages/calcite-components/src/components/list/list.stories.ts @@ -90,6 +90,11 @@ export const simple = (args: ListStoryArgs): string => html` description="Vestibulum auctor dapibus neque. " > + `; @@ -286,6 +291,14 @@ export const startAndEndContentSlots = (): string => Halp!
+ + + + Some value or something and a thing. +
+ Halp! +
+
`; export const contentBottomSlots = (): string => @@ -299,6 +312,9 @@ export const contentBottomSlots = (): string => Some value or something and a thing. + + Some value or something and a thing. + `; export const contentBottomSlotsNested = (): string => diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index f329e36f215..52a7f43dea2 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -163,7 +163,7 @@ export class List @Prop() filterProps: string[]; @Watch("filterProps") - async handlefilterPropsChange(): Promise { + async handleFilterPropsChange(): Promise { this.performFilter(); } @@ -232,7 +232,7 @@ export class List @Watch("selectionMode") @Watch("selectionAppearance") handleListItemChange(): void { - this.updateListItems(); + this.updateListItems({ performFilter: true }); } //-------------------------------------------------------------------------- @@ -409,7 +409,7 @@ export class List connectLocalized(this); connectMessages(this); this.connectObserver(); - this.updateListItems(); + this.updateListItems({ performFilter: true }); this.setUpSorting(); this.setParentList(); } @@ -471,7 +471,9 @@ export class List listItems: HTMLCalciteListItemElement[] = []; - mutationObserver = createObserver("mutation", () => this.updateListItems()); + mutationObserver = createObserver("mutation", () => + this.updateListItems({ performFilter: true }), + ); visibleItems: HTMLCalciteListItemElement[] = []; @@ -807,10 +809,15 @@ export class List this.filteredData = filterEl.filteredItems as ItemData; } - this.updateListItems(emit); + this.updateListItems({ emitFilterChange: emit }); + } + + private async filterAndUpdateData(): Promise { + await this.filterEl?.filter(this.filterText); + this.updateFilteredData(); } - private async performFilter(): Promise { + private performFilter(): void { const { filterEl, filterText, filterProps } = this; if (!filterEl) { @@ -819,8 +826,7 @@ export class List filterEl.value = filterText; filterEl.filterProps = filterProps; - await filterEl.filter(filterText); - this.updateFilteredData(); + this.filterAndUpdateData(); } private setFilterEl = (el: HTMLCalciteFilterElement): void => { @@ -844,39 +850,47 @@ export class List })); }; - private updateListItems = debounce((emitFilterChange = false): void => { - const { selectionAppearance, selectionMode, dragEnabled, el } = this; + private updateListItems = debounce( + (options?: { emitFilterChange?: boolean; performFilter?: boolean }): void => { + const emitFilterChange = options?.emitFilterChange ?? false; + const performFilter = options?.performFilter ?? false; + + const { selectionAppearance, selectionMode, dragEnabled, el, filterEl, filterEnabled } = this; + + const items = Array.from(this.el.querySelectorAll(listItemSelector)); - const items = Array.from(this.el.querySelectorAll(listItemSelector)); + items.forEach((item) => { + item.selectionAppearance = selectionAppearance; + item.selectionMode = selectionMode; + if (item.closest("calcite-list") === el) { + item.dragHandle = dragEnabled; + } + }); - items.forEach((item) => { - item.selectionAppearance = selectionAppearance; - item.selectionMode = selectionMode; - if (item.closest("calcite-list") === el) { - item.dragHandle = dragEnabled; + if (this.parentListEl) { + this.setUpSorting(); + return; } - }); - if (this.parentListEl) { - this.setUpSorting(); - return; - } + this.listItems = items; + if (filterEnabled && performFilter) { + this.dataForFilter = this.getItemData(); - this.listItems = items; - if (this.filterEnabled) { - this.dataForFilter = this.getItemData(); - if (this.filterEl) { - this.filterEl.items = this.dataForFilter; + if (filterEl) { + filterEl.items = this.dataForFilter; + this.filterAndUpdateData(); + } } - } - this.visibleItems = this.listItems.filter((item) => !item.closed && !item.hidden); - this.updateFilteredItems(emitFilterChange); - this.borderItems(); - this.focusableItems = this.filteredItems.filter((item) => !item.disabled); - this.setActiveListItem(); - this.updateSelectedItems(); - this.setUpSorting(); - }, debounceTimeout); + this.visibleItems = this.listItems.filter((item) => !item.closed && !item.hidden); + this.updateFilteredItems(emitFilterChange); + this.borderItems(); + this.focusableItems = this.filteredItems.filter((item) => !item.disabled); + this.setActiveListItem(); + this.updateSelectedItems(); + this.setUpSorting(); + }, + debounceTimeout, + ); private focusRow = (focusEl: HTMLCalciteListItemElement): void => { const { focusableItems } = this; diff --git a/packages/calcite-components/src/components/menu-item/menu-item.tsx b/packages/calcite-components/src/components/menu-item/menu-item.tsx index c3ce2236414..8fc26acba11 100644 --- a/packages/calcite-components/src/components/menu-item/menu-item.tsx +++ b/packages/calcite-components/src/components/menu-item/menu-item.tsx @@ -14,7 +14,12 @@ import { Watch, } from "@stencil/core"; import { FlipContext } from "../interfaces"; -import { Direction, getElementDir, slotChangeGetAssignedElements } from "../../utils/dom"; +import { + Direction, + getElementDir, + slotChangeGetAssignedElements, + toAriaBoolean, +} from "../../utils/dom"; import { componentFocusable, LoadableComponent, @@ -470,8 +475,8 @@ export class CalciteMenuItem implements LoadableComponent, T9nComponent, Localiz
{ hidden("calcite-panel"); }); + describe("handles action-menu placement and flipPlacements", () => { + handlesActionMenuPlacements(html` + + + + `); + }); + describe("defaults", () => { defaults("calcite-panel", [ { @@ -122,6 +132,14 @@ describe("calcite-panel", () => { propertyName: "scale", defaultValue: "m", }, + { + propertyName: "menuPlacement", + defaultValue: defaultEndMenuPlacement, + }, + { + propertyName: "menuFlipPlacements", + defaultValue: undefined, + }, ]); }); @@ -143,6 +161,10 @@ describe("calcite-panel", () => { propertyName: "overlayPositioning", value: "fixed", }, + { + propertyName: "menuPlacement", + value: "bottom", + }, ]); }); @@ -654,6 +676,10 @@ describe("calcite-panel", () => { shadowSelector: `.${CSS.contentWrapper}`, targetProp: "padding", }, + "--calcite-panel-background-color": { + shadowSelector: `.${CSS.contentWrapper}`, + targetProp: "backgroundColor", + }, }); }); }); diff --git a/packages/calcite-components/src/components/panel/panel.scss b/packages/calcite-components/src/components/panel/panel.scss index db7b809c01d..2838acacdf2 100644 --- a/packages/calcite-components/src/components/panel/panel.scss +++ b/packages/calcite-components/src/components/panel/panel.scss @@ -6,6 +6,7 @@ * @prop --calcite-panel-content-space: Specifies the padding of the component's content. * @prop --calcite-panel-footer-padding: Specifies the padding of the component's footer. * @prop --calcite-panel-header-border-block-end: Specifies the component header's block end border. + * @prop --calcite-panel-background-color: Specifies the background color of the component. */ :host { @@ -161,12 +162,12 @@ flex-col flex-nowrap items-stretch - bg-background overflow-auto h-full focus-base relative; padding: var(--calcite-panel-content-space, 0); + background: var(--calcite-panel-background-color, var(--calcite-color-background)); } .content-wrapper:focus-visible { diff --git a/packages/calcite-components/src/components/panel/panel.stories.ts b/packages/calcite-components/src/components/panel/panel.stories.ts index d6a48a5ecc3..9f318bbcd71 100644 --- a/packages/calcite-components/src/components/panel/panel.stories.ts +++ b/packages/calcite-components/src/components/panel/panel.stories.ts @@ -1,6 +1,7 @@ import { boolean, modesDarkDefault } from "../../../.storybook/utils"; import { html } from "../../../support/formatting"; import { ATTRIBUTES } from "../../../.storybook/resources"; +import { defaultEndMenuPlacement, placements } from "../../utils/floating-ui"; import { Panel } from "./panel"; import { SLOTS } from "./resources"; const { collapseDirection, scale } = ATTRIBUTES; @@ -8,7 +9,15 @@ const { collapseDirection, scale } = ATTRIBUTES; interface PanelStoryArgs extends Pick< Panel, - "closed" | "disabled" | "closable" | "collapsed" | "collapsible" | "collapseDirection" | "loading" | "scale" + | "closed" + | "disabled" + | "closable" + | "collapsed" + | "collapsible" + | "collapseDirection" + | "loading" + | "scale" + | "menuPlacement" > { heightScale: string; } @@ -16,6 +25,7 @@ interface PanelStoryArgs export default { title: "Components/Panel", args: { + menuPlacement: defaultEndMenuPlacement, closed: false, disabled: false, closable: false, @@ -27,6 +37,10 @@ export default { loading: false, }, argTypes: { + menuPlacement: { + options: placements, + control: { type: "select" }, + }, collapseDirection: { options: collapseDirection.values, control: { type: "select" }, @@ -91,6 +105,7 @@ export const simple = (args: PanelStoryArgs): string => html` heightScale="${args.heightScale}" scale="${args.scale}" ${boolean("loading", args.loading)} + menu-placement="${args.menuPlacement}" heading="Heading" description="A great panel description" > diff --git a/packages/calcite-components/src/components/panel/panel.tsx b/packages/calcite-components/src/components/panel/panel.tsx index cea9303e368..ec8498880bf 100644 --- a/packages/calcite-components/src/components/panel/panel.tsx +++ b/packages/calcite-components/src/components/panel/panel.tsx @@ -39,7 +39,12 @@ import { T9nComponent, updateMessages, } from "../../utils/t9n"; -import { OverlayPositioning } from "../../utils/floating-ui"; +import { + defaultEndMenuPlacement, + FlipPlacement, + LogicalPlacement, + OverlayPositioning, +} from "../../utils/floating-ui"; import { CollapseDirection } from "../interfaces"; import { Scale } from "../interfaces"; import { PanelMessages } from "./assets/panel/t9n"; @@ -130,11 +135,21 @@ export class Panel /** A description for the component. */ @Prop() description: string; + /** + * Specifies the component's fallback menu `placement` when it's initial or specified `placement` has insufficient space available. + */ + @Prop() menuFlipPlacements: FlipPlacement[]; + /** * When `true`, the action menu items in the `header-menu-actions` slot are open. */ @Prop({ reflect: true }) menuOpen = false; + /** + * Determines where the action menu will be positioned. + */ + @Prop({ reflect: true }) menuPlacement: LogicalPlacement = defaultEndMenuPlacement; + /** * Use this property to override individual strings used by the component. */ @@ -549,17 +564,17 @@ export class Panel } renderMenu(): VNode { - const { hasMenuItems, messages, menuOpen } = this; + const { hasMenuItems, messages, menuOpen, menuFlipPlacements, menuPlacement } = this; return (
@@ -227,7 +221,7 @@ export class SegmentedControl // //-------------------------------------------------------------------------- - protected handleClick = (event: MouseEvent): void => { + private handleClick = (event: MouseEvent): void => { if (this.disabled) { return; } @@ -238,7 +232,7 @@ export class SegmentedControl }; @Listen("calciteInternalSegmentedControlItemChange") - protected handleSelected(event: Event): void { + handleSelected(event: Event): void { event.preventDefault(); const el = event.target as HTMLCalciteSegmentedControlItemElement; if (el.checked) { @@ -268,7 +262,7 @@ export class SegmentedControl } } - const items = this.getItems(); + const { items } = this; let selectedIndex = -1; items.forEach((item, index) => { @@ -321,7 +315,7 @@ export class SegmentedControl async setFocus(): Promise { await componentFocusable(this); - (this.selectedItem || this.getItems()[0])?.focus(); + (this.selectedItem || this.items[0])?.focus(); } //-------------------------------------------------------------------------- @@ -332,16 +326,22 @@ export class SegmentedControl @Element() el: HTMLCalciteSegmentedControlElement; + private items: HTMLCalciteSegmentedControlItemElement[] = []; + labelEl: HTMLCalciteLabelElement; formEl: HTMLFormElement; defaultValue: SegmentedControl["value"]; - private mutationObserver = createObserver("mutation", () => this.setUpItems()); + //-------------------------------------------------------------------------- + // + // Private Methods + // + //-------------------------------------------------------------------------- private handleItemPropChange(): void { - const items = this.getItems(); + const { items } = this; items.forEach((item) => { item.appearance = this.appearance; @@ -350,18 +350,32 @@ export class SegmentedControl }); } - //-------------------------------------------------------------------------- - // - // Private Methods - // - //-------------------------------------------------------------------------- + private handleSelectedItem(): void { + const { items } = this; - onLabelClick(): void { - this.setFocus(); + const lastChecked = items.filter((item) => item.checked).pop(); + + if (lastChecked) { + this.selectItem(lastChecked); + } else if (items[0]) { + items[0].tabIndex = 0; + } } - private getItems(): HTMLCalciteSegmentedControlItemElement[] { - return Array.from(this.el.querySelectorAll("calcite-segmented-control-item")); + private handleDefaultSlotChange = (event: Event): void => { + const items = slotChangeGetAssignedElements(event).filter( + (el): el is HTMLCalciteSegmentedControlItemElement => + el.matches("calcite-segmented-control-item"), + ); + + this.items = items; + + this.handleSelectedItem(); + this.handleItemPropChange(); + }; + + onLabelClick(): void { + this.setFocus(); } private selectItem(selected: HTMLCalciteSegmentedControlItemElement, emit = false): void { @@ -369,7 +383,7 @@ export class SegmentedControl return; } - const items = this.getItems(); + const { items } = this; let match: HTMLCalciteSegmentedControlItemElement = null; items.forEach((item) => { @@ -395,15 +409,4 @@ export class SegmentedControl match.focus(); } } - - private setUpItems(): void { - const items = this.getItems(); - const lastChecked = items.filter((item) => item.checked).pop(); - - if (lastChecked) { - this.selectItem(lastChecked); - } else if (items[0]) { - items[0].tabIndex = 0; - } - } } diff --git a/packages/calcite-components/src/components/slider/slider.tsx b/packages/calcite-components/src/components/slider/slider.tsx index 91e9fe2a746..65dfa5f0a3f 100644 --- a/packages/calcite-components/src/components/slider/slider.tsx +++ b/packages/calcite-components/src/components/slider/slider.tsx @@ -438,7 +438,6 @@ export class Slider return (
{(this.selectionCell || this.readCellContentsToAT) && ( - + {this.selectionCell && this.selectionText} {this.readCellContentsToAT && !this.selectionCell && this.contentsText} diff --git a/packages/calcite-components/src/components/table-header/table-header.tsx b/packages/calcite-components/src/components/table-header/table-header.tsx index a5ea7bec4d9..f39778658e2 100644 --- a/packages/calcite-components/src/components/table-header/table-header.tsx +++ b/packages/calcite-components/src/components/table-header/table-header.tsx @@ -261,11 +261,7 @@ export class TableHeader implements LocalizedComponent, LoadableComponent, T9nCo /> )} {(this.selectionCell || this.numberCell) && ( - + {this.screenReaderText} )} diff --git a/packages/calcite-components/src/components/table-row/table-row.tsx b/packages/calcite-components/src/components/table-row/table-row.tsx index 9c616ebce7b..9e2d841c6cf 100644 --- a/packages/calcite-components/src/components/table-row/table-row.tsx +++ b/packages/calcite-components/src/components/table-row/table-row.tsx @@ -13,7 +13,11 @@ import { } from "@stencil/core"; import { LocalizedComponent } from "../../utils/locale"; import { Alignment, Scale, SelectionMode } from "../interfaces"; -import { focusElementInGroup, FocusElementInGroupDestination } from "../../utils/dom"; +import { + focusElementInGroup, + FocusElementInGroupDestination, + toAriaBoolean, +} from "../../utils/dom"; import { RowType, TableInteractionMode, TableRowFocusEvent } from "../table/interfaces"; import { isActivationKey } from "../../utils/key"; import { @@ -409,9 +413,8 @@ export class TableRow implements InteractiveComponent, LocalizedComponent { (this.tableRowEl = el)} 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 77c8cf070f0..5d2ef27d57c 100644 --- a/packages/calcite-components/src/components/text-area/text-area.scss +++ b/packages/calcite-components/src/components/text-area/text-area.scss @@ -3,17 +3,17 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-text-area-background-color: Specifies the background color of the component. - * @prop --calcite-text-area-border-color: Specifies the border color of the text area. - * @prop --calcite-text-area-character-limit-text-color: Specifies the color of the character limit text displayed in footer of the component. + * @prop --calcite-text-area-background-color: Specifies the component's background color. + * @prop --calcite-text-area-border-color: Specifies the component's text area border color. + * @prop --calcite-text-area-character-limit-text-color: Specifies the color of the character limit text displayed in the footer of the component. * @prop --calcite-text-area-divider-color: Specifies the color of the divider between the text area and footer. - * @prop --calcite-text-area-font-size: Specifies the font size of the thext area and footer. - * @prop --calcite-text-area-max-height: Specifies the the maximum height of the text area in the component. - * @prop --calcite-text-area-min-height: Specifies the minimum height of the text area in the component. - * @prop --calcite-text-area-max-width: Specifies the the maximum width of the text area in the component. - * @prop --calcite-text-area-min-width: Specifies the minimum width of the text area in the component. - * @prop --calcite-text-area-text-color: Specifies the color of text in the component. - * @prop --calcite-text-area-footer-border-color: Specifies the border color of the footer. + * @prop --calcite-text-area-font-size: Specifies the font size of the text area and footer. + * @prop --calcite-text-area-max-height: Specifies the component's text area maximum height. + * @prop --calcite-text-area-min-height: Specifies the component's text area minimum height. + * @prop --calcite-text-area-max-width: Specifies the component's text area maximum width. + * @prop --calcite-text-area-min-width: Specifies the component's text area minimum width. + * @prop --calcite-text-area-text-color: Specifies the component's text color. + * @prop --calcite-text-area-footer-border-color: Specifies the footer's border color. */ :host { @@ -62,10 +62,7 @@ } &.text-area--invalid { - --calcite-internal-text-area-border-color: var( - --calcite-text-area-border-color, - var(--calcite-color-status-danger) - ); + --calcite-internal-text-area-border-color: var(--calcite-color-status-danger); &:focus { @apply focus-inset-danger; @@ -192,7 +189,7 @@ } :host([status="invalid"]) { - --calcite-internal-text-area-border-color: var(--calcite-text-area-border-color, var(--calcite-color-status-danger)); + --calcite-internal-text-area-border-color: var(--calcite-color-status-danger); .text-area:focus { @apply focus-inset-danger; diff --git a/packages/calcite-components/src/components/text-area/text-area.tsx b/packages/calcite-components/src/components/text-area/text-area.tsx index e9562989af4..fe68f538946 100644 --- a/packages/calcite-components/src/components/text-area/text-area.tsx +++ b/packages/calcite-components/src/components/text-area/text-area.tsx @@ -366,7 +366,7 @@ export class TextArea {this.isCharacterLimitExceeded() && ( - + {this.replacePlaceHoldersInMessages()} )} diff --git a/packages/calcite-components/src/components/tip-manager/tip-manager.scss b/packages/calcite-components/src/components/tip-manager/tip-manager.scss index aaeb6652eaa..d1ce9c0a559 100644 --- a/packages/calcite-components/src/components/tip-manager/tip-manager.scss +++ b/packages/calcite-components/src/components/tip-manager/tip-manager.scss @@ -14,11 +14,11 @@ box-border block; + --calcite-tip-manager-height: 19vh; + * { @apply box-border; } - - --calcite-tip-manager-height: 19vh; } :host([closed]) { diff --git a/packages/calcite-components/src/components/tooltip/TooltipManager.ts b/packages/calcite-components/src/components/tooltip/TooltipManager.ts index 302841ebb1e..157e46ecffb 100644 --- a/packages/calcite-components/src/components/tooltip/TooltipManager.ts +++ b/packages/calcite-components/src/components/tooltip/TooltipManager.ts @@ -22,6 +22,8 @@ export default class TooltipManager { private registeredElementCount = 0; + private clickedTooltip: HTMLCalciteTooltipElement = null; + // -------------------------------------------------------------------------- // // Public Methods @@ -100,11 +102,17 @@ export default class TooltipManager { return; } + if (tooltip === this.clickedTooltip) { + return; + } + if (tooltip) { this.openHoveredTooltip(tooltip); } else if (activeTooltip?.open) { this.closeHoveredTooltip(); } + + this.clickedTooltip = null; }; private pathHasOpenTooltip(tooltip: HTMLCalciteTooltipElement, composedPath: EventTarget[]): boolean { @@ -116,6 +124,7 @@ export default class TooltipManager { } private clickHandler = (event: Event): void => { + this.clickedTooltip = null; const composedPath = event.composedPath(); const tooltip = this.queryTooltip(composedPath); @@ -133,6 +142,7 @@ export default class TooltipManager { this.clearHoverTimeout(); if (tooltip.closeOnClick) { + this.clickedTooltip = tooltip; this.toggleTooltip(tooltip, false); return; } diff --git a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts index ab7770609c8..ab3b5a6a4a2 100644 --- a/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts +++ b/packages/calcite-components/src/components/tooltip/tooltip.e2e.ts @@ -661,6 +661,14 @@ describe("calcite-tooltip", () => { await page.waitForChanges(); expect(await tooltip.getProperty("open")).toBe(false); + + await referenceElement.hover(); + + await page.waitForTimeout(TOOLTIP_OPEN_DELAY_MS); + + await page.waitForChanges(); + + expect(await tooltip.getProperty("open")).toBe(false); }); it("should close tooltip when closeOnClick is true and referenceElement is clicked quickly", async () => { diff --git a/packages/calcite-components/src/components/tree/tree.tsx b/packages/calcite-components/src/components/tree/tree.tsx index deaf636d865..6f07c8e5d91 100644 --- a/packages/calcite-components/src/components/tree/tree.tsx +++ b/packages/calcite-components/src/components/tree/tree.tsx @@ -9,7 +9,7 @@ import { Prop, VNode, } from "@stencil/core"; -import { focusElement, nodeListToArray } from "../../utils/dom"; +import { focusElement, nodeListToArray, toAriaBoolean } from "../../utils/dom"; import { Scale, SelectionMode } from "../interfaces"; import { TreeItemSelectDetail } from "../tree-item/interfaces"; import { getTraversableItems, isTreeItem } from "./utils"; @@ -88,9 +88,9 @@ export class Tree { aria-multiselectable={ this.child ? undefined - : ( - this.selectionMode === "multiple" || this.selectionMode === "multichildren" - ).toString() + : toAriaBoolean( + this.selectionMode === "multiple" || this.selectionMode === "multichildren", + ) } onKeyDown={this.keyDownHandler} role={!this.child ? "tree" : undefined} diff --git a/packages/calcite-components/src/demos/list.html b/packages/calcite-components/src/demos/list.html index 92e783add62..330842570b0 100644 --- a/packages/calcite-components/src/demos/list.html +++ b/packages/calcite-components/src/demos/list.html @@ -90,7 +90,14 @@

List

--calcite-color-status-success > - + + + + { + * handlesActionMenuPlacements(html` + * + * + * + * `); + * }); + * + * @param componentTagOrHTML - The component tag or HTML markup to test against. + */ +export async function handlesActionMenuPlacements(componentTagOrHTML: TagOrHTML): Promise { + it("handles placement and flipPlacements", async () => { + const page = await simplePageSetup(componentTagOrHTML); + const tag = getTag(componentTagOrHTML); + + await page.waitForChanges(); + + const flipPlacements = ["top", "bottom"]; + + const component = await page.find(tag); + component.setProperty("menuFlipPlacements", flipPlacements); + component.setProperty("menuPlacement", "top"); + await page.waitForChanges(); + + const actionMenu = await page.find(`${tag} >>> calcite-action-menu`); + + expect(await actionMenu.getProperty("placement")).toBe("top"); + expect(await actionMenu.getProperty("flipPlacements")).toEqual(flipPlacements); + }); +} diff --git a/packages/calcite-components/src/tests/commonTests/index.ts b/packages/calcite-components/src/tests/commonTests/index.ts index f4e7e12baae..5842da83559 100644 --- a/packages/calcite-components/src/tests/commonTests/index.ts +++ b/packages/calcite-components/src/tests/commonTests/index.ts @@ -5,7 +5,7 @@ export { reflects } from "./reflects"; export { renders } from "./renders"; export { disabled } from "./disabled"; export { hidden } from "./hidden"; -export { floatingUIOwner, delegatesToFloatingUiOwningComponent } from "./floatingUI"; +export { floatingUIOwner, delegatesToFloatingUiOwningComponent, handlesActionMenuPlacements } from "./floatingUI"; export { focusable } from "./focusable"; export { formAssociated } from "./formAssociated"; export { slots } from "./slots"; diff --git a/packages/calcite-components/src/tests/commonTests/themed.ts b/packages/calcite-components/src/tests/commonTests/themed.ts index e2045c7fb90..5667ec25a84 100644 --- a/packages/calcite-components/src/tests/commonTests/themed.ts +++ b/packages/calcite-components/src/tests/commonTests/themed.ts @@ -174,7 +174,7 @@ export function themed(componentTestSetup: ComponentTestSetup, tokens: Component }); } -type ContextSelectByAttr = { attribute: string; value: string | RegExp }; +type ContextSelectByAttr = { attribute: string; value: string }; type CSSProp = Extract; @@ -303,16 +303,13 @@ async function assertThemedProps(page: E2EPage, options: TestTarget): Promise { const { attribute, value } = context as { attribute: string; - value: string | RegExp; + value: string; }; if (node.nodeType === 1) { const attr = (node as Element).getAttribute(attribute); if (typeof value === "string" && attr === value) { return node; } - if (value instanceof RegExp && attr && value.test(attr)) { - return node ?? undefined; - } if (attr === value) { return node; } diff --git a/packages/calcite-components/src/utils/floating-ui.ts b/packages/calcite-components/src/utils/floating-ui.ts index ad5faedea05..43503148793 100644 --- a/packages/calcite-components/src/utils/floating-ui.ts +++ b/packages/calcite-components/src/utils/floating-ui.ts @@ -271,6 +271,7 @@ export type MenuPlacement = Extract< >; export const defaultMenuPlacement: MenuPlacement = "bottom-start"; +export const defaultEndMenuPlacement: MenuPlacement = "bottom-end"; export interface FloatingUIComponent { /** @@ -319,6 +320,8 @@ export type FloatingLayout = Extract; export const FloatingCSS = { animation: "calcite-floating-ui-anim", animationActive: "calcite-floating-ui-anim--active", + arrow: "calcite-floating-ui-arrow", + arrowStroke: "calcite-floating-ui-arrow__stroke", }; function getMiddleware({ diff --git a/packages/calcite-ui-icons/docs/keywords.json b/packages/calcite-ui-icons/docs/keywords.json index 1feb93e4586..67416a8edfb 100644 --- a/packages/calcite-ui-icons/docs/keywords.json +++ b/packages/calcite-ui-icons/docs/keywords.json @@ -9006,6 +9006,11 @@ "category": "GIS", "release": "3.32.0" }, + "parcel-parameter": { + "alias": ["3.32.0", "gis", "feature", "data", "points", "lines", "assets"], + "category": "GIS", + "release": "3.32.0" + }, "n-variable": { "alias": ["3.32.0", "text", "variables", "math", "calculation", "letter"], "category": "Text", @@ -9015,5 +9020,10 @@ "alias": ["3.32.0", "gis", "forms", "variable", "text", "letter"], "category": "GIS", "release": "3.32.0" + }, + "dashboard-graph": { + "alias": ["3.32.0", "windows", "measure", "objects", "statistics", "measurements"], + "category": "Windows", + "release": "3.32.0" } } diff --git a/packages/calcite-ui-icons/icons/dashboard-graph-16.svg b/packages/calcite-ui-icons/icons/dashboard-graph-16.svg new file mode 100755 index 00000000000..03858d0fb9c --- /dev/null +++ b/packages/calcite-ui-icons/icons/dashboard-graph-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/calcite-ui-icons/icons/dashboard-graph-24.svg b/packages/calcite-ui-icons/icons/dashboard-graph-24.svg new file mode 100755 index 00000000000..833647c38d4 --- /dev/null +++ b/packages/calcite-ui-icons/icons/dashboard-graph-24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/calcite-ui-icons/icons/dashboard-graph-32.svg b/packages/calcite-ui-icons/icons/dashboard-graph-32.svg new file mode 100755 index 00000000000..8d29cfa050f --- /dev/null +++ b/packages/calcite-ui-icons/icons/dashboard-graph-32.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/calcite-ui-icons/icons/parcel-parameter-16.svg b/packages/calcite-ui-icons/icons/parcel-parameter-16.svg new file mode 100755 index 00000000000..1f3b361915a --- /dev/null +++ b/packages/calcite-ui-icons/icons/parcel-parameter-16.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/calcite-ui-icons/icons/parcel-parameter-24.svg b/packages/calcite-ui-icons/icons/parcel-parameter-24.svg new file mode 100755 index 00000000000..33c82116560 --- /dev/null +++ b/packages/calcite-ui-icons/icons/parcel-parameter-24.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/calcite-ui-icons/icons/parcel-parameter-32.svg b/packages/calcite-ui-icons/icons/parcel-parameter-32.svg new file mode 100755 index 00000000000..5d103fc5da3 --- /dev/null +++ b/packages/calcite-ui-icons/icons/parcel-parameter-32.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md b/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md index 55528ecd2f7..c54081ede54 100644 --- a/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md +++ b/packages/eslint-plugin-calcite-components/docs/enforce-ref-last-prop.md @@ -1,5 +1,7 @@ # enforce-ref-last-prop +**Deprecated** This rule is deprecated and will be removed in a future release. It is no longer needed if you are using Stencil 4.14.1 or greater. + This ensures the node passed into the `ref` callback is in sync with its JSX attributes/properties when invoked. Placing `ref` last helps work around a [Stencil bug](https://github.com/ionic-team/stencil/issues/4074) where the `ref` callback is invoked in the specified order and not after initializing the element with all its attributes/properties. This can cause attributes/properties to be outdated by the time the callback is invoked. diff --git a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts index 17a040121ef..3d6776b2dd2 100644 --- a/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts +++ b/packages/eslint-plugin-calcite-components/src/rules/enforce-ref-last-prop.ts @@ -3,6 +3,7 @@ import type { JSXAttribute, JSXOpeningElement, JSXSpreadAttribute } from "@babel const rule: Rule.RuleModule = { meta: { + deprecated: true, docs: { description: `This ensures the node passed into the ref callback is in sync with its JSX attributes/properties when invoked.`, recommended: true, diff --git a/release-please-config.json b/release-please-config.json index 9a78a7c49df..c74072f6a59 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -4,6 +4,20 @@ "tag-separator": "@", "draft-pull-request": true, "include-v-in-tag": false, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "revert", "section": "Reverts", "hidden": true }, + { "type": "deprecate", "section": "Deprecations" }, + { "type": "docs", "section": "Documentation", "hidden": true }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "chore", "section": "Miscellaneous Chores", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "build", "section": "Build System", "hidden": true }, + { "type": "ci", "section": "Continuous Integration", "hidden": true } + ], "packages": { "packages/calcite-components": { "component": "@esri/calcite-components",