diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml index b7cd1b7c89f..3b50413e3d8 100644 --- a/.github/ISSUE_TEMPLATE/enhancement.yml +++ b/.github/ISSUE_TEMPLATE/enhancement.yml @@ -66,6 +66,7 @@ body: - label: "@esri/calcite-components" - label: "@esri/calcite-components-react" - label: "@esri/calcite-design-tokens" + - label: "@esri/calcite-ui-icons" - label: "@esri/eslint-plugin-calcite-components" - type: dropdown id: esri-team diff --git a/.github/scripts/notifyAboutIconRequest.js b/.github/scripts/notifyAboutIconRequest.js new file mode 100644 index 00000000000..024bcd8d125 --- /dev/null +++ b/.github/scripts/notifyAboutIconRequest.js @@ -0,0 +1,33 @@ +// @ts-check + +// If the "calcite-ui-icons" label is added to an issue, generates a notification to the Icons team leads to review +// The secret is formatted like so: icon-team-member-1, icon-team-member-2, icon-team-member-3 +// Note the script automatically adds the "@" character in to notify the icon team lead(s) + +/** @param {import('github-script').AsyncFunctionArguments} AsyncFunctionArguments */ +module.exports = async ({ github, context }) => { + const { repo, owner } = context.repo; + + const payload = /** @type {import('@octokit/webhooks-types').IssuesLabeledEvent} */ (context.payload); + const { + issue: { number }, + } = payload; + + const { ICONS_TEAM } = process.env; + + // Add a "@" character to notify the user + const icon_leads = ICONS_TEAM?.split(",").map((v) => " @" + v.trim()); + + if (!icon_leads?.length) { + console.error("unable to determine icon leads"); + process.exit(1); + } + + // Add a comment to issues with the 'calcite-ui-icons' label to notify icon team lead(s) + await github.rest.issues.createComment({ + owner, + repo, + issue_number: number, + body: `cc ${icon_leads}`, + }); +}; diff --git a/.github/scripts/removeIssuesFromDesignProjects.js b/.github/scripts/removeIssueFromDesignProjects.js similarity index 93% rename from .github/scripts/removeIssuesFromDesignProjects.js rename to .github/scripts/removeIssueFromDesignProjects.js index 36d7ed65bc3..c9e301a2538 100644 --- a/.github/scripts/removeIssuesFromDesignProjects.js +++ b/.github/scripts/removeIssueFromDesignProjects.js @@ -52,7 +52,7 @@ try { const deleteQuery = `mutation { deleteProjectV2Item(input: {projectId: "${projectItem.project.id}", itemId: "${projectItem.id}"}) { clientMutationId } }`; runQuery(deleteQuery); createComment( - `The issue has been removed from the [${projectItem.project.title}](${projectItem.project.url}) project.`, + `This issue has been removed from the [${projectItem.project.title}](${projectItem.project.url}) project.`, ); console.log("Issue removed from project."); } else { diff --git a/.github/workflows/issue-notifications.yaml b/.github/workflows/issue-notifications.yml similarity index 80% rename from .github/workflows/issue-notifications.yaml rename to .github/workflows/issue-notifications.yml index 24e26fb5f4d..2ce21e74e7b 100644 --- a/.github/workflows/issue-notifications.yaml +++ b/.github/workflows/issue-notifications.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name : "Assign for verification" + - name: "Assign for verification" if: github.event.label.name == '3 - installed' uses: actions/github-script@v7 env: @@ -51,3 +51,12 @@ jobs: const action = require('${{ github.workspace }}/.github/scripts/notifyAboutNewComponent.js') await action({github, context, core}) + - name: "Icon request notification" + if: github.event.label.name == 'calcite-ui-icons' + uses: actions/github-script@v7 + env: + ICONS_TEAM: ${{secrets.ICON_LEADS}} + with: + script: | + const action = require('${{ github.workspace }}/.github/scripts/notifyAboutIconRequest.js') + await action({github, context, core}) diff --git a/.github/workflows/remove-issue-from-design-projects.yml b/.github/workflows/remove-issue-from-design-projects.yml index 789ebdb81db..6013684d858 100644 --- a/.github/workflows/remove-issue-from-design-projects.yml +++ b/.github/workflows/remove-issue-from-design-projects.yml @@ -1,4 +1,4 @@ -name: Archive Issue in Project +name: Remove Issue From Project on: issues: @@ -18,7 +18,7 @@ jobs: with: node-version-file: package.json - - name: Archive Issue + - name: Remove Issue env: GITHUB_TOKEN: ${{ secrets.ADMIN_TOKEN }} OWNER: ${{ github.repository_owner }} diff --git a/package-lock.json b/package-lock.json index 98bcfb7728f..36b7f2ffdee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -31895,13 +31895,13 @@ }, "packages/calcite-components": { "name": "@esri/calcite-components", - "version": "3.0.0-next.105", + "version": "3.0.0-next.106", "license": "SEE LICENSE.md", "dependencies": { "@arcgis/components-controllers": "^4.32.0-next.81", "@arcgis/components-utils": "^4.32.0-next.81", "@arcgis/lumina": "^4.32.0-next.81", - "@esri/calcite-ui-icons": "4.0.0-next.9", + "@esri/calcite-ui-icons": "4.0.0-next.10", "@floating-ui/dom": "^1.6.12", "@floating-ui/utils": "^0.2.8", "@types/color": "^4.2.0", @@ -31914,7 +31914,7 @@ "lodash-es": "^4.17.21", "sortablejs": "^1.15.6", "timezone-groups": "^0.10.4", - "type-fest": "4.32.0" + "type-fest": "^4.30.1" }, "devDependencies": { "@esri/calcite-design-tokens": "3.0.0-next.7", @@ -31923,10 +31923,10 @@ }, "packages/calcite-components-react": { "name": "@esri/calcite-components-react", - "version": "3.0.0-next.105", + "version": "3.0.0-next.106", "license": "SEE LICENSE.md", "dependencies": { - "@esri/calcite-components": "3.0.0-next.105", + "@esri/calcite-components": "3.0.0-next.106", "@lit/react": "1.0.7" }, "peerDependencies": { @@ -31941,7 +31941,7 @@ }, "packages/calcite-ui-icons": { "name": "@esri/calcite-ui-icons", - "version": "4.0.0-next.9", + "version": "4.0.0-next.10", "license": "SEE LICENSE.md", "bin": { "spriter": "bin/spriter.js" diff --git a/packages/calcite-components-react/CHANGELOG.md b/packages/calcite-components-react/CHANGELOG.md index 6b8faeac828..bafe2674369 100644 --- a/packages/calcite-components-react/CHANGELOG.md +++ b/packages/calcite-components-react/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-next.106](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@3.0.0-next.105...@esri/calcite-components-react@3.0.0-next.106) (2025-01-22) + +**Note:** Version bump only for package @esri/calcite-components-react + ## [3.0.0-next.105](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components-react@3.0.0-next.104...@esri/calcite-components-react@3.0.0-next.105) (2025-01-17) **Note:** Version bump only for package @esri/calcite-components-react diff --git a/packages/calcite-components-react/package.json b/packages/calcite-components-react/package.json index 3f9d5b4694a..82565e77fb4 100644 --- a/packages/calcite-components-react/package.json +++ b/packages/calcite-components-react/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components-react", - "version": "3.0.0-next.105", + "version": "3.0.0-next.106", "description": "A set of React components that wrap calcite components", "homepage": "https://developers.arcgis.com/calcite-design-system/", "repository": { @@ -26,7 +26,7 @@ "tsc": "tsc" }, "dependencies": { - "@esri/calcite-components": "3.0.0-next.105", + "@esri/calcite-components": "3.0.0-next.106", "@lit/react": "1.0.7" }, "peerDependencies": { diff --git a/packages/calcite-components/CHANGELOG.md b/packages/calcite-components/CHANGELOG.md index 1f147ed1d15..5eb826aad4a 100644 --- a/packages/calcite-components/CHANGELOG.md +++ b/packages/calcite-components/CHANGELOG.md @@ -3,6 +3,19 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [3.0.0-next.106](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@3.0.0-next.105...@esri/calcite-components@3.0.0-next.106) (2025-01-22) + +### Features + +- **button:** add component tokens ([#10358](https://github.com/Esri/calcite-design-system/issues/10358)) ([a7dc494](https://github.com/Esri/calcite-design-system/commit/a7dc4945bbf9cfabc54a81e8321ac128124ceb8b)), closes [#7180](https://github.com/Esri/calcite-design-system/issues/7180) [#11210](https://github.com/Esri/calcite-design-system/issues/11210) +- **panel:** control panel header action tokens ([#11300](https://github.com/Esri/calcite-design-system/issues/11300)) ([45f573f](https://github.com/Esri/calcite-design-system/commit/45f573f29d3ed5ffa6eef8420dde3905d16b1c83)) + +### Bug Fixes + +- **color-picker:** fix error caused by initial value with a different format ([#11351](https://github.com/Esri/calcite-design-system/issues/11351)) ([1d187da](https://github.com/Esri/calcite-design-system/commit/1d187daf65e5f0e4f062283e9df10a310e52d3d6)), closes [#10731](https://github.com/Esri/calcite-design-system/issues/10731) [#10310](https://github.com/Esri/calcite-design-system/issues/10310) +- **combobox, input-time-zone:** fix initial item selection delay ([#11326](https://github.com/Esri/calcite-design-system/issues/11326)) ([59ab243](https://github.com/Esri/calcite-design-system/commit/59ab24368455cc968e990ea2d35f44a6949e4726)), closes [#10731](https://github.com/Esri/calcite-design-system/issues/10731) +- **tree:** render lines correctly in RTL direction ([#11346](https://github.com/Esri/calcite-design-system/issues/11346)) ([7df1b15](https://github.com/Esri/calcite-design-system/commit/7df1b1555a85eda52e52f6b64457bfbdd25825c7)), closes [#11334](https://github.com/Esri/calcite-design-system/issues/11334) + ## [3.0.0-next.105](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-components@3.0.0-next.104...@esri/calcite-components@3.0.0-next.105) (2025-01-17) **Note:** Version bump only for package @esri/calcite-components diff --git a/packages/calcite-components/package.json b/packages/calcite-components/package.json index 4a019c488c1..80c25798cd6 100644 --- a/packages/calcite-components/package.json +++ b/packages/calcite-components/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-components", - "version": "3.0.0-next.105", + "version": "3.0.0-next.106", "description": "Web Components for Esri's Calcite Design System.", "homepage": "https://developers.arcgis.com/calcite-design-system/", "repository": { @@ -72,7 +72,7 @@ "@arcgis/components-controllers": "^4.32.0-next.81", "@arcgis/components-utils": "^4.32.0-next.81", "@arcgis/lumina": "^4.32.0-next.81", - "@esri/calcite-ui-icons": "4.0.0-next.9", + "@esri/calcite-ui-icons": "4.0.0-next.10", "@floating-ui/dom": "^1.6.12", "@floating-ui/utils": "^0.2.8", "@types/color": "^4.2.0", diff --git a/packages/calcite-components/src/components/action-pad/action-pad.scss b/packages/calcite-components/src/components/action-pad/action-pad.scss index d5e1a1d3d28..0b400e6db80 100755 --- a/packages/calcite-components/src/components/action-pad/action-pad.scss +++ b/packages/calcite-components/src/components/action-pad/action-pad.scss @@ -30,7 +30,7 @@ @apply shadow-2 inline-flex flex-col - overflow-y-auto; + overflow-hidden; gap: var(--calcite-action-pad-items-space, 0); border-radius: calc(var(--calcite-action-pad-corner-radius, 0.125rem) * 2) /* 4px */; diff --git a/packages/calcite-components/src/components/button/button.e2e.ts b/packages/calcite-components/src/components/button/button.e2e.ts index b732aa8aae5..5aa148117ff 100644 --- a/packages/calcite-components/src/components/button/button.e2e.ts +++ b/packages/calcite-components/src/components/button/button.e2e.ts @@ -1,7 +1,7 @@ // @ts-strict-ignore import { newE2EPage, E2EElement } from "@arcgis/lumina-compiler/puppeteerTesting"; import { describe, expect, it } from "vitest"; -import { accessible, defaults, disabled, hidden, HYDRATED_ATTR, labelable, t9n } from "../../tests/commonTests"; +import { accessible, defaults, disabled, hidden, HYDRATED_ATTR, labelable, t9n, themed } from "../../tests/commonTests"; import { GlobalTestProps } from "../../tests/utils"; import { html } from "../../../support/formatting"; import { CSS } from "./resources"; @@ -713,4 +713,59 @@ describe("calcite-button", () => { expect(elementHost).toEqualAttribute("width", "full"); expect(await elementAsButton.getComputedStyle()["width"]).toEqual(await elementHost.getComputedStyle()["width"]); }); + + describe("theme", () => { + describe("default", () => { + themed("calcite-button", { + "--calcite-button-background-color": { + shadowSelector: "button", + targetProp: "backgroundColor", + }, + "--calcite-button-corner-radius": { + shadowSelector: "button", + targetProp: "borderRadius", + }, + "--calcite-button-text-color": { + shadowSelector: "button", + targetProp: "color", + }, + "--calcite-button-border-color": { + shadowSelector: "button", + targetProp: "borderColor", + }, + }); + }); + describe("loading", () => { + themed(html``, { + "--calcite-button-background-color": { + shadowSelector: "button", + targetProp: "backgroundColor", + }, + "--calcite-button-corner-radius": { + shadowSelector: "button", + targetProp: "borderRadius", + }, + "--calcite-button-text-color": { + shadowSelector: "button", + targetProp: "color", + }, + "--calcite-button-loader-color": { + shadowSelector: `.${CSS.loadingIn}`, + targetProp: "color", + }, + "--calcite-button-border-color": { + shadowSelector: "button", + targetProp: "borderColor", + }, + }); + }); + describe("outline", () => { + themed(html``, { + "--calcite-button-border-color": { + shadowSelector: "button", + targetProp: "borderColor", + }, + }); + }); + }); }); diff --git a/packages/calcite-components/src/components/button/button.scss b/packages/calcite-components/src/components/button/button.scss index 5a01a0740b2..0597fd4d834 100644 --- a/packages/calcite-components/src/components/button/button.scss +++ b/packages/calcite-components/src/components/button/button.scss @@ -1,64 +1,89 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-button-background-color: Specifies the component's background color when appearance="solid" or appearance="outline-fill". + * @prop --calcite-button-border-color: Specifies the component's border color when it has appearance="outline" or appearance="outline-fill". + * @prop --calcite-button-corner-radius: Specifies the component's corner radius. + * @prop --calcite-button-loader-color: Specifies the component's loader color. + * @prop --calcite-button-shadow-color: Specifies the component's box-shadow color. + * @prop --calcite-button-text-color: Specifies the component's text color. + */ + :host { @apply inline-block w-auto align-middle; -} -// fab variants -:host([round]) { - border-radius: 50px; - & a, - & button { - border-radius: 50px; - } -} - -// focus styles -:host button, -:host a { - @apply focus-base; - &:focus { - @apply focus-outset; - } -} -// button base -:host button, -:host a { - --calcite-button-content-margin-internal: theme("margin.2"); - --calcite-button-padding-x-internal: 7px; - --calcite-button-padding-y-internal: 3px; - padding-block: var(--calcite-button-padding-y-internal); - padding-inline: var(--calcite-button-padding-x-internal); - @apply font-inherit - relative + border-radius: var(--calcite-button-corner-radius, var(--calcite-internal-button-corner-radius, 0)); + + a, + button { + --calcite-internal-button-content-margin: theme("margin.2"); + --calcite-internal-button-padding-x: 7px; + --calcite-internal-button-padding-y-internal: 3px; + + @apply appearance-none + border-none box-border + cursor-pointer flex + focus-base + font-inherit + font-normal h-full - w-full - cursor-pointer - select-none - appearance-none items-center justify-center - rounded-none - border-none + no-underline + relative + select-none text-center - font-normal - no-underline; - // include transition from focus - transition: - color var(--calcite-animation-timing) ease-in-out, - background-color var(--calcite-animation-timing) ease-in-out, - box-shadow var(--calcite-animation-timing) ease-in-out, - outline-color var(--calcite-internal-animation-timing-fast) ease-in-out; - &:hover { - @apply no-underline; - } - & span { - @apply truncate; + w-full; + + background-color: var( + --calcite-button-background-color, + var(--calcite-internal-button-background-color, var(--calcite-color-transparent)) + ); + border-color: var( + --calcite-button-border-color, + var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + ); + border-radius: var(--calcite-button-corner-radius, var(--calcite-internal-button-corner-radius, 0)); + color: var(--calcite-button-text-color, var(--calcite-internal-button-text-color, currentColor)); + padding-block: var(--calcite-internal-button-padding-y-internal); + padding-inline: var(--calcite-internal-button-padding-x); + + // include transition from focus + transition: + color var(--calcite-animation-timing) ease-in-out, + background-color var(--calcite-animation-timing) ease-in-out, + box-shadow var(--calcite-animation-timing) ease-in-out, + outline-color var(--calcite-internal-animation-timing-fast) ease-in-out; + + &:hover { + @apply no-underline; + } + + &:focus { + @apply focus-outset; + } + + span { + @apply truncate; + } + + calcite-loader { + color: var(--calcite-button-loader-color, var(--calcite-internal-button-loader-color, currentColor)); + } } } +// fab variants +:host([round]) { + --calcite-internal-button-corner-radius: 50px; +} + .content { - margin-inline: var(--calcite-button-content-margin-internal); + margin-inline: var(--calcite-internal-button-content-margin); } .icon-start-empty { @@ -75,13 +100,13 @@ :host([scale="m"]) { button, a { - --calcite-button-content-margin-internal: theme("margin.3"); + --calcite-internal-button-content-margin: theme("margin.3"); } } :host([scale="l"]) { button, a { - --calcite-button-content-margin-internal: theme("margin.4"); + --calcite-internal-button-content-margin: theme("margin.4"); } } @@ -147,7 +172,7 @@ a:not(.content--slotted), button:not(.content--slotted) { .icon--start + .icon--end { - margin-inline-start: var(--calcite-button-content-margin-internal); + margin-inline-start: var(--calcite-internal-button-content-margin); } } } @@ -199,7 +224,7 @@ button.content--slotted, a.content--slotted { .calcite-button--loader calcite-loader { - margin-inline-end: var(--calcite-button-content-margin-internal); + margin-inline-end: var(--calcite-internal-button-content-margin); } } // hide icons when loading with no text @@ -216,271 +241,297 @@ :host([appearance]) { button, a { - @apply border-color-transparent - border - border-solid; + --calcite-internal-button-border-color: var(--calcite-color-transparent); + + @apply border-solid; + + border-width: var(--calcite-button-border-size, 1px); } } :host([kind="brand"]) { button, a { - @apply text-color-inverse bg-brand; + --calcite-internal-button-text-color: var(--calcite-color-text-inverse); + --calcite-internal-button-background-color: var(--calcite-color-brand); + &:hover, &:focus { - @apply bg-brand-hover; + --calcite-internal-button-background-color: var(--calcite-color-brand-hover); } &:active { - @apply bg-brand-press; - } - calcite-loader { - @apply text-color-inverse; + --calcite-internal-button-background-color: var(--calcite-color-brand-press); } } } :host([kind="danger"]) { button, a { - @apply text-color-inverse bg-danger; + --calcite-internal-button-text-color: var(--calcite-color-text-inverse); + --calcite-internal-button-background-color: var(--calcite-color-status-danger); + &:hover, &:focus { - @apply bg-danger-hover; + --calcite-internal-button-background-color: var(--calcite-color-status-danger-hover); } &:active { - @apply bg-danger-press; - } - calcite-loader { - @apply text-color-inverse; + --calcite-internal-button-background-color: var(--calcite-color-status-danger-press); } } } :host([kind="neutral"]) { button, a { - @apply text-color-1 bg-foreground-3; + --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-background-color: var(--calcite-color-foreground-3); &:hover, &:focus { - @apply bg-foreground-2; + --calcite-internal-button-background-color: var(--calcite-color-foreground-2); } &:active { - @apply bg-foreground-1; - } - calcite-loader { - @apply text-color-1; + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); } } } :host([kind="inverse"]) { button, a { - @apply text-color-inverse; - background-color: var(--calcite-color-inverse); + --calcite-internal-button-text-color: var(--calcite-color-text-inverse); + --calcite-internal-button-background-color: var(--calcite-color-inverse); &:hover, &:focus { - background-color: var(--calcite-color-inverse-hover); + --calcite-internal-button-background-color: var(--calcite-color-inverse-hover); } &:active { - background-color: var(--calcite-color-inverse-press); - } - calcite-loader { - @apply text-color-inverse; + --calcite-internal-button-background-color: var(--calcite-color-inverse-press); } } } // outline +:host([appearance="outline-fill"]), +:host([appearance="outline"]) { + a, + button { + box-shadow: inset 0 0 0 1px + var(--calcite-button-shadow-color, var(--calcite-internal-button-shadow-color, transparent)); + + &:hover { + box-shadow: inset 0 0 0 1px + var( + --calcite-button-shadow-color, + var(--calcite-internal-button-shadow-color, var(--calcite-color-foreground-3)) + ); + } + &:focus, + &:active { + box-shadow: inset 0 0 0 2px + var( + --calcite-button-shadow-color, + var(--calcite-internal-button-shadow-color, var(--calcite-color-foreground-3)) + ); + } + } +} + :host([appearance="outline-fill"]) { button, a { - @apply bg-foreground-1 border border-solid; - box-shadow: inset 0 0 0 1px transparent; + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); + @apply border-solid; + border-width: var(--calcite-button-border-size, 1px); } } :host([appearance="outline-fill"][kind="brand"]) { button, a { - @apply border-color-brand bg-foreground-1; - color: theme("colors.brand"); + --calcite-internal-button-border-color: var(--calcite-color-brand); + --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); + &:hover { - @apply border-color-brand-hover; - color: theme("colors.brand-hover"); - box-shadow: inset 0 0 0 1px var(--calcite-color-brand-hover); + --calcite-internal-button-border-color: var(--calcite-color-brand-hover); + --calcite-internal-button-text-color: theme("colors.brand-hover"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand-hover); } &:focus { - @apply border-color-brand; - color: theme("colors.brand"); - box-shadow: inset 0 0 0 2px var(--calcite-color-brand); + --calcite-internal-button-border-color: var(--calcite-color-brand); + --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand); } &:active { - @apply border-color-brand-press; - color: theme("colors.brand-press"); - box-shadow: inset 0 0 0 2px var(--calcite-color-brand-press); + --calcite-internal-button-border-color: var(--calcite-color-brand-press); + --calcite-internal-button-text-color: theme("colors.brand-press"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand-press); } calcite-loader { - color: theme("colors.brand"); + --calcite-internal-button-loader-color: theme("colors.brand"); } } } :host([appearance="outline-fill"][kind="danger"]) { button, a { - @apply border-color-danger bg-foreground-1; - color: theme("colors.danger"); + --calcite-internal-button-border-color: var(--calcite-color-status-danger); + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); + --calcite-internal-button-text-color: theme("colors.danger"); + &:hover { - @apply border-color-danger-hover; - color: theme("colors.danger-hover"); - box-shadow: inset 0 0 0 1px var(--calcite-color-status-danger-hover); + --calcite-internal-button-border-color: var(--calcite-color-status-danger-hover); + --calcite-internal-button-text-color: theme("colors.danger-hover"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger-hover); } &:focus { - @apply border-color-danger; - color: theme("colors.danger"); - box-shadow: inset 0 0 0 2px var(--calcite-color-status-danger); + --calcite-internal-button-border-color: var(--calcite-color-status-danger); + --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger); } &:active { - @apply border-color-danger-press; - color: theme("colors.danger-press"); - box-shadow: inset 0 0 0 2px var(--calcite-color-status-danger-press); + --calcite-internal-button-border-color: var(--calcite-color-status-danger-press); + --calcite-internal-button-text-color: theme("colors.danger-press"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger-press); } calcite-loader { - color: theme("colors.danger"); + --calcite-internal-button-loader-color: theme("colors.danger"); } } } :host([appearance="outline-fill"][kind="neutral"]) { button, a { - @apply text-color-1 bg-foreground-1; - border-color: theme("borderColor.color.1"); + --calcite-internal-button-border-color: theme("borderColor.color.1"); + --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); + &:hover { - box-shadow: inset 0 0 0 1px var(--calcite-color-foreground-3); + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } &:focus { - box-shadow: inset 0 0 0 2px var(--calcite-color-foreground-3); + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } &:active { - box-shadow: inset 0 0 0 2px var(--calcite-color-foreground-3); - } - calcite-loader { - @apply text-color-1; + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } } } :host([appearance="outline-fill"][kind="inverse"]) { button, a { - @apply text-color-1 bg-foreground-1; - border-color: var(--calcite-color-inverse); + --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-background-color: var(--calcite-color-foreground-1); + --calcite-internal-button-border-color: var(--calcite-color-inverse); + &:hover { - border-color: var(--calcite-color-inverse-hover); - box-shadow: inset 0 0 0 1px var(--calcite-color-inverse-hover); + --calcite-internal-button-border-color: var(--calcite-color-inverse-hover); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse-hover); } &:focus { - border-color: var(--calcite-color-inverse); - box-shadow: inset 0 0 0 2px var(--calcite-color-inverse); + --calcite-internal-button-border-color: var(--calcite-color-inverse); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse); } &:active { - border-color: var(--calcite-color-inverse-press); - box-shadow: inset 0 0 0 2px var(--calcite-color-inverse-press); - } - calcite-loader { - @apply text-color-1; + --calcite-internal-button-border-color: var(--calcite-color-inverse-press); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse-press); } } } :host([appearance="outline"]) { button, a { - @apply border border-solid bg-transparent; - box-shadow: inset 0 0 0 1px transparent; + --calcite-internal-button-background-color: var(--calcite-color-transparent); + + @apply border-solid; + border-width: var(--calcite-button-border-size, 1px); } } :host([appearance="outline"][kind="brand"]) { button, a { - @apply border-color-brand bg-transparent; - color: theme("colors.brand"); + --calcite-internal-button-border-color: var(--calcite-color-brand); + --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-background-color: var(--calcite-color-transparent); + &:hover { - @apply border-color-brand-hover; - color: theme("colors.brand-hover"); - box-shadow: inset 0 0 0 1px var(--calcite-color-brand-hover); + --calcite-internal-button-border-color: var(--calcite-color-brand-hover); + --calcite-internal-button-text-color: theme("colors.brand-hover"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand-hover); } &:focus { - @apply border-color-brand; - color: theme("colors.brand"); - box-shadow: inset 0 0 0 2px var(--calcite-color-brand); + --calcite-internal-button-border-color: var(--calcite-color-brand); + --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand); } &:active { - @apply border-color-brand-press; - color: theme("colors.brand-press"); - box-shadow: inset 0 0 0 2px var(--calcite-color-brand-press); + --calcite-internal-button-border-color: var(--calcite-color-brand-press); + --calcite-internal-button-text-color: theme("colors.brand-press"); + --calcite-internal-button-shadow-color: var(--calcite-color-brand-press); } calcite-loader { - color: theme("colors.brand"); + --calcite-internal-button-loader-color: theme("colors.brand"); } } } :host([appearance="outline"][kind="danger"]) { button, a { - @apply border-color-danger bg-transparent; - color: theme("colors.danger"); + --calcite-internal-button-border-color: var(--calcite-color-status-danger); + --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-background-color: var(--calcite-color-transparent); + &:hover { - @apply border-color-danger-hover; - color: theme("colors.danger-hover"); - box-shadow: inset 0 0 0 1px var(--calcite-color-status-danger-hover); + --calcite-internal-button-border-color: var(--calcite-color-status-danger-hover); + --calcite-internal-button-text-color: theme("colors.danger-hover"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger-hover); } &:focus { - @apply border-color-danger; - color: theme("colors.danger"); - box-shadow: inset 0 0 0 2px var(--calcite-color-status-danger); + --calcite-internal-button-border-color: var(--calcite-color-status-danger); + --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger); } &:active { - @apply border-color-danger-press; - color: theme("colors.danger-press"); - box-shadow: inset 0 0 0 2px var(--calcite-color-status-danger-press); + --calcite-internal-button-border-color: var(--calcite-color-status-danger-press); + --calcite-internal-button-text-color: theme("colors.danger-press"); + --calcite-internal-button-shadow-color: var(--calcite-color-status-danger-press); } calcite-loader { - color: theme("colors.danger"); + --calcite-internal-button-loader-color: theme("colors.danger"); } } } :host([appearance="outline"][kind="neutral"]) { button, a { - @apply text-color-1 bg-transparent; - border-color: theme("borderColor.color.1"); + --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-background-color: var(--calcite-color-transparent); + --calcite-internal-button-border-color: theme("borderColor.color.1"); + &:hover { - box-shadow: inset 0 0 0 1px var(--calcite-color-foreground-3); + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } &:focus { - box-shadow: inset 0 0 0 2px var(--calcite-color-foreground-3); + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } &:active { - box-shadow: inset 0 0 0 2px var(--calcite-color-foreground-3); - } - calcite-loader { - @apply text-color-1; + --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); } } } :host([appearance="outline"][kind="inverse"]) { button, a { - @apply text-color-1 bg-transparent; - border-color: var(--calcite-color-inverse); + --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-background-color: var(--calcite-color-transparent); + --calcite-internal-button-border-color: var(--calcite-color-inverse); &:hover { - border-color: var(--calcite-color-inverse-hover); - box-shadow: inset 0 0 0 1px var(--calcite-color-inverse-hover); + --calcite-internal-button-border-color: var(--calcite-color-inverse-hover); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse-hover); } &:focus { - border-color: var(--calcite-color-inverse); - box-shadow: inset 0 0 0 2px var(--calcite-color-inverse); + --calcite-internal-button-border-color: var(--calcite-color-inverse); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse); } &:active { - border-color: var(--calcite-color-inverse-press); - box-shadow: inset 0 0 0 2px var(--calcite-color-inverse-press); - } - calcite-loader { - @apply text-color-1; + --calcite-internal-button-border-color: var(--calcite-color-inverse-press); + --calcite-internal-button-shadow-color: var(--calcite-color-inverse-press); } } } @@ -501,7 +552,7 @@ :host([appearance="transparent"]:not(.enable-editing-button)) { button, a { - @apply bg-transparent; + background-color: var(--calcite-color-transparent); &:hover, &:focus { background-color: var(--calcite-color-transparent-hover); @@ -514,18 +565,18 @@ :host([appearance="transparent"][kind="brand"]) { button, a { - color: theme("colors.brand"); + --calcite-internal-button-text-color: theme("colors.brand"); &:hover { - color: theme("colors.brand-hover"); + --calcite-internal-button-text-color: theme("colors.brand-hover"); } &:focus { - color: theme("colors.brand"); + --calcite-internal-button-text-color: theme("colors.brand"); } &:active { - color: theme("colors.brand-press"); + --calcite-internal-button-text-color: theme("colors.brand-press"); } calcite-loader { - color: theme("colors.brand"); + --calcite-internal-button-loader-color: theme("colors.brand"); } } } @@ -533,18 +584,18 @@ :host([appearance="transparent"][kind="danger"]) { button, a { - color: theme("colors.danger"); + --calcite-internal-button-text-color: theme("colors.danger"); &:hover { - color: theme("colors.danger-hover"); + --calcite-internal-button-text-color: theme("colors.danger-hover"); } &:focus { - color: theme("colors.danger"); + --calcite-internal-button-text-color: theme("colors.danger"); } &:active { - color: theme("colors.danger-press"); + --calcite-internal-button-text-color: theme("colors.danger-press"); } calcite-loader { - color: theme("colors.danger"); + --calcite-internal-button-loader-color: theme("colors.danger"); } } } @@ -553,12 +604,13 @@ button, a, calcite-loader { - @apply text-color-1; + --calcite-internal-button-text-color: var(--calcite-color-text-1); } } :host([appearance="transparent"][kind="neutral"].cancel-editing-button) { button { + --calcite-internal-button-text-color: var(--calcite-color-text-3); @apply text-color-3 border-t-color-input border-b-color-input @@ -567,11 +619,11 @@ border-block-style: solid; &:not(.content--slotted) { - --calcite-button-padding-y-internal: 0; + --calcite-internal-button-padding-y-internal: 0; } &:hover { - @apply text-color-1; + --calcite-internal-button-text-color: var(--calcite-color-text-1); } } } @@ -596,7 +648,7 @@ button, a, calcite-loader { - @apply text-color-inverse; + --calcite-internal-button-text-color: var(--calcite-color-text-inverse); } } @@ -609,67 +661,67 @@ // accommodate for transparent buttons not having borders :host([scale="s"][appearance="transparent"]) button.content--slotted, :host([scale="s"][appearance="transparent"]) a.content--slotted { - --calcite-button-padding-x-internal: theme("padding.2"); + --calcite-internal-button-padding-x: theme("padding.2"); } :host([scale="s"]) button, :host([scale="s"]) a { - --calcite-button-padding-y-internal: 3px; + --calcite-internal-button-padding-y-internal: 3px; } :host([scale="m"]) button.content--slotted, :host([scale="m"]) a.content--slotted { - --calcite-button-padding-x-internal: 11px; + --calcite-internal-button-padding-x: 11px; @apply text-n1h; } :host([scale="m"]) button, :host([scale="m"]) a { - --calcite-button-padding-y-internal: 7px; + --calcite-internal-button-padding-y-internal: 7px; } // accommodate for transparent buttons not having borders :host([scale="m"][appearance="transparent"]) button.content--slotted, :host([scale="m"][appearance="transparent"]) a.content--slotted { - --calcite-button-padding-x-internal: theme("padding.3"); + --calcite-internal-button-padding-x: theme("padding.3"); } :host([scale="l"]) button.content--slotted, :host([scale="l"]) a.content--slotted { - --calcite-button-padding-x-internal: 15px; + --calcite-internal-button-padding-x: 15px; @apply text-0h; } :host([scale="l"]) { .button-padding { - --calcite-button-padding-x-internal: theme("padding.4"); - --calcite-button-padding-y-internal: 11px; + --calcite-internal-button-padding-x: theme("padding.4"); + --calcite-internal-button-padding-y-internal: 11px; } //shrink the padding if an icon is present to preserve the height .button-padding--shrunk { - --calcite-button-padding-y-internal: 9px; + --calcite-internal-button-padding-y-internal: 9px; } } // generate fab scales (scenario: 1 icon, ie., should be square) :host([scale="s"]) button:not(.content--slotted), :host([scale="s"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding[0.5]"); - --calcite-button-padding-y-internal: 3px; + --calcite-internal-button-padding-x: theme("padding[0.5]"); + --calcite-internal-button-padding-y-internal: 3px; @apply text-0h w-6; min-block-size: theme("height.6"); } :host([scale="m"]) button:not(.content--slotted), :host([scale="m"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding[0.5]"); - --calcite-button-padding-y-internal: 7px; + --calcite-internal-button-padding-x: theme("padding[0.5]"); + --calcite-internal-button-padding-y-internal: 7px; @apply text-0h w-8; min-block-size: theme("height.8"); } :host([scale="l"]) button:not(.content--slotted), :host([scale="l"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding[0.5]"); - --calcite-button-padding-y-internal: 9px; + --calcite-internal-button-padding-x: theme("padding[0.5]"); + --calcite-internal-button-padding-y-internal: 9px; @apply text-0h w-11; min-block-size: theme("height.11"); } @@ -686,33 +738,33 @@ // accommodate for transparent buttons not having borders :host([scale="l"][appearance="transparent"]) button:not(.content--slotted), :host([scale="l"][appearance="transparent"]) a:not(.content--slotted) { - --calcite-button-padding-y-internal: theme("padding[2.5]"); + --calcite-internal-button-padding-y-internal: theme("padding[2.5]"); } // generate fab scales (scenario: 2 icons, ie., should not be square) :host([scale="s"][icon-start][icon-end]) button:not(.content--slotted), :host([scale="s"][icon-start][icon-end]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: 23px; + --calcite-internal-button-padding-x: 23px; @apply text-0h h-6; } // accommodate for transparent buttons not having borders :host([scale="s"][icon-start][icon-end][appearance="transparent"]) button:not(.content--slotted), :host([scale="s"][icon-start][icon-end][appearance="transparent"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding.6"); + --calcite-internal-button-padding-x: theme("padding.6"); } :host([scale="m"][icon-start][icon-end]) button:not(.content--slotted), :host([scale="m"][icon-start][icon-end]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding.8"); + --calcite-internal-button-padding-x: theme("padding.8"); @apply text-0h h-8; } // accommodate for transparent buttons not having borders :host([scale="m"][icon-start][icon-end][appearance="transparent"]) button:not(.content--slotted), :host([scale="m"][icon-start][icon-end][appearance="transparent"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: 33px; + --calcite-internal-button-padding-x: 33px; } :host([scale="l"][icon-start][icon-end]) button:not(.content--slotted), :host([scale="l"][icon-start][icon-end]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: 43px; + --calcite-internal-button-padding-x: 43px; @apply text-0h h-11; // add space between when only 2 icons .icon--start + .icon--end { @@ -722,7 +774,7 @@ // accommodate for transparent buttons not having borders :host([scale="l"][icon-start][icon-end][appearance="transparent"]) button:not(.content--slotted), :host([scale="l"][icon-start][icon-end][appearance="transparent"]) a:not(.content--slotted) { - --calcite-button-padding-x-internal: theme("padding.11"); + --calcite-internal-button-padding-x: theme("padding.11"); } @include base-component(); diff --git a/packages/calcite-components/src/components/color-picker/color-picker.e2e.ts b/packages/calcite-components/src/components/color-picker/color-picker.e2e.ts index c1943a2c195..c97eefcf062 100644 --- a/packages/calcite-components/src/components/color-picker/color-picker.e2e.ts +++ b/packages/calcite-components/src/components/color-picker/color-picker.e2e.ts @@ -2470,4 +2470,14 @@ describe("calcite-color-picker", () => { }); }); }); + + it("does not throw when initialized with different format value (format='auto')", async () => { + async function doTest(): Promise { + const page = await newE2EPage(); + await page.setContent(``); + await page.waitForChanges(); + } + + await expect(doTest()).resolves.toBeUndefined(); + }); }); diff --git a/packages/calcite-components/src/components/color-picker/color-picker.tsx b/packages/calcite-components/src/components/color-picker/color-picker.tsx index 3b1b981cea5..ce157eb0d39 100644 --- a/packages/calcite-components/src/components/color-picker/color-picker.tsx +++ b/packages/calcite-components/src/components/color-picker/color-picker.tsx @@ -549,7 +549,8 @@ export class ColorPicker extends LitElement implements InteractiveComponent, Loa if (modeChanged || colorChanged) { this.internalColorSet( color, - this.alphaChannel && !(this.mode.endsWith("a") || this.mode.endsWith("a-css")), + (this.alphaChannel && !(this.mode.endsWith("a") || this.mode.endsWith("a-css"))) || + this.internalColorUpdateContext === "internal", "internal", ); } diff --git a/packages/calcite-components/src/components/combobox/combobox.e2e.ts b/packages/calcite-components/src/components/combobox/combobox.e2e.ts index 364eb1ed1f5..2b38fd91481 100644 --- a/packages/calcite-components/src/components/combobox/combobox.e2e.ts +++ b/packages/calcite-components/src/components/combobox/combobox.e2e.ts @@ -1264,7 +1264,7 @@ describe("calcite-combobox", () => { `, ); - const eventSpy = await page.spyOnEvent("calciteComboboxChipClose", "window"); + const eventSpy = await page.spyOnEvent("calciteComboboxChipClose"); const chip = await page.find("calcite-combobox >>> calcite-chip"); @@ -1285,6 +1285,7 @@ describe("calcite-combobox", () => { `); const input = await page.find("calcite-combobox >>> input"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); await input.click(); await input.press("K"); @@ -1298,6 +1299,7 @@ describe("calcite-combobox", () => { expect((await combobox.getProperty("selectedItems")).length).toBe(1); expect(await item.getProperty("selected")).toBe(true); + expect(eventSpy).toHaveReceivedEventTimes(1); }); it("should replace current value to new custom value in single selection mode", async () => { @@ -1312,6 +1314,7 @@ describe("calcite-combobox", () => { await skipAnimations(page); const combobox = await page.find("calcite-combobox"); const input = await page.find("calcite-combobox >>> input"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); await input.click(); await input.press("K"); @@ -1326,6 +1329,7 @@ describe("calcite-combobox", () => { expect((await combobox.getProperty("selectedItems")).length).toBe(1); expect(await customValue.getProperty("selected")).toBe(true); expect(await item1.getProperty("selected")).toBe(false); + expect(eventSpy).toHaveReceivedEventTimes(1); }); it("should auto-select new custom values in multiple selection mode", async () => { @@ -1339,6 +1343,7 @@ describe("calcite-combobox", () => { `); const combobox = await page.find("calcite-combobox"); const input = await page.find("calcite-combobox >>> input"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); await input.click(); await input.press("K"); @@ -1356,6 +1361,7 @@ describe("calcite-combobox", () => { expect(await customValue.getProperty("selected")).toBe(true); expect(await item1.getProperty("selected")).toBe(true); expect(await item2.getProperty("selected")).toBe(true); + expect(eventSpy).toHaveReceivedEventTimes(1); }); }); @@ -1666,17 +1672,21 @@ describe("calcite-combobox", () => { }); it("should not throw when typing custom value and pressing ArrowDown", async () => { - const combobox = await page.find("calcite-combobox"); - combobox.setProperty("allowCustomValues", true); - await page.waitForChanges(); - const inputEl = await page.find(`#myCombobox >>> input`); - await inputEl.focus(); - await page.waitForChanges(); - expect(await page.evaluate(() => document.activeElement.id)).toBe("myCombobox"); - await page.keyboard.type("asdf"); - await page.waitForChanges(); - await page.keyboard.press("ArrowDown"); - await page.waitForChanges(); + async function runTest(): Promise { + const combobox = await page.find("calcite-combobox"); + combobox.setProperty("allowCustomValues", true); + await page.waitForChanges(); + const inputEl = await page.find(`#myCombobox >>> input`); + await inputEl.focus(); + await page.waitForChanges(); + expect(await page.evaluate(() => document.activeElement.id)).toBe("myCombobox"); + await page.keyboard.type("asdf"); + await page.waitForChanges(); + await page.keyboard.press("ArrowDown"); + await page.waitForChanges(); + } + + await expect(runTest()).resolves.toBeUndefined(); }); it(`ArrowDown opens the item group for combobox in focus and jumps to the first item`, async () => { @@ -1766,7 +1776,7 @@ describe("calcite-combobox", () => { }); it("should cycle through items on ArrowUp/ArrowDown and toggle selection on/off on Enter", async () => { - const eventSpy = await page.spyOnEvent("calciteComboboxChange", "window"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); const item1 = await page.find("calcite-combobox-item#one"); const item2 = await page.find("calcite-combobox-item#two"); const item3 = await page.find("calcite-combobox-item#three"); @@ -1988,6 +1998,27 @@ describe("calcite-combobox", () => { expect(eventSpy).toHaveReceivedEventTimes(1); expect((await element.getProperty("selectedItems")).length).toBe(2); }); + + it("should not emit calciteComboboxChange event when value attribute is updated", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + `, + ); + await page.waitForChanges(); + + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); + const combobox = await page.find("calcite-combobox"); + expect(await combobox.getProperty("value")).toBe("one"); + + combobox.setProperty("value", "two"); + await page.waitForChanges(); + expect(eventSpy).toHaveReceivedEventTimes(0); + expect(await combobox.getProperty("value")).toBe("two"); + }); }); describe("calciteComboboxItemChange event correctly updates active item index", () => { @@ -2063,6 +2094,7 @@ describe("calcite-combobox", () => { `); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); let chip = await page.find("calcite-combobox >>> calcite-chip"); expect(chip).toBeNull(); @@ -2071,6 +2103,7 @@ describe("calcite-combobox", () => { await element.press("K"); await element.press("Enter"); + expect(eventSpy).toHaveReceivedEventTimes(1); chip = await page.find("calcite-combobox >>> calcite-chip"); expect(chip).toBeDefined(); @@ -2082,6 +2115,7 @@ describe("calcite-combobox", () => { await element.press("Enter"); const chips = await page.findAll("calcite-combobox >>> calcite-chip"); expect(chips.length).toBe(1); + expect(eventSpy).toHaveReceivedEventTimes(2); }); it("should fire calciteComboboxChange when entering new unknown tag", async () => { @@ -2099,6 +2133,7 @@ describe("calcite-combobox", () => { await input.press("K"); await input.press("Enter"); + await page.waitForChanges(); expect(eventSpy).toHaveReceivedEventTimes(1); }); @@ -2113,6 +2148,7 @@ describe("calcite-combobox", () => { `); const chip = await page.find("calcite-combobox >>> calcite-chip"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); expect(chip).toBeNull(); const input = await page.find("calcite-combobox >>> input"); @@ -2130,6 +2166,7 @@ describe("calcite-combobox", () => { let chips = await page.findAll("calcite-combobox >>> calcite-chip"); expect(chips.length).toBe(1); + expect(eventSpy).toHaveReceivedEventTimes(1); await input.press("j"); await page.waitForChanges(); @@ -2142,6 +2179,7 @@ describe("calcite-combobox", () => { chips = await page.findAll("calcite-combobox >>> calcite-chip"); expect(chips.length).toBe(2); + expect(eventSpy).toHaveReceivedEventTimes(2); }); it("should select known tag when input", async () => { @@ -2154,6 +2192,7 @@ describe("calcite-combobox", () => { `); let chip = await page.find("calcite-combobox >>> calcite-chip"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); expect(chip).toBeNull(); const input = await page.find("calcite-combobox >>> input"); @@ -2169,6 +2208,7 @@ describe("calcite-combobox", () => { expect(await chip.getProperty("value")).toBe("one"); const item1 = await page.find("calcite-combobox-item#one"); expect(await item1.getProperty("selected")).toBe(true); + expect(eventSpy).toHaveReceivedEventTimes(1); }); }); @@ -2448,8 +2488,9 @@ describe("calcite-combobox", () => { const page = await newE2EPage(); await page.setContent(html); await skipAnimations(page); + const openEvent = page.waitForEvent("calciteComboboxOpen"); await page.click("calcite-combobox"); - await page.waitForChanges(); + await openEvent; const activeItem = await page.find("calcite-combobox-item[active]"); expect(await activeItem.getProperty("value")).toBe(expectedActiveItemValue); @@ -2557,12 +2598,12 @@ describe("calcite-combobox", () => { async function assertClickOutside(selectionMode = "multiple", allowCustomValues = false): Promise { const combobox = await page.find("calcite-combobox"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); combobox.setProperty("selectionMode", selectionMode); combobox.setProperty("allowCustomValues", allowCustomValues); + await page.waitForChanges(); const inputEl = await page.find(`#myCombobox >>> input`); - await inputEl.focus(); - await page.waitForChanges(); expect(await page.evaluate(() => document.activeElement.id)).toBe("myCombobox"); const comboboxRect = await page.evaluate(() => { @@ -2580,6 +2621,7 @@ describe("calcite-combobox", () => { expect(await page.evaluate(() => document.activeElement.id)).not.toBe("myCombobox"); expect(await inputEl.getProperty("value")).toBe(""); expect(await combobox.getProperty("value")).toBe(allowCustomValues ? "three" : ""); + expect(eventSpy).toHaveReceivedEventTimes(allowCustomValues ? 1 : 0); } selectionModes.forEach((mode) => { @@ -2608,6 +2650,7 @@ describe("calcite-combobox", () => { async function clearInputValueOnBlur(selectionMode = "multiple", allowCustomValues = false): Promise { const combobox = await page.find("calcite-combobox"); + const eventSpy = await page.spyOnEvent("calciteComboboxChange"); combobox.setProperty("selectionMode", selectionMode); combobox.setProperty("allowCustomValues", allowCustomValues); const inputEl = await page.find(`#myCombobox >>> input`); @@ -2622,6 +2665,7 @@ describe("calcite-combobox", () => { await page.waitForChanges(); expect(await inputEl.getProperty("value")).toBe(""); expect(await combobox.getProperty("value")).toBe(allowCustomValues ? "three" : ""); + expect(eventSpy).toHaveReceivedEventTimes(allowCustomValues ? 1 : 0); } selectionModes.forEach((mode) => { @@ -2770,4 +2814,25 @@ describe("calcite-combobox", () => { expect(await combobox.getProperty("value")).toBe("three"); }); + + it("should not emit calciteComboboxItemChange event when selected attribute is toggled", async () => { + const page = await newE2EPage(); + await page.setContent( + html` + + + + `, + ); + await page.waitForChanges(); + + const eventSpy = await page.spyOnEvent("calciteComboboxItemChange"); + const two = await page.find("#two"); + two.setProperty("selected", "true"); + await page.waitForChanges(); + expect(eventSpy).toHaveReceivedEventTimes(0); + + const combobox = await page.find("calcite-combobox"); + expect((await combobox.getProperty("selectedItems")).length).toBe(1); + }); }); diff --git a/packages/calcite-components/src/components/combobox/combobox.tsx b/packages/calcite-components/src/components/combobox/combobox.tsx index 5f294220572..3c32e3a3004 100644 --- a/packages/calcite-components/src/components/combobox/combobox.tsx +++ b/packages/calcite-components/src/components/combobox/combobox.tsx @@ -108,10 +108,6 @@ export class Combobox // #region Private Properties - private internalComboboxChangeEvent = (): void => { - this.calciteComboboxChange.emit(); - }; - private allSelectedIndicatorChipEl: Chip["el"]; private chipContainerEl: HTMLDivElement; @@ -120,7 +116,9 @@ export class Combobox defaultValue: Combobox["value"]; - private emitComboboxChange = debounce(this.internalComboboxChangeEvent, 0); + private emitComboboxChange(): void { + this.calciteComboboxChange.emit(); + } private get effectiveFilterProps(): string[] { if (!this.filterProps) { @@ -217,6 +215,8 @@ export class Combobox private groupData: GroupData[]; + private groupItems: HTMLCalciteComboboxItemGroupElement["el"][] = []; + private guid = guid(); private ignoreSelectedEventsFlag = false; @@ -225,6 +225,8 @@ export class Combobox private internalValueChangeFlag = false; + private items: HTMLCalciteComboboxItemElement["el"][] = []; + labelEl: Label["el"]; private listContainerEl: HTMLDivElement; @@ -279,12 +281,6 @@ export class Combobox @state() compactSelectionDisplay = false; - @state() groupItems: HTMLCalciteComboboxItemGroupElement["el"][] = []; - - @state() items: HTMLCalciteComboboxItemElement["el"][] = []; - - @state() needsIcon: boolean; - @state() selectedHiddenChipsCount = 0; @state() selectedVisibleChipsCount = 0; @@ -565,7 +561,6 @@ export class Combobox async load(): Promise { setUpLoadableComponent(this); - this.filterItems(this.filterText, false, false); } override willUpdate(changes: PropertyValues): void { @@ -612,16 +607,15 @@ export class Combobox } updateHostInteraction(this); - - if (!this.hasUpdated) { - this.refreshSelectionDisplay(); - } + this.refreshSelectionDisplay(); } loaded(): void { afterConnectDefaultValueSet(this, this.getValue()); connectFloatingUI(this); setComponentLoaded(this); + this.updateItems(); + this.filterItems(this.filterText, false, false); } override disconnectedCallback(): void { @@ -659,14 +653,10 @@ export class Combobox private valueHandler(value: string | string[]): void { if (!this.internalValueChangeFlag) { - const items = this.getItems(); - if (Array.isArray(value)) { - items.forEach((item) => (item.selected = value.includes(item.value))); - } else if (value) { - items.forEach((item) => (item.selected = value === item.value)); - } else { - items.forEach((item) => (item.selected = false)); - } + this.getItems().forEach((item) => { + item.selected = Array.isArray(value) ? value.includes(item.value) : value === item.value; + }); + this.updateItems(); } } @@ -719,7 +709,9 @@ export class Combobox event: CustomEvent, ): void { event.stopPropagation(); - this.updateItems(); + if (this.hasUpdated) { + this.updateItems(); + } } private clearValue(): void { @@ -932,6 +924,7 @@ export class Combobox listContainerEl.style.inlineSize = `${referenceEl.clientWidth}px`; await this.reposition(true); } + private calciteChipCloseHandler(comboboxItem: HTMLCalciteComboboxItemElement["el"]): void { this.open = false; @@ -1230,15 +1223,20 @@ export class Combobox return this.filterText === "" ? this.items : this.items.filter((item) => !isHidden(item)); } - private updateItems = debounce((): void => { + private updateItems(): void { this.items = this.getItems(); this.groupItems = this.getGroupItems(); + this.data = this.getData(); this.groupData = this.getGroupData(); + + this.updateItemProps(); + this.selectedItems = this.getSelectedItems(); this.filteredItems = this.getFilteredItems(); - this.needsIcon = this.getNeedsIcon(); + } + private updateItemProps(): void { this.items.forEach((item) => { item.selectionMode = this.selectionMode; item.scale = this.scale; @@ -1261,7 +1259,7 @@ export class Combobox nextGroupItem.afterEmptyGroup = groupItem.children.length === 0; } }); - }, DEBOUNCE.nextTick); + } private getData(): ItemData[] { return this.items.map((item) => ({ @@ -1282,10 +1280,6 @@ export class Combobox })); } - private getNeedsIcon(): boolean { - return isSingleLike(this.selectionMode) && this.items.some((item) => item.icon); - } - private resetText(): void { if (this.textInput.value) { this.textInput.value.value = ""; @@ -1309,9 +1303,6 @@ export class Combobox if (existingItem) { this.toggleSelection(existingItem, true); } else { - if (!this.isMulti()) { - this.toggleSelection(this.selectedItems[this.selectedItems.length - 1], false); - } const item = document.createElement( // TODO: [MIGRATION] If this is dynamically creating a web component, please read the docs: https://qawebgis.esri.com/arcgis-components/?path=/docs/lumina-jsx--docs#rendering-jsx-outside-the-component "calcite-combobox-item", @@ -1320,14 +1311,11 @@ export class Combobox item.heading = value; item.selected = true; this.el.prepend(item); - this.resetText(); + this.toggleSelection(item, true); + this.open = true; if (focus) { this.setFocus(); } - this.updateItems(); - this.filterItems(""); - this.open = true; - this.emitComboboxChange(); } } diff --git a/packages/calcite-components/src/components/panel/panel.scss b/packages/calcite-components/src/components/panel/panel.scss index 3cb4518df30..2ab0af241e3 100644 --- a/packages/calcite-components/src/components/panel/panel.scss +++ b/packages/calcite-components/src/components/panel/panel.scss @@ -7,25 +7,27 @@ * @prop --calcite-panel-heading-text-color: Specifies the text color of the component's `heading`. * @prop --calcite-panel-description-text-color: Specifies the text color of the component's `description`. * @prop --calcite-panel-border-color: Specifies the component's border color. - * @prop --calcite-panel-background-color: Specifies the component's background color. * @prop --calcite-panel-header-background-color: Specifies the background color of the component's header. * @prop --calcite-panel-footer-background-color: Specifies the background color of the component's footer. - * @prop --calcite-panel-space: Specifies the padding of the component's `"unnamed (default)"` slot. * @prop --calcite-panel-header-content-space: Specifies the padding of the `"header-content"` slot. * @prop --calcite-panel-footer-space: Specifies the padding of the component's footer. - * @prop --calcite-action-background-color: Specifies the background color of the component's `closable`, `collapsible`, and `back` `calcite-action`s. Applies to any slotted `calcite-action`s. * @prop --calcite-action-background-color-hover: Specifies the background color of the component's `closable`, `collapsible`, and `back` `calcite-action`s when hovered. Applies to any slotted `calcite-action`s. * @prop --calcite-action-background-color-pressed: Specifies the background color of the component's `closable`, `collapsible`, and `back` `calcite-action`s when pressed. Applies to any slotted `calcite-action`s. * @prop --calcite-action-text-color-hover: Specifies the text and icon color of the component's `closable`, `collapsible`, and `back` `calcite-action`s when hovered. Applies to any slotted `calcite-action`s. * @prop --calcite-action-text-color-pressed: Specifies the text and icon color of the component's `closable`, `collapsible`, and `back` `calcite-action`s when pressed. Applies to any slotted `calcite-action`s. * @prop --calcite-popover-border-color: Specifies the border color of the component's internally rendered `calcite-popover`, which is rendered within a `calcite-action` menu when slotted `calcite-action`s are present in the `header-actions-end` slot. Applies to any slotted `calcite-popover`s. - * @prop --calcite-panel-content-space: [Deprecated] Use `--calcite-panel-space` instead. Specifies the padding of the component's content. * @prop --calcite-panel-footer-padding: [Deprecated] Use `--calcite-panel-footer-space` instead. Specifies the padding of the component's footer. * @prop --calcite-panel-header-border-block-end: [Deprecated] Use `--calcite-panel-border-color` instead. Specifies the component header's block end border. + * @prop --calcite-panel-header-action-background-color-hover: Specifies the background color of the component's `calcite-action` items in the panel header when hovered. + * @prop --calcite-panel-header-action-background-color-press: Specifies the background color of the component's `calcite-action` items in the panel header when pressed. + * @prop --calcite-panel-header-action-background-color: Specifies the background color of the component's `calcite-action` items in the panel header. + * @prop --calcite-panel-header-action-indicator-color: Specifies the color of the component's `calcite-action` items' indicator in the panel header. + * @prop --calcite-panel-header-action-text-color-press: Specifies the text color of the component's `calcite-action` items in the panel header when pressed. + * @prop --calcite-panel-header-action-text-color: Specifies the text color of the component's `calcite-action` items in the panel header. */ :host { @@ -147,6 +149,15 @@ margin-inline-end: auto; justify-content: center; + .header-actions { + --calcite-action-background-color-hover: var(--calcite-panel-header-action-background-color-hover); + --calcite-action-background-color-press: var(--calcite-panel-header-action-background-color-press); + --calcite-action-background-color: var(--calcite-panel-header-action-background-color); + --calcite-action-indicator-color: var(--calcite-panel-header-action-indicator-color); + --calcite-action-text-color-press: var(--calcite-panel-header-action-text-color-press); + --calcite-action-text-color: var(--calcite-panel-header-action-text-color); + } + .heading, .description { @apply block diff --git a/packages/calcite-components/src/components/table/table.tsx b/packages/calcite-components/src/components/table/table.tsx index bf6b8fb1c02..e44a4571072 100644 --- a/packages/calcite-components/src/components/table/table.tsx +++ b/packages/calcite-components/src/components/table/table.tsx @@ -70,9 +70,6 @@ export class Table extends LitElement implements LoadableComponent { @state() pageStartRow = 1; - /* Workaround for Safari https://bugs.webkit.org/show_bug.cgi?id=258430 https://bugs.webkit.org/show_bug.cgi?id=239478 */ - - // ⚠️ browser-sniffing is not a best practice and should be avoided ⚠️ @state() readCellContentsToAT: boolean; @state() selectedCount = 0; @@ -179,7 +176,11 @@ export class Table extends LitElement implements LoadableComponent { async load(): Promise { setUpLoadableComponent(this); + + /* Workaround for Safari https://bugs.webkit.org/show_bug.cgi?id=258430 https://bugs.webkit.org/show_bug.cgi?id=239478 */ + // ⚠️ browser-sniffing is not a best practice and should be avoided ⚠️ this.readCellContentsToAT = /safari/i.test(getUserAgentString()); + this.listenOn(this.el.shadowRoot, "slotchange", this.handleSlotChange); } diff --git a/packages/calcite-components/src/components/tree-item/tree-item.scss b/packages/calcite-components/src/components/tree-item/tree-item.scss index 3880270c15a..a33efbd7cee 100644 --- a/packages/calcite-components/src/components/tree-item/tree-item.scss +++ b/packages/calcite-components/src/components/tree-item/tree-item.scss @@ -59,7 +59,7 @@ z-default; // ensure lines don't overlap focus outline block-size: 100%; - left: var(--calcite-internal-tree-item-line-left-position); + inset-inline-start: var(--calcite-internal-tree-item-line-left-position); content: ""; background-color: var(--calcite-color-border-2); } diff --git a/packages/calcite-components/src/components/tree/tree.stories.ts b/packages/calcite-components/src/components/tree/tree.stories.ts index 149bd33b16f..b0e9916dda0 100644 --- a/packages/calcite-components/src/components/tree/tree.stories.ts +++ b/packages/calcite-components/src/components/tree/tree.stories.ts @@ -141,13 +141,17 @@ const iconStartLargeActionsEnd = (scale: string) => html` `; export const singleSelectionMode = (): string => html` ${treeItems()} `; - singleSelectionMode.decorators = [allScaleTreeBuilder]; +singleSelectionMode.args = { selectionMode: "single" }; export const selectionModeNone = (): string => html`${treeItems()}`; selectionModeNone.decorators = [allScaleTreeBuilder]; selectionModeNone.args = { selectionMode: "none" }; +export const linesRTL = (): string => html`
${treeItems()}
`; +linesRTL.decorators = [allScaleTreeBuilder]; +linesRTL.args = { lines: true, selectionMode: "single" }; + export const multipleSelectionModeWithLines_TestOnly = (): string => html` ${treeItems()} `; multipleSelectionModeWithLines_TestOnly.decorators = [allScaleTreeBuilder]; @@ -164,8 +168,8 @@ singlePersistSelectionModeWithLines_TestOnly.decorators = [allScaleTreeBuilder]; singlePersistSelectionModeWithLines_TestOnly.args = { lines: true, selectionMode: "single-persist" }; export const iconStartAndActionsEnd = (scale: string): string => html` ${iconStartLargeActionsEnd(scale)} `; - iconStartAndActionsEnd.decorators = [allScaleTreeBuilder]; +iconStartAndActionsEnd.args = { selectionMode: "single" }; export const treeItemTextContentWraps_TestOnly = (): string => html` @@ -207,8 +211,9 @@ export const treeItemContentIsNotClipped_TestOnly = (): string => html` `; -export const darkModeRTL_TestOnly = (): string => html` ${treeItems()} `; +export const darkModeRTL_TestOnly = (): string => html`
${treeItems()}
`; darkModeRTL_TestOnly.parameters = { themes: modesDarkDefault }; +darkModeRTL_TestOnly.args = { selectionMode: "single" }; darkModeRTL_TestOnly.decorators = [allScaleTreeBuilder]; export const OverflowingSubtree = (): string => diff --git a/packages/calcite-components/src/utils/loadable.ts b/packages/calcite-components/src/utils/loadable.ts index 3f072448caf..c6f7f56e8e9 100644 --- a/packages/calcite-components/src/utils/loadable.ts +++ b/packages/calcite-components/src/utils/loadable.ts @@ -56,6 +56,7 @@ export interface LoadableComponent {} * * @param _component */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars -- this method is deprecated, and we allow it to be empty for incremental migration export function setUpLoadableComponent(_component: LoadableComponent): void { // intentionally empty } @@ -74,6 +75,7 @@ export function setUpLoadableComponent(_component: LoadableComponent): void { * * @param _component */ +// eslint-disable-next-line @typescript-eslint/no-unused-vars -- this method is deprecated, and we allow it to be empty for incremental migration export function setComponentLoaded(_component: LoadableComponent): void { // intentionally empty } diff --git a/packages/calcite-ui-icons/CHANGELOG.md b/packages/calcite-ui-icons/CHANGELOG.md index 32ae894d226..f45e0386ce2 100644 --- a/packages/calcite-ui-icons/CHANGELOG.md +++ b/packages/calcite-ui-icons/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.0.0-next.10](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-ui-icons@4.0.0-next.9...@esri/calcite-ui-icons@4.0.0-next.10) (2025-01-22) + +**Note:** Version bump only for package @esri/calcite-ui-icons + ## [4.0.0-next.9](https://github.com/Esri/calcite-design-system/compare/@esri/calcite-ui-icons@4.0.0-next.8...@esri/calcite-ui-icons@4.0.0-next.9) (2025-01-17) ### Features diff --git a/packages/calcite-ui-icons/package.json b/packages/calcite-ui-icons/package.json index 60c509bb11d..6242da74d83 100644 --- a/packages/calcite-ui-icons/package.json +++ b/packages/calcite-ui-icons/package.json @@ -1,6 +1,6 @@ { "name": "@esri/calcite-ui-icons", - "version": "4.0.0-next.9", + "version": "4.0.0-next.10", "private": false, "description": "A collection of UI SVG icons created by Esri for applications.", "keywords": [