diff --git a/.storybook/utils.tsx b/.storybook/utils.tsx
index e3a5db1d97e..3b6d0e9baa4 100644
--- a/.storybook/utils.tsx
+++ b/.storybook/utils.tsx
@@ -139,3 +139,78 @@ export const filterComponentAttributes = (
.filter((attr) => !exceptions.find((except) => except === attr.name))
.map((attr) => attr.commit());
};
+
+/*
+MIT License
+
+Copyright (c) 2020 Cloud Four
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+https://github.com/cloudfour/simple-svg-placeholder
+*/
+
+interface SimpleSvgPlaceholderParams {
+ width?: number;
+ height?: number;
+ text?: string;
+ fontFamily?: string;
+ fontWeight?: string;
+ fontSize?: number;
+ dy?: number;
+ bgColor?: string;
+ textColor?: string;
+ dataUri?: boolean;
+ charset?: string;
+}
+
+export function placeholderImage({
+ width = 300,
+ height = 150,
+ text = `${width}×${height}`,
+ fontFamily = "sans-serif",
+ fontWeight = "bold",
+ fontSize = Math.floor(Math.min(width, height) * 0.2),
+ dy = fontSize * 0.35,
+ bgColor = "#ddd",
+ textColor = "rgba(0,0,0,0.5)",
+ dataUri = true,
+ charset = "UTF-8"
+}: SimpleSvgPlaceholderParams): string {
+ const str = ``;
+
+ // Thanks to: filamentgroup/directory-encoder
+ const cleaned = str
+ .replace(/[\t\n\r]/gim, "") // Strip newlines and tabs
+ .replace(/\s\s+/g, " ") // Condense multiple spaces
+ .replace(/'/gim, "\\i"); // Normalize quotes
+
+ if (dataUri) {
+ const encoded = encodeURIComponent(cleaned)
+ .replace(/\(/g, "%28") // Encode brackets
+ .replace(/\)/g, "%29");
+
+ return `data:image/svg+xml;charset=${charset},${encoded}`;
+ }
+
+ return cleaned;
+}
diff --git a/src/assets/styles/includes.scss b/src/assets/styles/includes.scss
index 151624b7e1c..b927bbc5b9f 100644
--- a/src/assets/styles/includes.scss
+++ b/src/assets/styles/includes.scss
@@ -41,3 +41,18 @@
z-index: -1 !important;
}
}
+
+// mixin to provide base disabled styles for interactive components
+// additional styling can be passed via @content
+@mixin disabled() {
+ :host([disabled]) {
+ @apply opacity-disabled pointer-events-none cursor-default select-none;
+ @content;
+
+ ::slotted([calcite-hydrated][disabled]),
+ [calcite-hydrated][disabled] {
+ /* prevent opacity stacking */
+ @apply opacity-100;
+ }
+ }
+}
diff --git a/src/components/accordion/accordion.e2e.ts b/src/components/accordion/accordion.e2e.ts
index b35486a85a4..2131b38c23b 100644
--- a/src/components/accordion/accordion.e2e.ts
+++ b/src/components/accordion/accordion.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, renders } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-accordion", () => {
const accordionContent = `
diff --git a/src/components/accordion/accordion.stories.ts b/src/components/accordion/accordion.stories.ts
index 6591eac3d7c..b2989f75fa9 100644
--- a/src/components/accordion/accordion.stories.ts
+++ b/src/components/accordion/accordion.stories.ts
@@ -3,15 +3,15 @@ import {
Attributes,
filterComponentAttributes,
createComponentHTML as create,
- themesDarkDefault
+ themesDarkDefault,
+ placeholderImage
} from "../../../.storybook/utils";
-import { html } from "../../tests/utils";
import { ATTRIBUTES } from "../../../.storybook/resources";
import { iconNames } from "../../../.storybook/helpers";
import { select, text } from "@storybook/addon-knobs";
import accordionReadme from "./readme.md";
import accordionItemReadme from "../accordion-item/readme.md";
-import { placeholderImage } from "../../tests/utils";
+import { html } from "../../../support/formatting";
const createAccordionAttributes: (options?: { exceptions: string[] }) => Attributes = (
{ exceptions } = { exceptions: [] }
diff --git a/src/components/action-bar/action-bar.e2e.ts b/src/components/action-bar/action-bar.e2e.ts
index 42180d4cf73..d6afaf2d6d7 100755
--- a/src/components/action-bar/action-bar.e2e.ts
+++ b/src/components/action-bar/action-bar.e2e.ts
@@ -2,7 +2,7 @@ import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, focusable, hidden, reflects, renders, slots } from "../../tests/commonTests";
import { CSS, SLOTS } from "./resources";
import { overflowActionsDebounceInMs } from "./utils";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-action-bar", () => {
it("renders", async () => renders("calcite-action-bar", { display: "inline-flex" }));
diff --git a/src/components/action-bar/action-bar.stories.ts b/src/components/action-bar/action-bar.stories.ts
index a49835e0912..85b676af05a 100644
--- a/src/components/action-bar/action-bar.stories.ts
+++ b/src/components/action-bar/action-bar.stories.ts
@@ -8,7 +8,7 @@ import {
} from "../../../.storybook/utils";
import readme from "./readme.md";
import { ATTRIBUTES } from "../../../.storybook/resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { TEXT } from "./resources";
export default {
diff --git a/src/components/action-group/action-group.stories.ts b/src/components/action-group/action-group.stories.ts
index 37519a7dd83..2f095558ecb 100644
--- a/src/components/action-group/action-group.stories.ts
+++ b/src/components/action-group/action-group.stories.ts
@@ -1,7 +1,7 @@
import { select, text } from "@storybook/addon-knobs";
import { iconNames } from "../../../.storybook/helpers";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Action Group",
diff --git a/src/components/action-menu/action-menu.e2e.ts b/src/components/action-menu/action-menu.e2e.ts
index d14996c7810..cffdcdfe915 100755
--- a/src/components/action-menu/action-menu.e2e.ts
+++ b/src/components/action-menu/action-menu.e2e.ts
@@ -1,7 +1,7 @@
import { accessible, hidden, renders, defaults, reflects, focusable, slots } from "../../tests/commonTests";
import { newE2EPage } from "@stencil/core/testing";
import { SLOTS, CSS } from "./resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-action-menu", () => {
it("renders", async () => renders("calcite-action-menu", { display: "flex" }));
diff --git a/src/components/action-pad/action-pad.e2e.ts b/src/components/action-pad/action-pad.e2e.ts
index 34ff79c689b..458ee214486 100755
--- a/src/components/action-pad/action-pad.e2e.ts
+++ b/src/components/action-pad/action-pad.e2e.ts
@@ -1,7 +1,7 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, focusable, hidden, reflects, renders, slots } from "../../tests/commonTests";
import { CSS, SLOTS } from "./resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-action-pad", () => {
it("renders", async () => renders("calcite-action-pad", { display: "block" }));
diff --git a/src/components/action-pad/action-pad.stories.ts b/src/components/action-pad/action-pad.stories.ts
index 19e4b8d2f6f..43b74cc0b01 100644
--- a/src/components/action-pad/action-pad.stories.ts
+++ b/src/components/action-pad/action-pad.stories.ts
@@ -8,7 +8,7 @@ import {
} from "../../../.storybook/utils";
import readme from "./readme.md";
import { ATTRIBUTES } from "../../../.storybook/resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { TEXT } from "./resources";
export default {
diff --git a/src/components/action/action.e2e.ts b/src/components/action/action.e2e.ts
index ea079d7f110..7adb28be1ab 100755
--- a/src/components/action/action.e2e.ts
+++ b/src/components/action/action.e2e.ts
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
-import { accessible, hidden, renders } from "../../tests/commonTests";
+import { accessible, disabled, hidden, renders } from "../../tests/commonTests";
import { CSS } from "./resources";
describe("calcite-action", () => {
@@ -7,6 +7,8 @@ describe("calcite-action", () => {
it("honors hidden attribute", async () => hidden("calcite-action"));
+ it("can be disabled", () => disabled("calcite-action"));
+
it("should have visible text when text is enabled", async () => {
const page = await newE2EPage();
await page.setContent(``);
@@ -99,14 +101,6 @@ describe("calcite-action", () => {
expect(button.getAttribute("aria-label")).toBe("hi");
});
- it("should be disabled", async () => {
- const page = await newE2EPage();
- await page.setContent(``);
-
- const button = await page.find(`calcite-action >>> .${CSS.button}`);
- expect(button).toHaveAttribute("disabled");
- });
-
it("should have appearance=solid", async () => {
const page = await newE2EPage();
await page.setContent(``);
@@ -119,18 +113,4 @@ describe("calcite-action", () => {
await accessible(``);
await accessible(``);
});
-
- it("should not emit click event when disabled", async () => {
- const page = await newE2EPage();
-
- await page.setContent(``);
-
- const action = await page.find("calcite-action");
-
- const clickSpy = await action.spyOnEvent("click");
-
- await action.click();
-
- expect(clickSpy).toHaveReceivedEventTimes(0);
- });
});
diff --git a/src/components/action/action.scss b/src/components/action/action.scss
index ba8d46d74a2..307751a3859 100755
--- a/src/components/action/action.scss
+++ b/src/components/action/action.scss
@@ -3,9 +3,7 @@
@apply flex bg-transparent;
}
-:host([disabled]) {
- @apply pointer-events-none;
-}
+@include disabled();
.button {
@apply bg-foreground-1
diff --git a/src/components/action/action.stories.ts b/src/components/action/action.stories.ts
index 115cfb1112f..47cc5304429 100644
--- a/src/components/action/action.stories.ts
+++ b/src/components/action/action.stories.ts
@@ -6,7 +6,7 @@ import {
createComponentHTML as create
} from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { createSteps, iconNames, stepStory, setTheme, setKnobs } from "../../../.storybook/helpers";
import { ATTRIBUTES } from "../../../.storybook/resources";
const { alignment, scale } = ATTRIBUTES;
diff --git a/src/components/action/action.tsx b/src/components/action/action.tsx
index 13ef69cb1d1..0585f182cfc 100755
--- a/src/components/action/action.tsx
+++ b/src/components/action/action.tsx
@@ -16,6 +16,7 @@ import { Alignment, Appearance, Scale } from "../interfaces";
import { CSS, TEXT } from "./resources";
import { createObserver } from "../../utils/observers";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding a `calcite-icon`.
@@ -25,7 +26,7 @@ import { createObserver } from "../../utils/observers";
styleUrl: "action.scss",
shadow: true
})
-export class Action {
+export class Action implements InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -133,6 +134,10 @@ export class Action {
this.mutationObserver?.disconnect();
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Methods
diff --git a/src/components/alert/alert.e2e.ts b/src/components/alert/alert.e2e.ts
index 2c0e927afd6..beeb82751c9 100644
--- a/src/components/alert/alert.e2e.ts
+++ b/src/components/alert/alert.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { renders, accessible, HYDRATED_ATTR } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-alert", () => {
const alertContent = `
diff --git a/src/components/alert/alert.stories.ts b/src/components/alert/alert.stories.ts
index 48f813ca736..a3d2af78423 100644
--- a/src/components/alert/alert.stories.ts
+++ b/src/components/alert/alert.stories.ts
@@ -2,7 +2,7 @@ import { select } from "@storybook/addon-knobs";
import { boolean, iconNames } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Alert",
diff --git a/src/components/avatar/avatar.stories.ts b/src/components/avatar/avatar.stories.ts
index 9b0e02b05e0..c28db3b59db 100644
--- a/src/components/avatar/avatar.stories.ts
+++ b/src/components/avatar/avatar.stories.ts
@@ -1,7 +1,7 @@
import { select, text } from "@storybook/addon-knobs";
-import { themesDarkDefault } from "../../../.storybook/utils";
-import { html, placeholderImage } from "../../tests/utils";
+import { placeholderImage, themesDarkDefault } from "../../../.storybook/utils";
+import { html } from "../../../support/formatting";
import readme from "./readme.md";
export default {
diff --git a/src/components/block/block.e2e.ts b/src/components/block/block.e2e.ts
index 921801be993..26e9301f4a7 100644
--- a/src/components/block/block.e2e.ts
+++ b/src/components/block/block.e2e.ts
@@ -1,7 +1,7 @@
import { newE2EPage } from "@stencil/core/testing";
import { CSS, SLOTS, TEXT } from "./resources";
-import { accessible, defaults, hidden, renders, slots } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { accessible, defaults, disabled, hidden, renders, slots } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
describe("calcite-block", () => {
it("renders", async () => renders("calcite-block", { display: "flex" }));
@@ -43,42 +43,8 @@ describe("calcite-block", () => {
`));
- it("can be disabled", async () => {
- const page = await newE2EPage({
- html: `
-
- content
-
- `
- });
-
- const content = await page.find(".content");
- const clickSpy = await content.spyOnEvent("click");
- await content.click();
- expect(clickSpy).toHaveReceivedEventTimes(1);
-
- const block = await page.find("calcite-block");
- block.setProperty("disabled", true);
- await page.waitForChanges();
-
- // `tabindex=-1` on host removes children from the tab order
- expect(block.getAttribute("tabindex")).toBe("-1");
-
- await content.click();
- expect(clickSpy).toHaveReceivedEventTimes(1);
-
- const header = await page.find(`calcite-block >>> .${CSS.headerContainer}`);
- const toggleSpy = await block.spyOnEvent("calciteBlockToggle");
-
- await header.click();
- await header.click();
- expect(toggleSpy).toHaveReceivedEventTimes(0);
-
- block.setAttribute("disabled", false);
- await page.waitForChanges();
-
- expect(block.getAttribute("tabindex")).toBeNull();
- });
+ it("can be disabled", () =>
+ disabled(html``));
it("has a loading state", async () => {
const page = await newE2EPage({
diff --git a/src/components/block/block.scss b/src/components/block/block.scss
index 6f6b1e25b85..12b514cba1d 100644
--- a/src/components/block/block.scss
+++ b/src/components/block/block.scss
@@ -17,6 +17,8 @@
flex-basis: auto;
}
+@include disabled();
+
@import "../../assets/styles/header";
.header {
@@ -161,14 +163,3 @@ calcite-action-menu {
@apply text-color-1;
}
}
-
-:host([disabled]) {
- pointer-events: none;
- user-select: none;
-
- @apply pointer-events-none select-none;
-
- .header-container {
- @apply opacity-50;
- }
-}
diff --git a/src/components/block/block.stories.ts b/src/components/block/block.stories.ts
index 27456e2edf4..3ebad57bb93 100644
--- a/src/components/block/block.stories.ts
+++ b/src/components/block/block.stories.ts
@@ -3,11 +3,12 @@ import {
Attribute,
filterComponentAttributes,
Attributes,
- createComponentHTML as create
+ createComponentHTML as create,
+ placeholderImage
} from "../../../.storybook/utils";
import blockReadme from "./readme.md";
import sectionReadme from "../block-section/readme.md";
-import { html, placeholderImage } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Block",
@@ -167,3 +168,9 @@ export const withHeaderControl = (): string =>
export const withIconAndHeader = (): string =>
create("calcite-block", createBlockAttributes({ exceptions: ["open", "collapsible"] }), `
✅
`);
+
+export const disabled = (): string => html`
+
+
+
+`;
diff --git a/src/components/block/block.tsx b/src/components/block/block.tsx
index 280a42a1a8d..b8b452d2588 100644
--- a/src/components/block/block.tsx
+++ b/src/components/block/block.tsx
@@ -8,6 +8,7 @@ import {
connectConditionalSlotComponent,
disconnectConditionalSlotComponent
} from "../../utils/conditionalSlot";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
import { guid } from "../../utils/guid";
/**
@@ -21,7 +22,7 @@ import { guid } from "../../utils/guid";
styleUrl: "block.scss",
shadow: true
})
-export class Block implements ConditionalSlotComponent {
+export class Block implements ConditionalSlotComponent, InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -93,6 +94,16 @@ export class Block implements ConditionalSlotComponent {
*/
@Prop() summary: string;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Private Properties
@@ -146,11 +157,10 @@ export class Block implements ConditionalSlotComponent {
// --------------------------------------------------------------------------
renderScrim(): VNode[] {
- const { disabled, loading } = this;
-
+ const { loading } = this;
const defaultSlot = ;
- return [loading || disabled ? : null, defaultSlot];
+ return [loading ? : null, defaultSlot];
}
renderIcon(): VNode[] {
@@ -193,8 +203,7 @@ export class Block implements ConditionalSlotComponent {
}
render(): VNode {
- const { collapsible, disabled, el, intlCollapse, intlExpand, loading, open, intlLoading } =
- this;
+ const { collapsible, el, intlCollapse, intlExpand, loading, open, intlLoading } = this;
const toggleLabel = open ? intlCollapse || TEXT.collapse : intlExpand || TEXT.expand;
@@ -255,7 +264,7 @@ export class Block implements ConditionalSlotComponent {
);
return (
-
+
{
it("renders as a button with default props", async () => {
@@ -44,6 +45,8 @@ describe("calcite-button", () => {
it("is labelable", async () => labelable("calcite-button"));
+ it("can be disabled", () => disabled("calcite-button"));
+
it("should update childElType when href changes", async () => {
const page = await newE2EPage({ html: `Continue` });
const link = await page.find("calcite-button");
diff --git a/src/components/button/button.scss b/src/components/button/button.scss
index a522616c3bf..0aded600fb8 100644
--- a/src/components/button/button.scss
+++ b/src/components/button/button.scss
@@ -159,9 +159,9 @@
line-height: inherit;
}
-// disabled styles
-:host([loading]),
-:host([disabled]) {
+@include disabled();
+
+:host([loading]) {
@apply pointer-events-none;
button,
a {
diff --git a/src/components/button/button.stories.ts b/src/components/button/button.stories.ts
index e5fb9890540..631cd82707c 100644
--- a/src/components/button/button.stories.ts
+++ b/src/components/button/button.stories.ts
@@ -1,7 +1,7 @@
import { text, select } from "@storybook/addon-knobs";
import { iconNames, boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import readme from "./readme.md";
export default {
@@ -175,3 +175,5 @@ export const RTL = (): string => html`
${text("text", "button text here")}
`;
+
+export const disabled = (): string => html`disabled`;
diff --git a/src/components/button/button.tsx b/src/components/button/button.tsx
index 58f5641314c..086fe734554 100644
--- a/src/components/button/button.tsx
+++ b/src/components/button/button.tsx
@@ -6,6 +6,7 @@ import { ButtonAlignment, ButtonAppearance, ButtonColor } from "./interfaces";
import { FlipContext, Scale, Width } from "../interfaces";
import { LabelableComponent, connectLabel, disconnectLabel, getLabelText } from "../../utils/label";
import { createObserver } from "../../utils/observers";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/** Passing a 'href' will render an anchor link, instead of a button. Role will be set to link, or button, depending on this. */
/** It is the consumers responsibility to add aria information, rel, target, for links, and any button attributes for form submission */
@@ -16,7 +17,7 @@ import { createObserver } from "../../utils/observers";
styleUrl: "button.scss",
shadow: true
})
-export class Button implements LabelableComponent {
+export class Button implements LabelableComponent, InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -140,6 +141,10 @@ export class Button implements LabelableComponent {
}
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
render(): VNode {
const childElType = this.href ? "a" : "button";
const Tag = childElType;
diff --git a/src/components/card/card.e2e.ts b/src/components/card/card.e2e.ts
index 87d58f7ad58..a8caa6ee4df 100644
--- a/src/components/card/card.e2e.ts
+++ b/src/components/card/card.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, renders, slots } from "../../tests/commonTests";
-import { placeholderImage } from "../../tests/utils";
+import { placeholderImage } from "../../../.storybook/utils";
import { CSS, SLOTS } from "./resources";
const placeholder = placeholderImage({
width: 350,
diff --git a/src/components/card/card.stories.ts b/src/components/card/card.stories.ts
index 3180f57db04..0122b3b6613 100644
--- a/src/components/card/card.stories.ts
+++ b/src/components/card/card.stories.ts
@@ -1,6 +1,6 @@
import { boolean } from "../../../.storybook/helpers";
-import { themesDarkDefault } from "../../../.storybook/utils";
-import { html, placeholderImage } from "../../tests/utils";
+import { placeholderImage, themesDarkDefault } from "../../../.storybook/utils";
+import { html } from "../../../support/formatting";
import readme from "./readme.md";
export default {
diff --git a/src/components/checkbox/checkbox.e2e.ts b/src/components/checkbox/checkbox.e2e.ts
index 3e9e280f07d..14bf4ce2b01 100644
--- a/src/components/checkbox/checkbox.e2e.ts
+++ b/src/components/checkbox/checkbox.e2e.ts
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
-import { accessible, focusable, formAssociated, HYDRATED_ATTR, labelable } from "../../tests/commonTests";
+import { accessible, disabled, focusable, formAssociated, HYDRATED_ATTR, labelable } from "../../tests/commonTests";
describe("calcite-checkbox", () => {
it("is accessible", async () =>
@@ -15,6 +15,8 @@ describe("calcite-checkbox", () => {
it("is form-associated", async () => formAssociated("calcite-checkbox", { testValue: true }));
+ it("can be disabled", () => disabled("calcite-checkbox"));
+
it("renders with correct default attributes", async () => {
const page = await newE2EPage();
await page.setContent("");
@@ -70,21 +72,6 @@ describe("calcite-checkbox", () => {
expect(spy).toHaveReceivedEventTimes(0);
});
- it("does not toggle when clicked if disabled", async () => {
- const page = await newE2EPage();
- await page.setContent("");
-
- const calciteCheckbox = await page.find("calcite-checkbox");
-
- expect(calciteCheckbox).not.toHaveAttribute("checked");
-
- await calciteCheckbox.click();
-
- await page.waitForChanges();
-
- expect(calciteCheckbox).not.toHaveAttribute("checked");
- });
-
it("removes the indeterminate attribute when clicked", async () => {
const page = await newE2EPage();
await page.setContent("");
diff --git a/src/components/checkbox/checkbox.scss b/src/components/checkbox/checkbox.scss
index ea7833fc285..e30450c1c84 100644
--- a/src/components/checkbox/checkbox.scss
+++ b/src/components/checkbox/checkbox.scss
@@ -59,8 +59,6 @@
@include focus-box-shadow(inset 0 0 0 1px var(--calcite-ui-brand));
}
}
-:host([disabled]) {
- @apply opacity-disabled pointer-events-none cursor-default;
-}
+@include disabled();
@include hidden-form-input();
diff --git a/src/components/checkbox/checkbox.stories.ts b/src/components/checkbox/checkbox.stories.ts
index 89b18f8c33b..9da39d7cd45 100644
--- a/src/components/checkbox/checkbox.stories.ts
+++ b/src/components/checkbox/checkbox.stories.ts
@@ -1,7 +1,7 @@
import { select, text } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import readme from "./readme.md";
export default {
@@ -49,3 +49,5 @@ export const RTL = (): string => html`
${text("label", "Checkbox")}
`;
+
+export const disabled = (): string => html``;
diff --git a/src/components/checkbox/checkbox.tsx b/src/components/checkbox/checkbox.tsx
index 0c944356ad7..bce3b27fffe 100644
--- a/src/components/checkbox/checkbox.tsx
+++ b/src/components/checkbox/checkbox.tsx
@@ -14,13 +14,14 @@ import { Scale } from "../interfaces";
import { CheckableFormCompoment, HiddenFormInputSlot } from "../../utils/form";
import { LabelableComponent, connectLabel, disconnectLabel, getLabelText } from "../../utils/label";
import { connectForm, disconnectForm } from "../../utils/form";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
@Component({
tag: "calcite-checkbox",
styleUrl: "checkbox.scss",
shadow: true
})
-export class Checkbox implements LabelableComponent, CheckableFormCompoment {
+export class Checkbox implements LabelableComponent, CheckableFormCompoment, InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -196,6 +197,10 @@ export class Checkbox implements LabelableComponent, CheckableFormCompoment {
disconnectForm(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Render Methods
diff --git a/src/components/chip/chip.stories.ts b/src/components/chip/chip.stories.ts
index a8174cae770..12932041810 100644
--- a/src/components/chip/chip.stories.ts
+++ b/src/components/chip/chip.stories.ts
@@ -1,8 +1,8 @@
import { select } from "@storybook/addon-knobs";
import { iconNames, boolean } from "../../../.storybook/helpers";
-import { themesDarkDefault } from "../../../.storybook/utils";
+import { placeholderImage, themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html, placeholderImage } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Chip",
diff --git a/src/components/color-picker/color-picker.e2e.ts b/src/components/color-picker/color-picker.e2e.ts
index 67822ec6d4c..f2e985935fa 100644
--- a/src/components/color-picker/color-picker.e2e.ts
+++ b/src/components/color-picker/color-picker.e2e.ts
@@ -1,4 +1,4 @@
-import { accessible, defaults, hidden, reflects, renders, focusable } from "../../tests/commonTests";
+import { accessible, defaults, hidden, reflects, renders, focusable, disabled } from "../../tests/commonTests";
import { CSS, DEFAULT_COLOR, DEFAULT_STORAGE_KEY_PREFIX, DIMENSIONS, TEXT } from "./resources";
import { E2EElement, E2EPage, EventSpy, newE2EPage } from "@stencil/core/testing";
@@ -163,6 +163,9 @@ describe("calcite-color-picker", () => {
}
]));
+ // #408047 is a color in the middle of the color field
+ it("can be disabled", () => disabled(""));
+
it(`should set all internal calcite-button types to 'button'`, async () => {
const page = await newE2EPage({
html: ""
diff --git a/src/components/color-picker/color-picker.scss b/src/components/color-picker/color-picker.scss
index 6cf61b953f9..9e52ec06ba2 100644
--- a/src/components/color-picker/color-picker.scss
+++ b/src/components/color-picker/color-picker.scss
@@ -8,6 +8,8 @@ $gap--large: 12px;
@apply text-n2h inline-block font-normal;
}
+@include disabled();
+
:host([scale="s"]) {
.container {
width: 160px;
diff --git a/src/components/color-picker/color-picker.stories.ts b/src/components/color-picker/color-picker.stories.ts
index 6fee334519d..dccfe46b36a 100644
--- a/src/components/color-picker/color-picker.stories.ts
+++ b/src/components/color-picker/color-picker.stories.ts
@@ -8,6 +8,7 @@ import {
} from "../../../.storybook/utils";
import colorReadme from "./readme.md";
import { ATTRIBUTES } from "../../../.storybook/resources";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/ColorPicker",
@@ -97,3 +98,5 @@ export const AllowingEmpty = (): string =>
{ name: "allow-empty", value: true },
{ name: "value", value: text("value", "") }
]);
+
+export const disabled = (): string => html``;
diff --git a/src/components/color-picker/color-picker.tsx b/src/components/color-picker/color-picker.tsx
index 00de55eb8bf..d9422927715 100644
--- a/src/components/color-picker/color-picker.tsx
+++ b/src/components/color-picker/color-picker.tsx
@@ -29,6 +29,7 @@ import { colorEqual, CSSColorMode, Format, normalizeHex, parseMode, SupportedMod
import { throttle } from "lodash-es";
import { clamp } from "../../utils/math";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
const throttleFor60FpsInMs = 16;
const defaultValue = normalizeHex(DEFAULT_COLOR.hex());
@@ -39,7 +40,7 @@ const defaultFormat = "auto";
styleUrl: "color-picker.scss",
shadow: true
})
-export class ColorPicker {
+export class ColorPicker implements InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -85,6 +86,11 @@ export class ColorPicker {
this.value = this.toValue(color);
}
+ /**
+ * When true, disabled prevents user interaction.
+ */
+ @Prop({ reflect: true }) disabled = false;
+
/**
* The format of the value property.
*
@@ -296,6 +302,8 @@ export class ColorPicker {
private hueThumbState: "idle" | "hover" | "drag" = "idle";
+ private hueScopeNode: HTMLDivElement;
+
private internalColorUpdateContext: "internal" | "initial" | null = null;
private previousColor: InternalColor | null;
@@ -516,9 +524,11 @@ export class ColorPicker {
if (region === "color-field") {
this.hueThumbState = "drag";
this.captureColorFieldColor(offsetX, offsetY);
+ this.colorFieldScopeNode.focus();
} else if (region === "slider") {
this.sliderThumbState = "drag";
this.captureHueSliderColor(offsetX);
+ this.hueScopeNode.focus();
}
// prevent text selection outside of color field & slider area
@@ -717,6 +727,10 @@ export class ColorPicker {
document.removeEventListener("mouseup", this.globalMouseUpHandler);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
//--------------------------------------------------------------------------
//
// Render Methods
@@ -788,6 +802,7 @@ export class ColorPicker {
aria-valuenow={color?.round().hue() || DEFAULT_COLOR.round().hue()}
class={{ [CSS.scope]: true, [CSS.hueScope]: true }}
onKeyDown={this.handleHueScopeKeyDown}
+ ref={this.storeHueScope}
role="slider"
style={{ top: `${hueTop}px`, left: `${hueLeft}px` }}
tabindex="0"
@@ -894,6 +909,10 @@ export class ColorPicker {
this.colorFieldScopeNode = node;
};
+ private storeHueScope = (node: HTMLDivElement): void => {
+ this.hueScopeNode = node;
+ };
+
private renderChannelsTabTitle = (channelMode: this["channelMode"]): VNode => {
const { channelMode: activeChannelMode, intlRgb, intlHsv } = this;
const active = channelMode === activeChannelMode;
diff --git a/src/components/combobox-item/combobox-item.e2e.ts b/src/components/combobox-item/combobox-item.e2e.ts
index ca9dafd950a..10bcbcbe025 100644
--- a/src/components/combobox-item/combobox-item.e2e.ts
+++ b/src/components/combobox-item/combobox-item.e2e.ts
@@ -1,4 +1,4 @@
-import { hidden, renders, slots } from "../../tests/commonTests";
+import { disabled, hidden, renders, slots } from "../../tests/commonTests";
describe("calcite-combobox-item", () => {
it("renders", async () => renders("calcite-combobox-item", { display: "flex" }));
@@ -6,4 +6,6 @@ describe("calcite-combobox-item", () => {
it("honors hidden attribute", async () => hidden("calcite-combobox-item"));
it("has slots", () => slots("calcite-combobox-item", [], true));
+
+ it("can be disabled", () => disabled("calcite-combobox-item", { focusTarget: "none" }));
});
diff --git a/src/components/combobox-item/combobox-item.scss b/src/components/combobox-item/combobox-item.scss
index 920b602b3e0..3e5e8795f99 100644
--- a/src/components/combobox-item/combobox-item.scss
+++ b/src/components/combobox-item/combobox-item.scss
@@ -29,6 +29,8 @@
@apply shadow-none;
}
+@include disabled();
+
:host,
ul {
@apply m-0 flex flex-col p-0 outline-none;
diff --git a/src/components/combobox-item/combobox-item.tsx b/src/components/combobox-item/combobox-item.tsx
index a057f98c607..986391f7176 100644
--- a/src/components/combobox-item/combobox-item.tsx
+++ b/src/components/combobox-item/combobox-item.tsx
@@ -21,6 +21,7 @@ import {
disconnectConditionalSlotComponent,
ConditionalSlotComponent
} from "../../utils/conditionalSlot";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding nested `calcite-combobox-item`s.
@@ -30,7 +31,7 @@ import {
styleUrl: "combobox-item.scss",
shadow: true
})
-export class ComboboxItem implements ConditionalSlotComponent {
+export class ComboboxItem implements ConditionalSlotComponent, InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -97,6 +98,10 @@ export class ComboboxItem implements ConditionalSlotComponent {
disconnectConditionalSlotComponent(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Events
diff --git a/src/components/combobox/combobox.e2e.ts b/src/components/combobox/combobox.e2e.ts
index f260a04d449..ca687fe0ec0 100644
--- a/src/components/combobox/combobox.e2e.ts
+++ b/src/components/combobox/combobox.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
-import { renders, hidden, accessible, defaults, labelable, formAssociated } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { renders, hidden, accessible, defaults, labelable, formAssociated, disabled } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
import { TEXT } from "./resources";
describe("calcite-combobox", () => {
@@ -45,6 +45,8 @@ describe("calcite-combobox", () => {
it("is labelable", async () => labelable("calcite-combobox"));
+ it("can be disabled", () => disabled("calcite-combobox"));
+
it("should show the listbox when it receives focus", async () => {
const page = await newE2EPage();
await page.setContent(`
diff --git a/src/components/combobox/combobox.scss b/src/components/combobox/combobox.scss
index 00ddc60133e..a76422e6c83 100644
--- a/src/components/combobox/combobox.scss
+++ b/src/components/combobox/combobox.scss
@@ -10,9 +10,7 @@
@apply relative block;
}
-:host([disabled]) {
- @apply pointer-events-none select-none opacity-50;
-}
+@include disabled();
:host([scale="s"]) {
@apply text-n2;
diff --git a/src/components/combobox/combobox.stories.ts b/src/components/combobox/combobox.stories.ts
index 00c7964cbe8..7cbb0b673fe 100644
--- a/src/components/combobox/combobox.stories.ts
+++ b/src/components/combobox/combobox.stories.ts
@@ -4,7 +4,7 @@ import { boolean, createSteps, stepStory } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme1 from "./readme.md";
import readme2 from "../combobox-item/readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/Combobox",
@@ -256,3 +256,16 @@ export const FlipPositioning = stepStory(
FlipPositioning.parameters = {
layout: "fullscreen"
};
+
+export const disabled = (): string => html`
+
+
+
+
+
+
+
+
+
+
+`;
diff --git a/src/components/combobox/combobox.tsx b/src/components/combobox/combobox.tsx
index 498c435f343..3e1f5b1e60c 100644
--- a/src/components/combobox/combobox.tsx
+++ b/src/components/combobox/combobox.tsx
@@ -42,6 +42,7 @@ import {
HiddenFormInputSlot
} from "../../utils/form";
import { createObserver } from "../../utils/observers";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
interface ItemData {
label: string;
value: string;
@@ -64,7 +65,7 @@ const inputUidPrefix = "combobox-input-";
styleUrl: "combobox.scss",
shadow: true
})
-export class Combobox implements LabelableComponent, FormComponent {
+export class Combobox implements LabelableComponent, FormComponent, InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -83,6 +84,11 @@ export class Combobox implements LabelableComponent, FormComponent {
@Watch("active")
activeHandler(newValue: boolean, oldValue: boolean): void {
+ if (this.disabled) {
+ this.active = false;
+ return;
+ }
+
// when closing, wait transition time then hide to prevent overscroll
if (oldValue && !newValue) {
this.el.addEventListener("calciteComboboxClose", this.toggleCloseEnd);
@@ -97,6 +103,13 @@ export class Combobox implements LabelableComponent, FormComponent {
/** Disable combobox input */
@Prop({ reflect: true }) disabled = false;
+ @Watch("disabled")
+ handleDisabledChange(value: boolean): void {
+ if (!value) {
+ this.active = false;
+ }
+ }
+
/** Aria label for combobox (required) */
@Prop() label!: string;
@@ -278,6 +291,8 @@ export class Combobox implements LabelableComponent, FormComponent {
this.reposition();
this.inputHeight = this.el.offsetHeight;
}
+
+ updateHostInteraction(this);
}
disconnectedCallback(): void {
diff --git a/src/components/date-picker-day/date-picker-day.e2e.ts b/src/components/date-picker-day/date-picker-day.e2e.ts
new file mode 100644
index 00000000000..19655d068ee
--- /dev/null
+++ b/src/components/date-picker-day/date-picker-day.e2e.ts
@@ -0,0 +1,18 @@
+import { disabled } from "../../tests/commonTests";
+import { newProgrammaticE2EPage } from "../../tests/utils";
+
+describe("calcite-date-picker-day", () => {
+ it("can be disabled", async () => {
+ const page = await newProgrammaticE2EPage();
+ await page.evaluate(() => {
+ const dateEl = document.createElement("calcite-date-picker-day") as HTMLCalciteDatePickerDayElement;
+ dateEl.active = true;
+ dateEl.day = 3;
+ dateEl.localeData = { numerals: "0123456789" } as HTMLCalciteDatePickerDayElement["localeData"];
+ document.body.append(dateEl);
+ });
+ await page.waitForChanges();
+
+ return disabled({ tag: "calcite-date-picker-day", page });
+ });
+});
diff --git a/src/components/date-picker-day/date-picker-day.scss b/src/components/date-picker-day/date-picker-day.scss
index dcdd35e4789..beb1a955362 100644
--- a/src/components/date-picker-day/date-picker-day.scss
+++ b/src/components/date-picker-day/date-picker-day.scss
@@ -7,6 +7,8 @@
width: calc(100% / 7);
}
+@include disabled();
+
.day-v-wrapper {
@apply flex-auto;
}
@@ -81,11 +83,6 @@
@apply opacity-100;
}
-:host([disabled]) {
- cursor: default;
- @apply opacity-25;
-}
-
:host(:hover:not([disabled])),
:host([active]:not([range])) {
& .day {
diff --git a/src/components/date-picker-day/date-picker-day.tsx b/src/components/date-picker-day/date-picker-day.tsx
index 4d12ff8d431..a8550d45bc1 100644
--- a/src/components/date-picker-day/date-picker-day.tsx
+++ b/src/components/date-picker-day/date-picker-day.tsx
@@ -14,13 +14,14 @@ import { getElementDir } from "../../utils/dom";
import { DateLocaleData } from "../date-picker/utils";
import { Scale } from "../interfaces";
import { CSS_UTILITY } from "../../utils/resources";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
@Component({
tag: "calcite-date-picker-day",
styleUrl: "date-picker-day.scss",
shadow: true
})
-export class DatePickerDay {
+export class DatePickerDay implements InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -128,12 +129,7 @@ export class DatePickerDay {
.join("");
const dir = getElementDir(this.el);
return (
-
+
@@ -144,4 +140,12 @@ export class DatePickerDay {
);
}
+
+ componentDidRender(): void {
+ updateHostInteraction(this, this.isTabbable);
+ }
+
+ isTabbable(): boolean {
+ return this.active;
+ }
}
diff --git a/src/components/date-picker-month-header/date-picker-month-header.e2e.ts b/src/components/date-picker-month-header/date-picker-month-header.e2e.ts
index 05f29bae488..09644dca685 100644
--- a/src/components/date-picker-month-header/date-picker-month-header.e2e.ts
+++ b/src/components/date-picker-month-header/date-picker-month-header.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { renders } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-date-picker-month-header", () => {
it("renders", async () => renders("calcite-date-picker-month-header", { display: "block" }));
diff --git a/src/components/date-picker/date-picker.e2e.ts b/src/components/date-picker/date-picker.e2e.ts
index 8c550b50fb4..a19e397a60b 100644
--- a/src/components/date-picker/date-picker.e2e.ts
+++ b/src/components/date-picker/date-picker.e2e.ts
@@ -1,7 +1,7 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
import { renders, defaults, hidden } from "../../tests/commonTests";
import { TEXT } from "./resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-date-picker", () => {
it("renders", async () => renders("calcite-date-picker", { display: "inline-block" }));
diff --git a/src/components/date-picker/date-picker.stories.ts b/src/components/date-picker/date-picker.stories.ts
index 0765ca5b6ed..e5ec9f25744 100644
--- a/src/components/date-picker/date-picker.stories.ts
+++ b/src/components/date-picker/date-picker.stories.ts
@@ -8,7 +8,7 @@ import {
themesDarkDefault
} from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { locales } from "../../utils/locale";
import { createSteps, setKnobs, setTheme, stepStory } from "../../../.storybook/helpers";
import { ATTRIBUTES } from "../../../.storybook/resources";
diff --git a/src/components/dropdown/dropdown.e2e.ts b/src/components/dropdown/dropdown.e2e.ts
index 050265d652a..91179911f98 100644
--- a/src/components/dropdown/dropdown.e2e.ts
+++ b/src/components/dropdown/dropdown.e2e.ts
@@ -1,7 +1,7 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
-import { accessible, defaults, renders } from "../../tests/commonTests";
+import { accessible, defaults, disabled, renders } from "../../tests/commonTests";
import dedent from "dedent";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-dropdown", () => {
it("renders", () =>
@@ -24,6 +24,20 @@ describe("calcite-dropdown", () => {
defaultValue: "absolute"
}
]));
+
+ it("can be disabled", () =>
+ disabled(
+ html`
+ Open dropdown
+
+ Dropdown Item Content
+ Dropdown Item Content
+ Dropdown Item Content
+
+ `,
+ { focusTarget: "child" }
+ ));
+
/**
* Test helper for selected calcite-dropdown items. Expects items to have IDs to test against.
*/
@@ -796,28 +810,6 @@ describe("calcite-dropdown", () => {
expect(await page.evaluate(() => document.activeElement.id)).toEqual("trigger");
});
- it("when disabled, clicks on slotted dropdown trigger do not open dropdown", async () => {
- const page = await newE2EPage();
- await page.setContent(html`
-
- Open dropdown
-
- Dropdown Item Content
- Dropdown Item Content
- Dropdown Item Content
-
-
- `);
-
- const element = await page.find("calcite-dropdown");
- const trigger = await element.find("#trigger");
- const dropdownWrapper = await page.find("calcite-dropdown >>> .calcite-dropdown-wrapper");
- expect(await dropdownWrapper.isVisible()).toBe(false);
- await trigger.click();
- await page.waitForChanges();
- expect(await dropdownWrapper.isVisible()).toBe(false);
- });
-
it("accepts multiple triggers", async () => {
const page = await newE2EPage();
await page.setContent(html`
diff --git a/src/components/dropdown/dropdown.scss b/src/components/dropdown/dropdown.scss
index 3249edb7c31..33a3acd13d1 100644
--- a/src/components/dropdown/dropdown.scss
+++ b/src/components/dropdown/dropdown.scss
@@ -10,10 +10,8 @@
@apply inline-flex flex-initial;
}
-// disabled styles
-:host([disabled]) {
- @apply opacity-disabled pointer-events-none;
-}
+@include disabled();
+
:host .calcite-dropdown-wrapper {
@include popperContainer();
@include popperWrapper();
diff --git a/src/components/dropdown/dropdown.stories.ts b/src/components/dropdown/dropdown.stories.ts
index 8d6fd3e4c64..ca9cb123dfa 100644
--- a/src/components/dropdown/dropdown.stories.ts
+++ b/src/components/dropdown/dropdown.stories.ts
@@ -5,7 +5,7 @@ import { DefaultDropdownPlacement } from "./resources";
import readme1 from "./readme.md";
import readme2 from "../dropdown-group/readme.md";
import readme3 from "../dropdown-item/readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
const placements = [
"top-start",
@@ -386,3 +386,21 @@ export const FlipPositioning = stepStory(
FlipPositioning.parameters = {
layout: "fullscreen"
};
+
+export const disabled = (): string => html`
+ Open Dropdown
+
+ 1
+ 2
+ 3
+ 4
+ 5
+
+
+ 6
+ 7
+ 8
+ 9
+ 10
+
+`;
diff --git a/src/components/dropdown/dropdown.tsx b/src/components/dropdown/dropdown.tsx
index cfe10b78b73..d5cee22368b 100644
--- a/src/components/dropdown/dropdown.tsx
+++ b/src/components/dropdown/dropdown.tsx
@@ -24,6 +24,7 @@ import { Instance as Popper, StrictModifiers } from "@popperjs/core";
import { Scale } from "../interfaces";
import { DefaultDropdownPlacement, SLOTS } from "./resources";
import { createObserver } from "../../utils/observers";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding `calcite-dropdown-group`s or `calcite-dropdown-item`s.
@@ -34,7 +35,7 @@ import { createObserver } from "../../utils/observers";
styleUrl: "dropdown.scss",
shadow: true
})
-export class Dropdown {
+export class Dropdown implements InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -54,7 +55,12 @@ export class Dropdown {
@Watch("active")
activeHandler(): void {
- this.reposition();
+ if (!this.disabled) {
+ this.reposition();
+ return;
+ }
+
+ this.active = false;
}
/**
@@ -66,6 +72,13 @@ export class Dropdown {
/** is the dropdown disabled */
@Prop({ reflect: true }) disabled = false;
+ @Watch("disabled")
+ handleDisabledChange(value: boolean): void {
+ if (!value) {
+ this.active = false;
+ }
+ }
+
/**
specify the maximum number of calcite-dropdown-items to display before showing the scroller, must be greater than 0 -
this value does not include groupTitles passed to calcite-dropdown-group
@@ -123,6 +136,10 @@ export class Dropdown {
this.reposition();
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
disconnectedCallback(): void {
this.mutationObserver?.disconnect();
this.resizeObserver?.disconnect();
@@ -133,7 +150,7 @@ export class Dropdown {
const { active } = this;
return (
-
+
{
diff --git a/src/components/fab/fab.e2e.ts b/src/components/fab/fab.e2e.ts
index af14f0df3d3..ca7a5caf1eb 100755
--- a/src/components/fab/fab.e2e.ts
+++ b/src/components/fab/fab.e2e.ts
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
-import { accessible, hidden, renders } from "../../tests/commonTests";
+import { accessible, disabled, hidden, renders } from "../../tests/commonTests";
import { CSS } from "./resources";
import { defaults } from "../../tests/commonTests";
@@ -20,6 +20,8 @@ describe("calcite-fab", () => {
}
]));
+ it("can be disabled", () => disabled("calcite-fab"));
+
it(`should set all internal calcite-button types to 'button'`, async () => {
const page = await newE2EPage({
html: "
"
@@ -82,14 +84,6 @@ describe("calcite-fab", () => {
expect(await calciteButton.getProperty("label")).toBe("hi");
});
- it("should be disabled", async () => {
- const page = await newE2EPage();
- await page.setContent(`
`);
-
- const button = await page.find(`calcite-fab >>> .${CSS.button}`);
- expect(button).toHaveAttribute("disabled");
- });
-
it("should have appearance=outline", async () => {
const page = await newE2EPage();
await page.setContent(`
`);
diff --git a/src/components/fab/fab.scss b/src/components/fab/fab.scss
index 7ea0c545f17..9df9e1eaa3b 100755
--- a/src/components/fab/fab.scss
+++ b/src/components/fab/fab.scss
@@ -2,6 +2,8 @@
@apply flex bg-transparent;
}
+@include disabled();
+
calcite-button {
@apply shadow-2;
&:hover {
diff --git a/src/components/fab/fab.stories.ts b/src/components/fab/fab.stories.ts
index 7dbef3f56c9..42bb157ba2c 100644
--- a/src/components/fab/fab.stories.ts
+++ b/src/components/fab/fab.stories.ts
@@ -9,6 +9,7 @@ import {
import readme from "./readme.md";
import { ATTRIBUTES } from "../../../.storybook/resources";
import { ICONS } from "./resources";
+import { html } from "../../../support/formatting";
const { scale } = ATTRIBUTES;
export default {
@@ -107,3 +108,5 @@ export const darkThemeRTL = (): string =>
);
darkThemeRTL.parameters = { themes: themesDarkDefault };
+
+export const disabled = (): string => html`
`;
diff --git a/src/components/fab/fab.tsx b/src/components/fab/fab.tsx
index b3d07c6d509..a56d52c30b6 100755
--- a/src/components/fab/fab.tsx
+++ b/src/components/fab/fab.tsx
@@ -3,13 +3,14 @@ import { Appearance, Scale } from "../interfaces";
import { ButtonColor } from "../button/interfaces";
import { CSS, ICONS } from "./resources";
import { focusElement } from "../../utils/dom";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
@Component({
tag: "calcite-fab",
styleUrl: "fab.scss",
shadow: true
})
-export class Fab {
+export class Fab implements InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -72,6 +73,16 @@ export class Fab {
private buttonEl: HTMLElement;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Methods
diff --git a/src/components/filter/filter.e2e.ts b/src/components/filter/filter.e2e.ts
index 94a35fc7bed..77d691aa4d1 100644
--- a/src/components/filter/filter.e2e.ts
+++ b/src/components/filter/filter.e2e.ts
@@ -1,5 +1,5 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
-import { accessible, defaults, focusable, hidden, reflects, renders } from "../../tests/commonTests";
+import { accessible, defaults, disabled, focusable, hidden, reflects, renders } from "../../tests/commonTests";
import { CSS } from "./resources";
describe("calcite-filter", () => {
@@ -11,6 +11,8 @@ describe("calcite-filter", () => {
it("is focusable", async () => focusable("calcite-filter"));
+ it("can be disabled", () => disabled("calcite-filter"));
+
it("reflects", async () =>
reflects("calcite-filter", [
{
diff --git a/src/components/filter/filter.scss b/src/components/filter/filter.scss
index 94455924d5d..cf5806fb7bc 100644
--- a/src/components/filter/filter.scss
+++ b/src/components/filter/filter.scss
@@ -3,6 +3,8 @@
@apply flex w-full;
}
+@include disabled();
+
.container {
@apply flex w-full p-2;
}
diff --git a/src/components/filter/filter.tsx b/src/components/filter/filter.tsx
index e0ca71191ce..b25c5de98cd 100644
--- a/src/components/filter/filter.tsx
+++ b/src/components/filter/filter.tsx
@@ -14,6 +14,7 @@ import { debounce, forIn } from "lodash-es";
import { CSS, ICONS, TEXT } from "./resources";
import { Scale } from "../interfaces";
import { focusElement } from "../../utils/dom";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
const filterDebounceInMs = 250;
@@ -22,7 +23,7 @@ const filterDebounceInMs = 250;
styleUrl: "filter.scss",
shadow: true
})
-export class Filter {
+export class Filter implements InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -92,6 +93,16 @@ export class Filter {
textInput: HTMLCalciteInputElement;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Events
@@ -187,12 +198,11 @@ export class Filter {
return (
- {disabled ? : null}
}
-
`;
+
+export const disabled = (): string => html`disabled component will propagate to the rendered child */
/** Passing a 'href' will render an anchor link, instead of a span. Role will be set to link, or link, depending on this. */
@@ -13,7 +14,7 @@ import { CSS_UTILITY } from "../../utils/resources";
styleUrl: "link.scss",
shadow: true
})
-export class Link {
+export class Link implements InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -61,6 +62,10 @@ export class Link {
//
//--------------------------------------------------------------------------
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
render(): VNode {
const { download, el } = this;
const dir = getElementDir(el);
@@ -85,7 +90,7 @@ export class Link {
const Tag = childElType;
const role = childElType === "span" ? "link" : null;
- const tabIndex = this.disabled ? -1 : childElType === "span" ? 0 : null;
+ const tabIndex = childElType === "span" ? 0 : null;
return (
diff --git a/src/components/list-item/list-item.e2e.ts b/src/components/list-item/list-item.e2e.ts
index 5a56e314f83..d34f0cbbe28 100755
--- a/src/components/list-item/list-item.e2e.ts
+++ b/src/components/list-item/list-item.e2e.ts
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
-import { hidden, renders, focusable, slots } from "../../tests/commonTests";
+import { hidden, renders, focusable, slots, disabled } from "../../tests/commonTests";
import { defaults } from "../../tests/commonTests";
import { CSS, SLOTS } from "./resources";
@@ -35,6 +35,8 @@ describe("calcite-list-item", () => {
it("has slots", () => slots("calcite-list-item", SLOTS));
+ it("can be disabled", () => disabled(``));
+
it("renders content node when label is provided", async () => {
const page = await newE2EPage({ html: `` });
diff --git a/src/components/list-item/list-item.scss b/src/components/list-item/list-item.scss
index 472f08eb749..3fa6c466463 100755
--- a/src/components/list-item/list-item.scss
+++ b/src/components/list-item/list-item.scss
@@ -2,9 +2,7 @@
@apply flex flex-col;
}
-:host([disabled]) {
- @apply pointer-events-none cursor-default;
-}
+@include disabled();
.container {
@apply bg-foreground-1
diff --git a/src/components/list-item/list-item.tsx b/src/components/list-item/list-item.tsx
index 37fd6a53362..51dadee36f4 100755
--- a/src/components/list-item/list-item.tsx
+++ b/src/components/list-item/list-item.tsx
@@ -6,6 +6,7 @@ import {
connectConditionalSlotComponent,
disconnectConditionalSlotComponent
} from "../../utils/conditionalSlot";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding `calcite-list-item` and `calcite-list-item-group` elements.
@@ -19,7 +20,7 @@ import {
styleUrl: "list-item.scss",
shadow: true
})
-export class ListItem implements ConditionalSlotComponent {
+export class ListItem implements ConditionalSlotComponent, InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -56,6 +57,16 @@ export class ListItem implements ConditionalSlotComponent {
focusEl: HTMLButtonElement;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Lifecycle
diff --git a/src/components/list/list.e2e.ts b/src/components/list/list.e2e.ts
index 9fbca27c8c1..5e16eb3937c 100755
--- a/src/components/list/list.e2e.ts
+++ b/src/components/list/list.e2e.ts
@@ -1,5 +1,5 @@
-import { accessible, hidden, renders, focusable } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { accessible, hidden, renders, focusable, disabled } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
describe("calcite-list", () => {
it("renders", async () => renders("calcite-list", { display: "block" }));
@@ -29,4 +29,12 @@ describe("calcite-list", () => {
`);
});
+
+ it("can be disabled", () =>
+ disabled(
+ html`
+
+ `,
+ { focusTarget: "child" }
+ ));
});
diff --git a/src/components/list/list.scss b/src/components/list/list.scss
index d05a4d078bb..10351161b8f 100755
--- a/src/components/list/list.scss
+++ b/src/components/list/list.scss
@@ -2,6 +2,8 @@
@apply block;
}
+@include disabled();
+
.container {
@apply box-border
flex
diff --git a/src/components/list/list.stories.ts b/src/components/list/list.stories.ts
index 1fb44527247..86701731a3f 100644
--- a/src/components/list/list.stories.ts
+++ b/src/components/list/list.stories.ts
@@ -1,8 +1,8 @@
-import { themesDarkDefault } from "../../../.storybook/utils";
+import { placeholderImage, themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
import itemReadme from "../list-item/readme.md";
import groupReadme from "../list-item-group/readme.md";
-import { html, placeholderImage } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/List",
@@ -187,3 +187,21 @@ export const DarkMode = (): string => html`
DarkMode.storyName = "Dark mode";
DarkMode.parameters = { themes: themesDarkDefault };
+
+export const disabled = (): string => html`
+
+
+
+`;
diff --git a/src/components/list/list.tsx b/src/components/list/list.tsx
index 168b7c54380..fa0c77c18b3 100755
--- a/src/components/list/list.tsx
+++ b/src/components/list/list.tsx
@@ -1,6 +1,7 @@
import { Component, Element, h, VNode, Host, Prop, Method } from "@stencil/core";
import { CSS } from "./resources";
import { HeadingLevel } from "../functional/Heading";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* A general purpose list that enables users to construct list items that conform to Calcite styling.
@@ -11,18 +12,33 @@ import { HeadingLevel } from "../functional/Heading";
styleUrl: "list.scss",
shadow: true
})
-export class List {
+export class List implements InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
//
// --------------------------------------------------------------------------
+ /**
+ * When true, disabled prevents user interaction.
+ */
+ @Prop({ reflect: true }) disabled = false;
+
/**
* Number at which section headings should start for this component.
*/
@Prop() headingLevel: HeadingLevel;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Private Properties
diff --git a/src/components/loader/loader.stories.ts b/src/components/loader/loader.stories.ts
index 2bc05f66625..3ef94645178 100644
--- a/src/components/loader/loader.stories.ts
+++ b/src/components/loader/loader.stories.ts
@@ -2,7 +2,7 @@ import { number, color, select } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Loader",
diff --git a/src/components/modal/modal.e2e.ts b/src/components/modal/modal.e2e.ts
index 6be977f826b..09e30d7a0d7 100644
--- a/src/components/modal/modal.e2e.ts
+++ b/src/components/modal/modal.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { focusable, renders, slots } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { SLOTS } from "./resources";
describe("calcite-modal properties", () => {
diff --git a/src/components/modal/modal.stories.ts b/src/components/modal/modal.stories.ts
index fa3915f5250..5893fbeb3bf 100644
--- a/src/components/modal/modal.stories.ts
+++ b/src/components/modal/modal.stories.ts
@@ -2,7 +2,7 @@ import { select, text, number } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Modal",
diff --git a/src/components/notice/notice.e2e.ts b/src/components/notice/notice.e2e.ts
index 517f6aaed63..585dc174c0f 100644
--- a/src/components/notice/notice.e2e.ts
+++ b/src/components/notice/notice.e2e.ts
@@ -1,7 +1,7 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, focusable, renders, slots } from "../../tests/commonTests";
import { CSS, SLOTS } from "./resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-notice", () => {
const noticeContent = `
diff --git a/src/components/notice/notice.stories.ts b/src/components/notice/notice.stories.ts
index 5f8762f2cd2..5de29608ba4 100644
--- a/src/components/notice/notice.stories.ts
+++ b/src/components/notice/notice.stories.ts
@@ -2,7 +2,7 @@ import { select } from "@storybook/addon-knobs";
import { boolean, iconNames } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Notice",
diff --git a/src/components/pagination/pagination.stories.ts b/src/components/pagination/pagination.stories.ts
index 2aa07fcdab0..1ba8ec7fb4d 100644
--- a/src/components/pagination/pagination.stories.ts
+++ b/src/components/pagination/pagination.stories.ts
@@ -2,7 +2,7 @@ import { number, select } from "@storybook/addon-knobs";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Pagination",
diff --git a/src/components/panel/panel.e2e.ts b/src/components/panel/panel.e2e.ts
index 2d3f4553f41..9fb957c3715 100644
--- a/src/components/panel/panel.e2e.ts
+++ b/src/components/panel/panel.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
-import { accessible, defaults, focusable, hidden, renders, slots } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { accessible, defaults, disabled, focusable, hidden, renders, slots } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
import { CSS, SLOTS } from "./resources";
describe("calcite-panel", () => {
@@ -22,6 +22,8 @@ describe("calcite-panel", () => {
it("has slots", () => slots("calcite-panel", SLOTS));
+ it("can be disabled", () => disabled(`scrolling content`));
+
it("honors dismissed prop", async () => {
const page = await newE2EPage();
diff --git a/src/components/panel/panel.scss b/src/components/panel/panel.scss
index f0152bf6e29..e4d408294e0 100644
--- a/src/components/panel/panel.scss
+++ b/src/components/panel/panel.scss
@@ -19,6 +19,8 @@
--calcite-panel-max-width: unset;
}
+@include disabled();
+
@import "../../assets/styles/header";
.container {
diff --git a/src/components/panel/panel.stories.ts b/src/components/panel/panel.stories.ts
index 12c69836b99..d18c271ff63 100644
--- a/src/components/panel/panel.stories.ts
+++ b/src/components/panel/panel.stories.ts
@@ -9,7 +9,7 @@ import {
import { ATTRIBUTES } from "../../../.storybook/resources";
import readme from "./readme.md";
import { SLOTS, TEXT } from "./resources";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Panel",
@@ -163,3 +163,5 @@ export const darkThemeRTL = (): string =>
);
darkThemeRTL.parameters = { themes: themesDarkDefault };
+
+export const disabled = (): string => html`disabled`;
diff --git a/src/components/panel/panel.tsx b/src/components/panel/panel.tsx
index 1d99ef0f9d7..87ba2a9070c 100644
--- a/src/components/panel/panel.tsx
+++ b/src/components/panel/panel.tsx
@@ -20,6 +20,7 @@ import {
connectConditionalSlotComponent,
disconnectConditionalSlotComponent
} from "../../utils/conditionalSlot";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding custom content.
@@ -36,7 +37,7 @@ import {
styleUrl: "panel.scss",
shadow: true
})
-export class Panel implements ConditionalSlotComponent {
+export class Panel implements ConditionalSlotComponent, InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -123,6 +124,16 @@ export class Panel implements ConditionalSlotComponent {
*/
@Prop({ reflect: true }) menuOpen = false;
+ //--------------------------------------------------------------------------
+ //
+ // Lifecycle
+ //
+ //--------------------------------------------------------------------------
+
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Private Properties
@@ -452,7 +463,7 @@ export class Panel implements ConditionalSlotComponent {
}
render(): VNode {
- const { dismissed, disabled, dismissible, loading, panelKeyDownHandler } = this;
+ const { dismissed, dismissible, loading, panelKeyDownHandler } = this;
const panelNode = (
- {loading || disabled ? : null}
+ {loading ? : null}
{panelNode}
);
diff --git a/src/components/pick-list-group/pick-list-group.e2e.ts b/src/components/pick-list-group/pick-list-group.e2e.ts
index f4cb9724ba5..2e35b90199a 100644
--- a/src/components/pick-list-group/pick-list-group.e2e.ts
+++ b/src/components/pick-list-group/pick-list-group.e2e.ts
@@ -1,7 +1,7 @@
import { newE2EPage } from "@stencil/core/testing";
import { CSS, SLOTS } from "./resources";
import { accessible, defaults, renders, slots } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-pick-list-group", () => {
it("renders", async () => renders("calcite-pick-list-group", { display: "block" }));
diff --git a/src/components/pick-list-item/pick-list-item.e2e.ts b/src/components/pick-list-item/pick-list-item.e2e.ts
index 0add40fe23b..92dc9173010 100644
--- a/src/components/pick-list-item/pick-list-item.e2e.ts
+++ b/src/components/pick-list-item/pick-list-item.e2e.ts
@@ -1,7 +1,7 @@
import { CSS, SLOTS } from "./resources";
-import { accessible, renders, slots } from "../../tests/commonTests";
+import { accessible, disabled, renders, slots } from "../../tests/commonTests";
import { newE2EPage } from "@stencil/core/testing";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-pick-list-item", () => {
it("renders", async () => renders("calcite-pick-list-item", { display: "flex" }));
@@ -28,6 +28,8 @@ describe("calcite-pick-list-item", () => {
it("has slots", () => slots("calcite-pick-list-item", SLOTS));
+ it("can be disabled", async () => disabled("calcite-pick-list-item"));
+
it("should toggle selected attribute when clicked", async () => {
const page = await newE2EPage({ html: `
` });
diff --git a/src/components/pick-list-item/pick-list-item.scss b/src/components/pick-list-item/pick-list-item.scss
index be0670bfd1b..d3881fa3b13 100644
--- a/src/components/pick-list-item/pick-list-item.scss
+++ b/src/components/pick-list-item/pick-list-item.scss
@@ -97,3 +97,5 @@
.actions--start ~ .label {
padding-inline-start: theme("padding.1");
}
+
+@include disabled();
diff --git a/src/components/pick-list-item/pick-list-item.tsx b/src/components/pick-list-item/pick-list-item.tsx
index ce43e8f983e..c16e14d3a44 100644
--- a/src/components/pick-list-item/pick-list-item.tsx
+++ b/src/components/pick-list-item/pick-list-item.tsx
@@ -18,6 +18,7 @@ import {
connectConditionalSlotComponent,
disconnectConditionalSlotComponent
} from "../../utils/conditionalSlot";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot actions-end - a slot for adding actions or content to the end side of the item.
@@ -28,7 +29,7 @@ import {
styleUrl: "pick-list-item.scss",
shadow: true
})
-export class PickListItem implements ConditionalSlotComponent {
+export class PickListItem implements ConditionalSlotComponent, InteractiveComponent {
// --------------------------------------------------------------------------
//
// Properties
@@ -48,7 +49,7 @@ export class PickListItem implements ConditionalSlotComponent {
/**
* When true, the item cannot be clicked and is visually muted.
*/
- @Prop({ reflect: true }) disabled? = false;
+ @Prop({ reflect: true }) disabled = false;
/**
* When false, the item cannot be deselected by user interaction.
@@ -150,6 +151,10 @@ export class PickListItem implements ConditionalSlotComponent {
disconnectConditionalSlotComponent(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this, this.el.closest("calcite-pick-list") ? "managed" : false);
+ }
+
// --------------------------------------------------------------------------
//
// Events
@@ -198,10 +203,6 @@ export class PickListItem implements ConditionalSlotComponent {
*/
@Method()
async toggleSelected(coerce?: boolean): Promise
{
- if (this.disabled) {
- return;
- }
-
this.selected = typeof coerce === "boolean" ? coerce : !this.selected;
}
diff --git a/src/components/pick-list/pick-list.e2e.ts b/src/components/pick-list/pick-list.e2e.ts
index 016bc1f84d7..6d65227749f 100644
--- a/src/components/pick-list/pick-list.e2e.ts
+++ b/src/components/pick-list/pick-list.e2e.ts
@@ -4,12 +4,13 @@ import { accessible, hidden, renders, defaults } from "../../tests/commonTests";
import {
selectionAndDeselection,
filterBehavior,
- disabledStates,
+ loadingState,
keyboardNavigation,
itemRemoval,
- focusing
+ focusing,
+ disabling
} from "./shared-list-tests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { CSS as PICK_LIST_GROUP_CSS } from "../pick-list-group/resources";
describe("calcite-pick-list", () => {
@@ -32,6 +33,10 @@ describe("calcite-pick-list", () => {
`));
+ describe("disabling", () => {
+ disabling("pick");
+ });
+
describe("Selection and Deselection", () => {
selectionAndDeselection("pick");
});
@@ -183,8 +188,8 @@ describe("calcite-pick-list", () => {
itemRemoval("pick");
});
- describe("disabled states", () => {
- disabledStates("pick");
+ describe("loading state", () => {
+ loadingState("pick");
});
describe("setFocus", () => {
diff --git a/src/components/pick-list/pick-list.scss b/src/components/pick-list/pick-list.scss
index 4e0699ecedc..43a16b0d908 100644
--- a/src/components/pick-list/pick-list.scss
+++ b/src/components/pick-list/pick-list.scss
@@ -15,6 +15,8 @@
}
}
+@include disabled();
+
:host([filter-enabled]) header {
@apply bg-foreground-1
mb-1
diff --git a/src/components/pick-list/pick-list.stories.ts b/src/components/pick-list/pick-list.stories.ts
index 726b4ba0055..fa5e517b368 100644
--- a/src/components/pick-list/pick-list.stories.ts
+++ b/src/components/pick-list/pick-list.stories.ts
@@ -9,7 +9,7 @@ import {
import readme from "./readme.md";
import itemReadme from "../pick-list-item/readme.md";
import groupReadme from "../pick-list-group/readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Pick List",
@@ -165,3 +165,14 @@ export const nested = (): string =>
`
);
+
+export const disabled = (): string => html`
+
+
+
+`;
diff --git a/src/components/pick-list/pick-list.tsx b/src/components/pick-list/pick-list.tsx
index 60551bcb8d3..f33bb74df9d 100644
--- a/src/components/pick-list/pick-list.tsx
+++ b/src/components/pick-list/pick-list.tsx
@@ -34,6 +34,7 @@ import {
import List from "./shared-list-render";
import { HeadingLevel } from "../functional/Heading";
import { createObserver } from "../../utils/observers";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding `calcite-pick-list-item` elements or `calcite-pick-list-group` elements. Items are displayed as a vertical list.
@@ -46,7 +47,8 @@ import { createObserver } from "../../utils/observers";
})
export class PickList<
ItemElement extends HTMLCalcitePickListItemElement = HTMLCalcitePickListItemElement
-> {
+> implements InteractiveComponent
+{
// --------------------------------------------------------------------------
//
// Properties
@@ -128,6 +130,10 @@ export class PickList<
cleanUpObserver.call(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Events
diff --git a/src/components/pick-list/shared-list-logic.ts b/src/components/pick-list/shared-list-logic.ts
index d505bb07765..be8b0697c4a 100644
--- a/src/components/pick-list/shared-list-logic.ts
+++ b/src/components/pick-list/shared-list-logic.ts
@@ -112,10 +112,10 @@ export function calciteListFocusOutHandler(this: List, event
return;
}
- items.forEach((item) => {
+ filterOutDisabled(items).forEach((item) => {
toggleSingleSelectItemTabbing(
item,
- selectedValues.size === 0 ? item.contains(event.target) || event.target === item : item.selected
+ selectedValues.size === 0 ? item.contains(event.target as HTMLElement) || event.target === item : item.selected
);
});
}
@@ -137,7 +137,7 @@ export function keyDownHandler(this: List, event: KeyboardEv
event.preventDefault();
- const index = getRoundRobinIndex(currentIndex + (key === "ArrowUp" ? -1 : 1), totalItems);
+ const index = moveItemIndex(this, target as ListItemElement, key === "ArrowUp" ? "up" : "down");
const item = items[index];
items.forEach((i: HTMLCalcitePickListItemElement | HTMLCalciteValueListItemElement) =>
@@ -151,6 +151,34 @@ export function keyDownHandler(this: List, event: KeyboardEv
focusElement(item);
}
+export function moveItemIndex(
+ list: List,
+ item: ListItemElement,
+ direction: "up" | "down"
+): number {
+ const { items } = list;
+ const { length: totalItems } = items;
+ const currentIndex = (items as ListItemElement[]).indexOf(item);
+ const directionFactor = direction === "up" ? -1 : 1;
+ let moveOffset = 1;
+ let index = getRoundRobinIndex(currentIndex + directionFactor * moveOffset++, totalItems);
+ const firstMovedIndex = index;
+
+ while (items[index].disabled) {
+ index = getRoundRobinIndex(currentIndex + directionFactor * moveOffset++, totalItems);
+
+ if (index === firstMovedIndex) {
+ break;
+ }
+ }
+
+ return index;
+}
+
+function filterOutDisabled(items: ListItemElement[]): ListItemElement[] {
+ return items.filter((item) => !item.disabled);
+}
+
export function internalCalciteListChangeEvent(this: List): void {
this.calciteListChange.emit(this.selectedValues as any);
}
@@ -175,6 +203,10 @@ export function removeItem>(this:
}
function toggleSingleSelectItemTabbing(item: ListItemElement, selectable: boolean): void {
+ if (item.disabled) {
+ return;
+ }
+
// using attribute intentionally
if (selectable) {
item.removeAttribute("tabindex");
@@ -196,12 +228,13 @@ export async function setFocus(this: List, focusId: ListFocu
}
if (multiple) {
- return items[0].setFocus();
+ return filterOutDisabled(items)[0]?.setFocus();
}
- const focusTarget = (items as ListItemElement[]).find((item) => item.selected) || items[0];
+ const filtered = filterOutDisabled(items);
+ const focusTarget = filtered.find((item) => item.selected) || filtered[0];
- if (selectionFollowsFocus) {
+ if (selectionFollowsFocus && focusTarget) {
focusTarget.selected = true;
}
@@ -232,7 +265,7 @@ export function setUpItems(
const [first] = items;
- if (!hasSelected && first) {
+ if (!hasSelected && first && !first.disabled) {
toggleSingleSelectItemTabbing(first, true);
}
}
diff --git a/src/components/pick-list/shared-list-render.tsx b/src/components/pick-list/shared-list-render.tsx
index 813f8425685..c632f659e8d 100644
--- a/src/components/pick-list/shared-list-render.tsx
+++ b/src/components/pick-list/shared-list-render.tsx
@@ -29,7 +29,7 @@ export const List: FunctionalComponent<{ props: ListProps } & DOMAttributes
}): VNode => {
const defaultSlot = ;
return (
-
+
{filterEnabled ? (
@@ -44,7 +44,7 @@ export const List: FunctionalComponent<{ props: ListProps } & DOMAttributes
) : null}
- {loading || disabled ? : null}
+ {loading ? : null}
{defaultSlot}
diff --git a/src/components/pick-list/shared-list-tests.ts b/src/components/pick-list/shared-list-tests.ts
index c6eb4e0dedb..f52ee7e1c4b 100644
--- a/src/components/pick-list/shared-list-tests.ts
+++ b/src/components/pick-list/shared-list-tests.ts
@@ -1,6 +1,6 @@
import { E2EElement, E2EPage, newE2EPage } from "@stencil/core/testing";
-import { focusable } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { disabled, focusable } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
import { CSS as PICK_LIST_ITEM_CSS } from "../pick-list-item/resources";
type ListType = "pick" | "value";
@@ -23,8 +23,10 @@ export function keyboardNavigation(listType: ListType): void {
const page = await newE2EPage({
html: `
+
+
`
});
@@ -203,14 +205,13 @@ export function keyboardNavigation(listType: ListType): void {
});
it("resets tabindex to selected item when focusing out of list", async () => {
- const page = await newE2EPage({
- html: `
+ const page = await newE2EPage();
+ await page.setContent(html`
- `
- });
+ `);
await page.keyboard.press("Tab");
await page.keyboard.press("Tab");
@@ -533,24 +534,7 @@ export function filterBehavior(listType: ListType): void {
});
}
-export function disabledStates(listType: ListType): void {
- it("disabled", async () => {
- const page = await newE2EPage({
- html: html`
-
-
-
- `
- });
-
- const list = await page.find(`calcite-${listType}-list`);
- const item1 = await list.find("[value=one]");
- const toggleSpy = await list.spyOnEvent("calciteListChange");
-
- await item1.click();
- expect(toggleSpy).toHaveReceivedEventTimes(0);
- });
-
+export function loadingState(listType: ListType): void {
it("loading", async () => {
const page = await newE2EPage();
await page.setContent(`
@@ -617,3 +601,17 @@ export function focusing(listType: ListType): void {
));
});
}
+
+export function disabling(listType: ListType): void {
+ it("can be disabled", () =>
+ disabled(
+ html`
+
+
+
+ `,
+ {
+ focusTarget: "child"
+ }
+ ));
+}
diff --git a/src/components/popover/popover.stories.ts b/src/components/popover/popover.stories.ts
index 15daa57571c..fa507bb3314 100644
--- a/src/components/popover/popover.stories.ts
+++ b/src/components/popover/popover.stories.ts
@@ -1,5 +1,5 @@
import { select, number, text } from "@storybook/addon-knobs";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
import { boolean, createSteps, stepStory, setTheme, setKnobs } from "../../../.storybook/helpers";
import readme from "./readme.md";
import managerReadme from "../popover-manager/readme.md";
diff --git a/src/components/progress/progress.stories.ts b/src/components/progress/progress.stories.ts
index 8f78595b44c..7ec58e74a4b 100644
--- a/src/components/progress/progress.stories.ts
+++ b/src/components/progress/progress.stories.ts
@@ -2,7 +2,7 @@ import { select, number, text, boolean } from "@storybook/addon-knobs";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Progress",
diff --git a/src/components/radio-button-group/radio-button-group.e2e.ts b/src/components/radio-button-group/radio-button-group.e2e.ts
index 033688ea82a..d94b70271bf 100644
--- a/src/components/radio-button-group/radio-button-group.e2e.ts
+++ b/src/components/radio-button-group/radio-button-group.e2e.ts
@@ -1,6 +1,6 @@
import { newE2EPage } from "@stencil/core/testing";
import { accessible, defaults, hidden, reflects, renders } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
describe("calcite-radio-button-group", () => {
it("renders", async () => renders("calcite-radio-button-group", { display: "flex" }));
diff --git a/src/components/radio-button-group/radio-button-group.stories.ts b/src/components/radio-button-group/radio-button-group.stories.ts
index c9e734dc851..df010851b61 100644
--- a/src/components/radio-button-group/radio-button-group.stories.ts
+++ b/src/components/radio-button-group/radio-button-group.stories.ts
@@ -2,7 +2,7 @@ import { select } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/Radio/Radio Button Group",
diff --git a/src/components/radio-button/radio-button.e2e.ts b/src/components/radio-button/radio-button.e2e.ts
index 42300312e09..5b180fd414c 100644
--- a/src/components/radio-button/radio-button.e2e.ts
+++ b/src/components/radio-button/radio-button.e2e.ts
@@ -2,6 +2,7 @@ import { newE2EPage } from "@stencil/core/testing";
import {
accessible,
defaults,
+ disabled,
focusable,
formAssociated,
hidden,
@@ -31,6 +32,8 @@ describe("calcite-radio-button", () => {
propertyToToggle: "checked"
}));
+ it("can be disabled", () => disabled("calcite-radio-button"));
+
it("focusing skips over hidden radio-buttons", async () => {
const page = await newE2EPage();
await page.setContent(`
diff --git a/src/components/radio-button/radio-button.scss b/src/components/radio-button/radio-button.scss
index 72b2bd167d7..b5020405e86 100644
--- a/src/components/radio-button/radio-button.scss
+++ b/src/components/radio-button/radio-button.scss
@@ -16,8 +16,7 @@
}
}
-:host([disabled]) {
- @apply cursor-pointer;
+@include disabled() {
.radio {
@apply opacity-disabled cursor-default;
}
diff --git a/src/components/radio-button/radio-button.stories.ts b/src/components/radio-button/radio-button.stories.ts
index 9057cba5519..e7bd5420e7c 100644
--- a/src/components/radio-button/radio-button.stories.ts
+++ b/src/components/radio-button/radio-button.stories.ts
@@ -2,7 +2,7 @@ import { select, text } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/Radio/Radio Button",
@@ -59,3 +59,5 @@ export const RTL = (): string => html`
${text("label", "Radio Button")}
`;
+
+export const disabled = (): string => html``;
diff --git a/src/components/radio-button/radio-button.tsx b/src/components/radio-button/radio-button.tsx
index a0f7607e95a..8e9479be043 100644
--- a/src/components/radio-button/radio-button.tsx
+++ b/src/components/radio-button/radio-button.tsx
@@ -23,13 +23,16 @@ import {
} from "../../utils/form";
import { CSS } from "./resources";
import { getRoundRobinIndex } from "../../utils/array";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
@Component({
tag: "calcite-radio-button",
styleUrl: "radio-button.scss",
shadow: true
})
-export class RadioButton implements LabelableComponent, CheckableFormCompoment {
+export class RadioButton
+ implements LabelableComponent, CheckableFormCompoment, InteractiveComponent
+{
//--------------------------------------------------------------------------
//
// Element
@@ -388,6 +391,10 @@ export class RadioButton implements LabelableComponent, CheckableFormCompoment {
disconnectForm(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
// --------------------------------------------------------------------------
//
// Render Methods
diff --git a/src/components/radio-group/radio-group.e2e.ts b/src/components/radio-group/radio-group.e2e.ts
index f658c27b572..a562fda0973 100644
--- a/src/components/radio-group/radio-group.e2e.ts
+++ b/src/components/radio-group/radio-group.e2e.ts
@@ -1,6 +1,6 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
-import { focusable, formAssociated, labelable, renders } from "../../tests/commonTests";
-import { html } from "../../tests/utils";
+import { disabled, focusable, formAssociated, labelable, renders } from "../../tests/commonTests";
+import { html } from "../../../support/formatting";
describe("calcite-radio-group", () => {
it("renders", () => renders("calcite-radio-group", { display: "flex" }));
@@ -15,6 +15,16 @@ describe("calcite-radio-group", () => {
{ focusTargetSelector: "calcite-radio-group-item" }
));
+ it("can be disabled", () =>
+ disabled(
+ html`
+
+
+
+ `,
+ { focusTarget: "child" }
+ ));
+
it("does not require an item to be checked", async () => {
const page = await newE2EPage();
await page.setContent(
diff --git a/src/components/radio-group/radio-group.scss b/src/components/radio-group/radio-group.scss
index 868bdc46f8f..a14ddc8f9ea 100644
--- a/src/components/radio-group/radio-group.scss
+++ b/src/components/radio-group/radio-group.scss
@@ -5,6 +5,8 @@
outline-offset: -1px;
}
+@include disabled();
+
:host([layout="vertical"]) {
@apply flex-col items-start self-start;
}
@@ -28,9 +30,4 @@
@apply z-0;
}
-// disabled styles
-:host([disabled]) {
- @apply opacity-disabled pointer-events-none;
-}
-
@include hidden-form-input();
diff --git a/src/components/radio-group/radio-group.stories.ts b/src/components/radio-group/radio-group.stories.ts
index df88899a531..b60a1c29adf 100644
--- a/src/components/radio-group/radio-group.stories.ts
+++ b/src/components/radio-group/radio-group.stories.ts
@@ -3,7 +3,7 @@ import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme1 from "./readme.md";
import readme2 from "../radio-group-item/readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/Radio/Radio Group",
@@ -115,3 +115,10 @@ export const RTL = (): string => html`
Vue
`;
+
+export const disabled = (): string => html`
+ React
+ Ember
+ Angular
+ Vue
+`;
diff --git a/src/components/radio-group/radio-group.tsx b/src/components/radio-group/radio-group.tsx
index 75a2247b378..2eef5d8ed0b 100644
--- a/src/components/radio-group/radio-group.tsx
+++ b/src/components/radio-group/radio-group.tsx
@@ -18,6 +18,7 @@ import { Layout, Scale, Width } from "../interfaces";
import { LabelableComponent, connectLabel, disconnectLabel } from "../../utils/label";
import { connectForm, disconnectForm, FormComponent, HiddenFormInputSlot } from "../../utils/form";
import { RadioAppearance } from "./interfaces";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding `calcite-radio-group-item`s.
@@ -27,7 +28,7 @@ import { RadioAppearance } from "./interfaces";
styleUrl: "radio-group.scss",
shadow: true
})
-export class RadioGroup implements LabelableComponent, FormComponent {
+export class RadioGroup implements LabelableComponent, FormComponent, InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -133,9 +134,13 @@ export class RadioGroup implements LabelableComponent, FormComponent {
disconnectForm(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
render(): VNode {
return (
-
+
diff --git a/src/components/rating/rating.e2e.ts b/src/components/rating/rating.e2e.ts
index 7dbfd8aae05..9aa2b3dbfef 100644
--- a/src/components/rating/rating.e2e.ts
+++ b/src/components/rating/rating.e2e.ts
@@ -1,5 +1,5 @@
import { newE2EPage } from "@stencil/core/testing";
-import { renders, accessible, focusable, labelable, formAssociated } from "../../tests/commonTests";
+import { renders, accessible, focusable, labelable, formAssociated, disabled } from "../../tests/commonTests";
describe("calcite-rating", () => {
it("renders", async () => renders("", { display: "flex" }));
@@ -8,6 +8,8 @@ describe("calcite-rating", () => {
it("is labelable", async () => labelable("calcite-rating"));
+ it("can be disabled", () => disabled(""));
+
it("renders outlined star when no value or average is set", async () => {
const page = await newE2EPage();
await page.setContent("");
@@ -371,16 +373,6 @@ describe("calcite-rating", () => {
expect(element).toEqualAttribute("value", "4");
});
- it("disables click interaction when disabled is requested", async () => {
- const page = await newE2EPage();
- await page.setContent("");
- const element = await page.find("calcite-rating");
- const ratingItem1 = await page.find("calcite-rating >>> .star");
- expect(element).toEqualAttribute("value", "0");
- await ratingItem1.click();
- expect(element).toEqualAttribute("value", "0");
- });
-
it("does not render the calcite chip when count and average are not present", async () => {
const page = await newE2EPage();
await page.setContent("");
diff --git a/src/components/rating/rating.scss b/src/components/rating/rating.scss
index 573ad0e83f8..48a5e7a0fdf 100644
--- a/src/components/rating/rating.scss
+++ b/src/components/rating/rating.scss
@@ -11,6 +11,8 @@
width: fit-content;
}
+@include disabled();
+
:host([scale="s"]) {
@apply h-6;
--calcite-rating-spacing-unit: theme("spacing.1");
@@ -26,10 +28,6 @@
--calcite-rating-spacing-unit: theme("spacing.3");
}
-:host([disabled]) {
- @apply pointer-events-none opacity-50;
-}
-
:host([read-only]) {
@apply pointer-events-none;
}
diff --git a/src/components/rating/rating.stories.ts b/src/components/rating/rating.stories.ts
index c9c14b76aaa..0ec5faf2f53 100644
--- a/src/components/rating/rating.stories.ts
+++ b/src/components/rating/rating.stories.ts
@@ -2,7 +2,7 @@ import { number, select, text } from "@storybook/addon-knobs";
import { boolean } from "../../../.storybook/helpers";
import { themesDarkDefault } from "../../../.storybook/utils";
import readme from "./readme.md";
-import { html } from "../../tests/utils";
+import { html } from "../../../support/formatting";
export default {
title: "Components/Controls/Rating",
@@ -80,3 +80,5 @@ export const Rtl = (): string => html`
`;
Rtl.storyName = "RTL";
+
+export const disabled = (): string => html``;
diff --git a/src/components/rating/rating.tsx b/src/components/rating/rating.tsx
index b8535aa5efc..90d90e3b0cb 100644
--- a/src/components/rating/rating.tsx
+++ b/src/components/rating/rating.tsx
@@ -16,13 +16,14 @@ import { Scale } from "../interfaces";
import { LabelableComponent, connectLabel, disconnectLabel } from "../../utils/label";
import { connectForm, disconnectForm, FormComponent, HiddenFormInputSlot } from "../../utils/form";
import { TEXT } from "./resources";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
@Component({
tag: "calcite-rating",
styleUrl: "rating.scss",
shadow: true
})
-export class Rating implements LabelableComponent, FormComponent {
+export class Rating implements LabelableComponent, FormComponent, InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -94,6 +95,10 @@ export class Rating implements LabelableComponent, FormComponent {
disconnectForm(this);
}
+ componentDidRender(): void {
+ updateHostInteraction(this);
+ }
+
//--------------------------------------------------------------------------
//
// Events
@@ -158,6 +163,10 @@ export class Rating implements LabelableComponent, FormComponent {
id={`${this.guid}-${i}`}
name={this.guid}
onChange={() => this.updateValue(i)}
+ onClick={(event) =>
+ // click is fired from the the component's label, so we treat this as an internal event
+ event.stopPropagation()
+ }
onFocus={() => {
this.hasFocus = true;
this.focusValue = i;
@@ -174,12 +183,13 @@ export class Rating implements LabelableComponent, FormComponent {
}
render() {
- const { intlRating, showChip, scale, count, average } = this;
+ const { disabled, intlRating, showChip, scale, count, average } = this;
return (
{
it("renders", () => renders("calcite-stepper-item", { display: "flex" }));
+ it("can be disabled", () => disabled("calcite-stepper-item"));
});
diff --git a/src/components/stepper-item/stepper-item.scss b/src/components/stepper-item/stepper-item.scss
index 1579baaf517..40b38a4a7ec 100644
--- a/src/components/stepper-item/stepper-item.scss
+++ b/src/components/stepper-item/stepper-item.scss
@@ -131,13 +131,7 @@
margin-inline-end: var(--calcite-stepper-item-spacing-unit-m);
}
-:host([disabled]) {
- @apply opacity-disabled;
-}
-:host([disabled]),
-:host([disabled]) * {
- @apply pointer-events-auto cursor-not-allowed;
-}
+@include disabled();
:host([complete]) .container {
// todo dark theme
diff --git a/src/components/stepper-item/stepper-item.tsx b/src/components/stepper-item/stepper-item.tsx
index 79f371e8a10..0b32ec99be7 100644
--- a/src/components/stepper-item/stepper-item.tsx
+++ b/src/components/stepper-item/stepper-item.tsx
@@ -12,6 +12,7 @@ import {
} from "@stencil/core";
import { getElementProp } from "../../utils/dom";
import { Scale } from "../interfaces";
+import { InteractiveComponent, updateHostInteraction } from "../../utils/interactive";
/**
* @slot - A slot for adding custom content.
@@ -21,7 +22,7 @@ import { Scale } from "../interfaces";
styleUrl: "stepper-item.scss",
shadow: true
})
-export class StepperItem {
+export class StepperItem implements InteractiveComponent {
//--------------------------------------------------------------------------
//
// Element
@@ -45,7 +46,7 @@ export class StepperItem {
@Prop() error = false;
/** is the step disabled and not navigable to by a user */
- @Prop() disabled = false;
+ @Prop({ reflect: true }) disabled = false;
/** pass a title for the stepper item */
@Prop() itemTitle?: string;
@@ -126,13 +127,13 @@ export class StepperItem {
}
}
+ componentDidRender(): void {
+ updateHostInteraction(this, true);
+ }
+
render(): VNode {
return (
- this.emitRequestedItem()}
- tabindex={this.disabled ? null : 0}
- >
+ this.emitRequestedItem()}>