diff --git a/packages/calcite-components/src/assets/styles/includes.scss b/packages/calcite-components/src/assets/styles/includes.scss index 1f34b2dff8a..383de896e84 100644 --- a/packages/calcite-components/src/assets/styles/includes.scss +++ b/packages/calcite-components/src/assets/styles/includes.scss @@ -106,50 +106,53 @@ } } -@mixin x-button() { - :host([scale="s"]) { - .x-button { - inline-size: theme("spacing.4"); - block-size: theme("spacing.4"); - } - } - - :host([scale="m"]) { - .x-button { - inline-size: theme("spacing.6"); - block-size: theme("spacing.6"); - } - } - - :host([scale="l"]) { - .x-button { - inline-size: theme("spacing.8"); - block-size: theme("spacing.8"); - } - } - +@mixin x-button( + $size: "var(--calcite-internal-close-size, 1.5rem)", + $padding: "0", + $color: "var(--calcite-close-icon-color, var(--calcite-color-text-1))", + $background-color: "var(--calcite-close-background-color, var(--calcite-color-transparent))", + $background-color-hover: "var(--calcite-close-background-color-hover, var(--calcite-color-transparent-hover))", + $background-color-press: "var(--calcite-close-background-color-press, var(--calcite-color-transparent-press))" +) { .x-button { - @apply appearance-none bg-transparent border-2 content-center cursor-pointer flex focus-base items-center justify-center m-0 self-center text-color-3 transition-default; - - border-radius: 50%; - border-color: transparent; - background-color: var(--calcite-color-foreground-2); + @apply transition-default; + border-style: none; + cursor: pointer; + outline-color: transparent; + align-items: center; + margin: 0; + background-color: #{$background-color}; + -webkit-appearance: none; + display: flex; + align-content: center; + justify-content: center; + color: #{$color}; + block-size: #{$size}; + inline-size: #{$size}; + min-block-size: #{$size}; + min-inline-size: #{$size}; + padding: #{$padding}; - &:active, - &:hover { - @apply text-color-1; - background-color: var(--calcite-color-foreground-3); + &:hover, + &:focus { + background-color: #{$background-color-hover}; + } + &:focus { + @apply focus-inset; } &:active { - @apply border-solid; - border-color: theme("borderColor.color-brand"); + background-color: #{$background-color-press}; } - & calcite-icon { + calcite-icon { color: inherit; } } + + .x-button--round { + border-radius: 9999px; + } } @mixin close-button( diff --git a/packages/calcite-components/src/components/button/button.e2e.ts b/packages/calcite-components/src/components/button/button.e2e.ts index e22d5988e87..12bef5d60a4 100644 --- a/packages/calcite-components/src/components/button/button.e2e.ts +++ b/packages/calcite-components/src/components/button/button.e2e.ts @@ -509,14 +509,11 @@ describe("calcite-button", () => { Layers `; - let page; - let buttonEl; - let buttonHoverStyle; it("should have defined CSS custom properties", async () => { - page = await newE2EPage({ html: buttonSnippet }); + const page = await newE2EPage({ html: buttonSnippet }); const buttonStyles = await page.evaluate(() => { - buttonEl = document.querySelector("calcite-button"); + const buttonEl = document.querySelector("calcite-button"); buttonEl.style.setProperty("--calcite-color-transparent-hover", "rgba(34, 23, 200, 0.4)"); buttonEl.style.setProperty("--calcite-color-transparent-press", "rgba(1, 20, 44, 0.1"); return { @@ -530,31 +527,31 @@ describe("calcite-button", () => { describe("when mode attribute is not provided", () => { it("should render button pseudo classes with default values tied to light mode", async () => { - page = await newE2EPage({ html: buttonSnippet }); - buttonEl = await page.find("calcite-button >>> button"); + const page = await newE2EPage({ html: buttonSnippet }); + const buttonEl = await page.find("calcite-button >>> button"); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(); + const buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(0, 0, 0, 0.04)"); }); }); describe("when mode attribute is dark", () => { it("should render button pseudo classes with value tied to dark mode", async () => { - page = await newE2EPage({ + const page = await newE2EPage({ html: `
${buttonSnippet}
`, }); - buttonEl = await page.find("calcite-button >>> button"); + const buttonEl = await page.find("calcite-button >>> button"); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(); + const buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual("rgba(255, 255, 255, 0.04)"); }); }); it("should allow the CSS custom property to be overridden", async () => { const overrideStyle = "rgba(255, 255, 0, 0.9)"; - page = await newE2EPage({ + const page = await newE2EPage({ html: `
${buttonSnippet}
`, }); - buttonEl = await page.find("calcite-button >>> button"); + const buttonEl = await page.find("calcite-button >>> button"); await buttonEl.hover(); await page.waitForChanges(); - buttonHoverStyle = await buttonEl.getComputedStyle(); + const buttonHoverStyle = await buttonEl.getComputedStyle(); expect(buttonHoverStyle.getPropertyValue("background-color")).toEqual(overrideStyle); }); }); diff --git a/packages/calcite-components/src/components/button/button.scss b/packages/calcite-components/src/components/button/button.scss index c101ad48115..de0a7697e3c 100644 --- a/packages/calcite-components/src/components/button/button.scss +++ b/packages/calcite-components/src/components/button/button.scss @@ -3,9 +3,10 @@ * * 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-background-color: Specifies the component's background color. + * @prop --calcite-button-border-color: Specifies the component's border color. * @prop --calcite-button-corner-radius: Specifies the component's corner radius. + * @prop --calcite-button-icon-color: Specifies the component's `iconStart` and/or `iconEnd` color. * @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. @@ -20,7 +21,7 @@ button { --calcite-internal-button-content-margin: theme("margin.2"); --calcite-internal-button-padding-x: 7px; - --calcite-internal-button-padding-y-internal: 3px; + --calcite-internal-button-padding-y: 3px; @apply appearance-none border-none @@ -43,13 +44,41 @@ --calcite-button-background-color, var(--calcite-internal-button-background-color, var(--calcite-color-transparent)) ); - border-color: var( + border-block-start-color: var( --calcite-button-border-color, - var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + var( + --calcite-internal-button-border-block-start-color, + var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + ) ); + border-block-end-color: var( + --calcite-button-border-color, + var( + --calcite-internal-button-border-block-end-color, + var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + ) + ); + border-inline-start-color: var( + --calcite-button-border-color, + var( + --calcite-internal-button-border-inline-start-color, + var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + ) + ); + border-inline-end-color: var( + --calcite-button-border-color, + var( + --calcite-internal-button-border-inline-end-color, + var(--calcite-internal-button-border-color, var(--calcite-color-transparent)) + ) + ); + border-style: solid; + border-width: var(--calcite-border-width-sm); + box-shadow: inset 0 0 0 0 + var(--calcite-button-shadow-color, var(--calcite-internal-button-shadow-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-block: var(--calcite-internal-button-padding-y); padding-inline: var(--calcite-internal-button-padding-x); // include transition from focus @@ -72,7 +101,13 @@ } calcite-loader { - color: var(--calcite-button-loader-color, var(--calcite-internal-button-loader-color, currentColor)); + color: var( + --calcite-button-loader-color, + var( + --calcite-internal-button-loader-color, + var(--calcite-button-text-color, var(--calcite-internal-button-text-color)) + ) + ); } } } @@ -177,14 +212,6 @@ } } -.icon { - @apply relative - m-0 - inline-flex - font-normal; - line-height: inherit; -} - @include disabled(); @keyframes loader-in { @@ -254,6 +281,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-inverse); --calcite-internal-button-background-color: var(--calcite-color-brand); + --calcite-internal-button-loader-color: var(--calcite-color-text-inverse); &:hover { --calcite-internal-button-background-color: var(--calcite-color-brand-hover); @@ -268,6 +296,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-inverse); --calcite-internal-button-background-color: var(--calcite-color-status-danger); + --calcite-internal-button-loader-color: var(--calcite-color-text-inverse); &:hover { --calcite-internal-button-background-color: var(--calcite-color-status-danger-hover); @@ -282,6 +311,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-1); --calcite-internal-button-background-color: var(--calcite-color-foreground-3); + --calcite-internal-button-loader-color: var(--calcite-color-text-1); &:hover { --calcite-internal-button-background-color: var(--calcite-color-foreground-2); } @@ -295,6 +325,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-inverse); --calcite-internal-button-background-color: var(--calcite-color-inverse); + --calcite-internal-button-loader-color: var(--calcite-color-text-inverse); &:hover { --calcite-internal-button-background-color: var(--calcite-color-inverse-hover); } @@ -323,6 +354,7 @@ a { --calcite-internal-button-border-color: var(--calcite-color-brand); --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-loader-color: var(--calcite-color-brand); &:hover { --calcite-internal-button-border-color: var(--calcite-color-brand-hover); @@ -349,6 +381,7 @@ a { --calcite-internal-button-border-color: var(--calcite-color-status-danger); --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-loader-color: var(--calcite-color-status-danger); &:hover { --calcite-internal-button-border-color: var(--calcite-color-status-danger-hover); @@ -375,6 +408,7 @@ a { --calcite-internal-button-border-color: theme("borderColor.color.1"); --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-loader-color: var(--calcite-color-text-1); &:hover { --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); @@ -405,6 +439,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-1); --calcite-internal-button-border-color: var(--calcite-color-inverse); + --calcite-internal-button-loader-color: var(--calcite-color-text-1); &:hover { --calcite-internal-button-border-color: var(--calcite-color-inverse-hover); @@ -441,6 +476,7 @@ a { --calcite-internal-button-border-color: var(--calcite-color-brand); --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-loader-color: var(--calcite-color-brand); &:hover { --calcite-internal-button-border-color: var(--calcite-color-brand-hover); @@ -467,6 +503,7 @@ a { --calcite-internal-button-border-color: var(--calcite-color-status-danger); --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-loader-color: var(--calcite-color-status-danger); &:hover { --calcite-internal-button-border-color: var(--calcite-color-status-danger-hover); @@ -493,6 +530,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-1); --calcite-internal-button-border-color: theme("borderColor.color.1"); + --calcite-internal-button-loader-color: var(--calcite-color-text-1); &:hover { --calcite-internal-button-shadow-color: var(--calcite-color-foreground-3); @@ -512,6 +550,7 @@ a { --calcite-internal-button-text-color: var(--calcite-color-text-1); --calcite-internal-button-border-color: var(--calcite-color-inverse); + --calcite-internal-button-loader-color: var(--calcite-color-text-1); &:hover { --calcite-internal-button-border-color: var(--calcite-color-inverse-hover); --calcite-internal-button-shadow-color: var(--calcite-color-inverse-hover); @@ -540,6 +579,20 @@ } // transparent +:host([appearance="transparent"]) { + button, + a { + --calcite-internal-button-background-color: var(--calcite-color-transparent); + + &:hover, + &:focus { + --calcite-internal-button-background-color: var(--calcite-color-transparent-hover); + } + &:active { + --calcite-internal-button-background-color: var(--calcite-color-transparent-press); + } + } +} :host([appearance="transparent"]:not(.enable-editing-button)) { button, a { @@ -556,6 +609,7 @@ button, a { --calcite-internal-button-text-color: theme("colors.brand"); + --calcite-internal-button-loader-color: var(--calcite-color-brand); &:hover { --calcite-internal-button-text-color: theme("colors.brand-hover"); } @@ -575,6 +629,7 @@ button, a { --calcite-internal-button-text-color: theme("colors.danger"); + --calcite-internal-button-loader-color: var(--calcite-color-status-danger); &:hover { --calcite-internal-button-text-color: theme("colors.danger-hover"); } @@ -601,29 +656,14 @@ :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 - border-t - border-b; - border-block-style: solid; - - &:not(.content--slotted) { - --calcite-internal-button-padding-y-internal: 0; - } &:hover { --calcite-internal-button-text-color: var(--calcite-color-text-1); + --calcite-internal-button-padding-y: 0; } } } -:host([appearance="transparent"][kind="neutral"].enable-editing-button) { - button { - @apply bg-transparent; - } -} - :host(.confirm-changes-button), :host(.cancel-editing-button), :host(.enable-editing-button) { @@ -656,7 +696,7 @@ :host([scale="s"]) button, :host([scale="s"]) a { - --calcite-internal-button-padding-y-internal: 3px; + --calcite-internal-button-padding-y: 3px; } :host([scale="m"]) button.content--slotted, @@ -667,7 +707,7 @@ :host([scale="m"]) button, :host([scale="m"]) a { - --calcite-internal-button-padding-y-internal: 7px; + --calcite-internal-button-padding-y: 7px; } // accommodate for transparent buttons not having borders :host([scale="m"][appearance="transparent"]) button.content--slotted, @@ -684,11 +724,11 @@ :host([scale="l"]) { .button-padding { --calcite-internal-button-padding-x: theme("padding.4"); - --calcite-internal-button-padding-y-internal: 11px; + --calcite-internal-button-padding-y: 11px; } //shrink the padding if an icon is present to preserve the height .button-padding--shrunk { - --calcite-internal-button-padding-y-internal: 9px; + --calcite-internal-button-padding-y: 9px; } } @@ -696,7 +736,7 @@ :host([scale="s"]) button:not(.content--slotted), :host([scale="s"]) a:not(.content--slotted) { --calcite-internal-button-padding-x: theme("padding[0.5]"); - --calcite-internal-button-padding-y-internal: 3px; + --calcite-internal-button-padding-y: 3px; @apply text-0h w-6; min-block-size: theme("height.6"); } @@ -704,14 +744,14 @@ :host([scale="m"]) button:not(.content--slotted), :host([scale="m"]) a:not(.content--slotted) { --calcite-internal-button-padding-x: theme("padding[0.5]"); - --calcite-internal-button-padding-y-internal: 7px; + --calcite-internal-button-padding-y: 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-internal-button-padding-x: theme("padding[0.5]"); - --calcite-internal-button-padding-y-internal: 9px; + --calcite-internal-button-padding-y: 9px; @apply text-0h w-11; min-block-size: theme("height.11"); } @@ -728,7 +768,7 @@ // 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-internal-button-padding-y-internal: theme("padding[2.5]"); + --calcite-internal-button-padding-y: theme("padding[2.5]"); } // generate fab scales (scenario: 2 icons, ie., should not be square) 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 9999490f114..f23c14aa9d5 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 @@ -134,7 +134,7 @@ describe("calcite-color-picker", () => { const buttons = await findAll(page, `calcite-color-picker >>> .${CSS.container} calcite-button`); - expect(buttons).toHaveLength(2); + expect(buttons).toHaveLength(4); for (const button of buttons) { expect(await button.getProperty("type")).toBe("button"); diff --git a/packages/calcite-components/src/components/combobox/combobox.scss b/packages/calcite-components/src/components/combobox/combobox.scss index c47509a8c12..bda08cc4725 100644 --- a/packages/calcite-components/src/components/combobox/combobox.scss +++ b/packages/calcite-components/src/components/combobox/combobox.scss @@ -16,15 +16,13 @@ @apply relative block; } -@include disabled(); -@include x-button(); - :host([scale="s"]) { @apply text-n2; --calcite-combobox-item-spacing-unit-l: theme("spacing.2"); --calcite-combobox-item-spacing-unit-s: theme("spacing.1"); --calcite-combobox-input-height: theme("spacing.4"); --calcite-internal-combobox-input-margin-block: calc(theme("spacing.1") - theme("borderWidth.DEFAULT")); + --calcite-internal-close-size: 1rem; .x-button { margin-inline: theme("spacing.2"); @@ -37,6 +35,7 @@ --calcite-combobox-item-spacing-unit-s: theme("spacing.2"); --calcite-combobox-input-height: theme("spacing.4"); --calcite-internal-combobox-input-margin-block: calc(theme("spacing.2") - theme("borderWidth.DEFAULT")); + --calcite-internal-close-size: 1.5rem; .x-button { margin-inline-end: theme("spacing.3"); @@ -49,12 +48,17 @@ --calcite-combobox-item-spacing-unit-s: theme("spacing.3"); --calcite-combobox-input-height: theme("spacing.6"); --calcite-internal-combobox-input-margin-block: calc(theme("spacing[2.5]") - theme("borderWidth.DEFAULT")); + --calcite-internal-close-size: 2rem; .x-button { margin-inline-end: theme("spacing.4"); } } +.x-button { + align-self: center; +} + .wrapper { @apply focus-base flex border border-solid; padding-block: calc(var(--calcite-combobox-item-spacing-unit-s) / 4); @@ -232,6 +236,12 @@ calcite-chip { @apply block; } +@include disabled(); +@include x-button( + $background-color: "var(--calcite-close-background-color, var(--calcite-color-foreground-2))", + $background-color-hover: "var(--calcite-close-background-color-hover, var(--calcite-color-foreground-3))", + $color: "var(--calcite-close-icon-color, var(--calcite-color-text-3))" +); @include form-validation-message(); @include hidden-form-input(); @include base-component(); diff --git a/packages/calcite-components/src/components/combobox/combobox.tsx b/packages/calcite-components/src/components/combobox/combobox.tsx index c51d8481561..1d5a7d085e9 100644 --- a/packages/calcite-components/src/components/combobox/combobox.tsx +++ b/packages/calcite-components/src/components/combobox/combobox.tsx @@ -100,6 +100,8 @@ export class Combobox // #region Private Properties + private closeButtonEl = createRef(); + private allSelectedIndicatorChipEl: Chip["el"]; private chipContainerEl: HTMLDivElement; @@ -1778,6 +1780,7 @@ export class Combobox disabled={this.disabled} key="close-button" label={this.messages.clear} + ref={this.closeButtonEl} scale={this.scale} /> ) : null} diff --git a/packages/calcite-components/src/components/functional/XButton.tsx b/packages/calcite-components/src/components/functional/XButton.tsx index db5a38f4e1c..72a7d99d188 100644 --- a/packages/calcite-components/src/components/functional/XButton.tsx +++ b/packages/calcite-components/src/components/functional/XButton.tsx @@ -3,24 +3,45 @@ import { h, LuminaJsx } from "@arcgis/lumina"; import { Scale } from "../interfaces"; import { getIconScale } from "../../utils/component"; -export interface XButtonProps extends LuminaJsx.CustomAttributes { +export interface XButtonProps extends LuminaJsx.CustomAttributes { disabled: boolean; + focusable?: boolean; label: string; + round?: boolean; scale: Scale; + title?: string; onClick?: LuminaJsx.DOMAttributes["onClick"]; } export const CSS = { button: "x-button", + buttonRound: "x-button--round", }; -export const XButton = ({ disabled, key, label, scale }: XButtonProps): TemplateResult => ( +export const XButton = ({ + disabled, + focusable, + key, + label, + onClick, + ref, + round = true, + scale, + title, +}: XButtonProps): TemplateResult => ( + /> ) : null; } diff --git a/packages/calcite-components/src/components/tab/tab.e2e.ts b/packages/calcite-components/src/components/tab/tab.e2e.ts index 699077f480a..2b893fb355e 100644 --- a/packages/calcite-components/src/components/tab/tab.e2e.ts +++ b/packages/calcite-components/src/components/tab/tab.e2e.ts @@ -1,5 +1,6 @@ import { describe } from "vitest"; -import { defaults, renders, hidden } from "../../tests/commonTests"; +import { defaults, renders, hidden, themed } from "../../tests/commonTests"; +import { CSS } from "./resources"; describe("calcite-tab", () => { const tabHtml = "A tab"; @@ -21,4 +22,15 @@ describe("calcite-tab", () => { { propertyName: "scale", defaultValue: "m" }, ]); }); + + describe("theme", () => { + describe("default", () => { + themed("calcite-tab", { + "--calcite-tab-content-space-y": { + shadowSelector: `.${CSS.content}`, + targetProp: "paddingBlock", + }, + }); + }); + }); }); diff --git a/packages/calcite-components/src/components/tab/tab.scss b/packages/calcite-components/src/components/tab/tab.scss index 63f7d27fa60..035e457fe30 100644 --- a/packages/calcite-components/src/components/tab/tab.scss +++ b/packages/calcite-components/src/components/tab/tab.scss @@ -3,42 +3,51 @@ * * These properties can be overridden using the component's tag as selector. * - * @prop --calcite-tab-content-block-padding: Specifies the block padding of the component's content in the `default` slot. + * @prop --calcite-tab-content-space-y: Specifies the vertical space between the component's content in the `default` slot. + * @prop --calcite-tab-content-block-padding: [Deprecated] Use `--calcite-tab-content-space-y` instead. Specifies the block padding of the component's content in the `default` slot. */ -:host([selected]) { - section, - .container { - @apply block; - } -} - :host { @apply hidden h-full w-full; } :host([selected]) { @apply block h-full w-full overflow-auto; + + section, + .container { + @apply block; + } } .content { @apply box-border; - padding-block: var(--calcite-internal-tab-content-block-padding); + + padding-block: var( + --calcite-tab-content-space-y, + var(--calcite-tab-content-block-padding, var(--calcite-internal-tab-content-space-y)) + ); } .scale-s { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.1")); - @apply text-n2h; + --calcite-internal-tab-content-space-y: theme("spacing.1"); + + font-size: var(--calcite-font-size-sm); + line-height: 1rem; } .scale-m { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.2")); - @apply text-n1h; + --calcite-internal-tab-content-space-y: theme("spacing.2"); + + font-size: var(--calcite-font-size); + line-height: 1rem; } .scale-l { - --calcite-internal-tab-content-block-padding: var(--calcite-tab-content-block-padding, theme("spacing.[2.5]")); - @apply text-0h; + --calcite-internal-tab-content-space-y: theme("spacing.[2.5]"); + + font-size: var(--calcite-font-size-md); + line-height: 1.25rem; } section, diff --git a/packages/calcite-components/src/components/tabs/resources.ts b/packages/calcite-components/src/components/tabs/resources.ts index 325745e85ad..62f036fc6d2 100644 --- a/packages/calcite-components/src/components/tabs/resources.ts +++ b/packages/calcite-components/src/components/tabs/resources.ts @@ -1,3 +1,7 @@ +export const CSS = { + section: "section", +}; + export const SLOTS = { titleGroup: "title-group", }; diff --git a/packages/calcite-components/src/components/tabs/tabs.e2e.ts b/packages/calcite-components/src/components/tabs/tabs.e2e.ts index 7155a2a5ef7..cc162a7e9f7 100644 --- a/packages/calcite-components/src/components/tabs/tabs.e2e.ts +++ b/packages/calcite-components/src/components/tabs/tabs.e2e.ts @@ -2,12 +2,13 @@ import { E2EElement, E2EPage, EventSpy, newE2EPage } from "@arcgis/lumina-compiler/puppeteerTesting"; import { beforeEach, describe, expect, it } from "vitest"; import { html } from "../../../support/formatting"; -import { accessible, defaults, hidden, reflects, renders } from "../../tests/commonTests"; +import { accessible, defaults, hidden, reflects, renders, themed } from "../../tests/commonTests"; import { findAll, GlobalTestProps } from "../../tests/utils"; import { Scale } from "../interfaces"; -import { CSS as TabTitleCSS } from "../tab-title/resources"; +import { CSS as XButtonCSS } from "../functional/XButton"; import type { TabTitle } from "../tab-title/tab-title"; import type { TabNav } from "../tab-nav/tab-nav"; +import { CSS } from "./resources"; import { TabPosition } from "./interfaces"; import type { Tabs } from "./tabs"; @@ -345,7 +346,7 @@ describe("calcite-tabs", () => { }); it("should emit tab change events when closing affects selected tab", async () => { - await page.click(`#tab-title-4 >>> .${TabTitleCSS.closeButton}`); + await page.click(`#tab-title-4 >>> .${XButtonCSS.button}`); await page.waitForChanges(); expect(tabsActivateSpy).toHaveReceivedEventTimes(1); @@ -363,7 +364,7 @@ describe("calcite-tabs", () => { }); it("should NOT emit tab change events when closing does not affect selected tab", async () => { - await page.click(`#tab-title-1 >>> .${TabTitleCSS.closeButton}`); + await page.click(`#tab-title-1 >>> .${XButtonCSS.button}`); await page.waitForChanges(); expect(tabsActivateSpy).toHaveReceivedEventTimes(0); @@ -397,7 +398,7 @@ describe("calcite-tabs", () => { const tab2 = await page.find("#tab-title-2"); - await page.click(`#tab-title-1 >>> .${TabTitleCSS.closeButton}`); + await page.click(`#tab-title-1 >>> .${XButtonCSS.button}`); await tab2.click(); await page.waitForChanges(); @@ -409,11 +410,11 @@ describe("calcite-tabs", () => { describe("hiding/displaying X", () => { it("should hide x when tabs 2 to 4 closed and display x closable tab added", async () => { for (let i = 2; i <= 4; ++i) { - await page.click(`#tab-title-${i} >>> .${TabTitleCSS.closeButton}`); + await page.click(`#tab-title-${i} >>> .${XButtonCSS.button}`); } let tab1 = await page.find(`#tab-title-1`); expect(await tab1.getProperty("closable")).toBe(false); - expect(await page.find(`#tab-title-1 >>> .${TabTitleCSS.closeButton}`)).toBeNull(); + expect(await page.find(`#tab-title-1 >>> .${XButtonCSS.button}`)).toBeNull(); await page.evaluate(() => { document @@ -423,16 +424,16 @@ describe("calcite-tabs", () => { await page.waitForChanges(); tab1 = await page.find(`#tab-title-1`); expect(await tab1.getProperty("closable")).toBe(true); - expect(await page.find(`#tab-title-1 >>> .${TabTitleCSS.closeButton}`)).toBeDefined(); + expect(await page.find(`#tab-title-1 >>> .${XButtonCSS.button}`)).toBeDefined(); }); it("should hide x when tabs 1 to 3 closed and display x when closable tab added", async () => { for (let i = 1; i <= 3; ++i) { - await page.click(`#tab-title-${i} >>> .${TabTitleCSS.closeButton}`); + await page.click(`#tab-title-${i} >>> .${XButtonCSS.button}`); } let tab4 = await page.find(`#tab-title-4`); expect(await tab4.getProperty("closable")).toBe(false); - expect(await page.find(`#tab-title-4 >>> .${TabTitleCSS.closeButton}`)).toBeNull(); + expect(await page.find(`#tab-title-4 >>> .${XButtonCSS.button}`)).toBeNull(); await page.evaluate(() => { document @@ -442,7 +443,43 @@ describe("calcite-tabs", () => { await page.waitForChanges(); tab4 = await page.find(`#tab-title-4`); expect(await tab4.getProperty("closable")).toBe(true); - expect(await page.find(`#tab-title-4 >>> .${TabTitleCSS.closeButton}`)).toBeDefined(); + expect(await page.find(`#tab-title-4 >>> .${XButtonCSS.button}`)).toBeDefined(); + }); + }); + }); + + describe("theme", () => { + describe("default", () => { + themed("calcite-tabs", { + "--calcite-tab-border-color": { + shadowSelector: `.${CSS.section}`, + targetProp: "borderBlockStartColor", + }, + }); + }); + + describe("bordered", () => { + themed(html` `, { + "--calcite-tab-background-color": { + targetProp: "backgroundColor", + }, + "--calcite-tab-border-color": [ + { + targetProp: "boxShadow", + }, + { + shadowSelector: `.${CSS.section}`, + targetProp: "borderColor", + }, + ], + }); + + describe("bottom position", () => { + themed(html` `, { + "--calcite-tab-border-color": { + targetProp: "boxShadow", + }, + }); }); }); }); diff --git a/packages/calcite-components/src/components/tabs/tabs.scss b/packages/calcite-components/src/components/tabs/tabs.scss index 96bf67d971e..e9735aa2c90 100644 --- a/packages/calcite-components/src/components/tabs/tabs.scss +++ b/packages/calcite-components/src/components/tabs/tabs.scss @@ -1,26 +1,53 @@ +/** + * CSS Custom Properties + * + * These properties can be overridden using the component's tag as selector. + * + * @prop --calcite-tab-background-color: When `bordered`, specifies the component's background color. + * @prop --calcite-tab-border-color: Specifies the component's border color. + */ + :host { @apply flex flex-col; } :host([bordered]) { - box-shadow: inset 0 1px 0 var(--calcite-color-border-1); - background-color: var(--calcite-color-foreground-1); + box-shadow: inset 0 1px 0 var(--calcite-tab-border-color, var(--calcite-color-border-1)); + background-color: var(--calcite-tab-background-color, var(--calcite-color-foreground-1)); section { - @apply border-color-1 border border-solid; + border-color: var(--calcite-tab-border-color, var(--calcite-color-border-1)); + border-style: solid; } } +section { + @apply border flex flex-grow overflow-hidden; + + border-block-start-style: solid; + border-block-start-color: var(--calcite-tab-border-color, var(--calcite-color-border-1)); +} + :host([bordered][position="bottom"]) { box-shadow: - inset 0 1px 0 var(--calcite-color-border-1), - inset 0 -1px 0 var(--calcite-color-border-1); + inset 0 1px 0 var(--calcite-tab-border-color, var(--calcite-color-border-1)), + inset 0 -1px 0 var(--calcite-tab-border-color, var(--calcite-color-border-1)); } :host([bordered]:not([position="bottom"])) ::slotted(calcite-tab-nav) { margin-block-end: -1px; } +:host([position="bottom"]) { + @apply flex-col-reverse; + + section { + @apply flex-col-reverse + border-t-0 + border-b; + } +} + :host([bordered][scale="s"]) section { @apply p-3; } @@ -33,28 +60,9 @@ @apply p-4; } -:host([position="bottom"]) { - @apply flex-col-reverse; -} - -section { - @apply border-t-color-1 - flex - flex-grow - overflow-hidden - border-t; - border-block-start-style: solid; -} - -:host([position="bottom"]) section { - @apply border-b-color-1 - flex-col-reverse - border-t-0 - border-b; -} - :host([position="bottom"]:not([bordered])) section { border-block-end-style: solid; + border-block-end-color: var(--calcite-tab-border-color, var(--calcite-color-border-1)); } @media (forced-colors: active) { diff --git a/packages/calcite-components/src/components/tabs/tabs.tsx b/packages/calcite-components/src/components/tabs/tabs.tsx index 4015af9cbf7..d97ee6678e1 100644 --- a/packages/calcite-components/src/components/tabs/tabs.tsx +++ b/packages/calcite-components/src/components/tabs/tabs.tsx @@ -6,7 +6,7 @@ import { getSlotAssignedElements, slotChangeGetAssignedElements } from "../../ut import type { TabTitle } from "../tab-title/tab-title"; import type { Tab } from "../tab/tab"; import { TabLayout, TabPosition } from "./interfaces"; -import { SLOTS } from "./resources"; +import { CSS, SLOTS } from "./resources"; import { styles } from "./tabs.scss"; declare global { @@ -195,7 +195,7 @@ export class Tabs extends LitElement { return ( <> -
+
diff --git a/packages/calcite-components/src/custom-theme/tabs.ts b/packages/calcite-components/src/custom-theme/tabs.ts index 23d8022620b..be9c91704bb 100644 --- a/packages/calcite-components/src/custom-theme/tabs.ts +++ b/packages/calcite-components/src/custom-theme/tabs.ts @@ -1,9 +1,20 @@ import { html } from "../../support/formatting"; export const tabsTokens = { - // additional tokens will be added in https://github.com/Esri/calcite-design-system/pull/10532/ + calciteTabBackgroundColor: "", + calciteTabBackgroundColorHover: "", + calciteTabBorderColor: "", + calciteTabTextColor: "", + calciteTabAccentColor: "", + calciteTabAccentColorHover: "", + calciteTabAccentColorPress: "", calciteTabIconColorEnd: "", calciteTabIconColorStart: "", + calciteTabCloseIconColor: "", + calciteTabCloseIconColorPress: "", + calciteTabCloseBackgroundColor: "", + calciteTabCloseBackgroundPress: "", + calciteTabContentSpaceY: "", }; export const tabs = html` diff --git a/packages/calcite-components/src/demos/tabs.html b/packages/calcite-components/src/demos/tabs.html index f0b7c4afc04..1be32dd7bb7 100644 --- a/packages/calcite-components/src/demos/tabs.html +++ b/packages/calcite-components/src/demos/tabs.html @@ -46,36 +46,6 @@
large
- -
-
simple
- -
- - Tab 1 Title - Tab 2 Title - Tab 3 Title - -
- -
- - Tab 1 Title - Tab 2 Title - Tab 3 Title - -
- -
- - Tab 1 Title - Tab 2 Title - Tab 3 Title - Tab 4 Title - -
-
-
tab position: top (default)
@@ -596,6 +566,20 @@