diff --git a/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts b/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts index 08ed0754245..6e532bbdb81 100644 --- a/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts +++ b/packages/calcite-components/src/components/accordion-item/accordion-item.e2e.ts @@ -218,4 +218,25 @@ describe("calcite-accordion-item", () => { expect(headerContent.getAttribute("aria-expanded")).toBe("true"); }); + + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-accordion-item"); + + const expandSpy = await page.spyOnEvent("calciteAccordionItemExpand"); + const collapseSpy = await page.spyOnEvent("calciteAccordionItemCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); }); diff --git a/packages/calcite-components/src/components/accordion-item/accordion-item.tsx b/packages/calcite-components/src/components/accordion-item/accordion-item.tsx index 1b4e5b2e387..68f710da9c8 100644 --- a/packages/calcite-components/src/components/accordion-item/accordion-item.tsx +++ b/packages/calcite-components/src/components/accordion-item/accordion-item.tsx @@ -1,4 +1,5 @@ // @ts-strict-ignore +import { PropertyValues } from "lit"; import { LitElement, property, createEvent, h, method, state, JsxNode } from "@arcgis/lumina"; import { closestElementCrossShadowBoundary, @@ -28,29 +29,29 @@ declare global { * @slot actions-start - A slot for adding `calcite-action`s or content to the start side of the component's header. */ export class AccordionItem extends LitElement { - // #region Static Members + //#region Static Members static override styles = styles; - // #endregion + //#endregion - // #region Private Properties + //#region Private Properties private headerEl: HTMLDivElement; private focusSetter = useSetFocus()(this); - // #endregion + //#endregion - // #region State Properties + //#region State Properties @state() hasActionsEnd = false; @state() hasActionsStart = false; - // #endregion + //#endregion - // #region Public Properties + //#region Public Properties /** * The containing `accordion` element. @@ -62,7 +63,7 @@ export class AccordionItem extends LitElement { /** Specifies a description for the component. */ @property() description: string; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Specifies heading text for the component. */ @@ -108,9 +109,9 @@ export class AccordionItem extends LitElement { */ @property({ reflect: true }) scale: Scale; - // #endregion + //#endregion - // #region Public Methods + //#region Public Methods /** * Sets focus on the component. @@ -126,9 +127,15 @@ export class AccordionItem extends LitElement { }, options); } - // #endregion + //#endregion - // #region Events + //#region Events + + /** Fires when the component's content area is collapsed. */ + calciteAccordionItemCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteAccordionItemExpand = createEvent({ cancelable: false }); /** @private */ calciteInternalAccordionItemClose = createEvent({ cancelable: false }); @@ -136,9 +143,9 @@ export class AccordionItem extends LitElement { /** @private */ calciteInternalAccordionItemSelect = createEvent({ cancelable: false }); - // #endregion + //#endregion - // #region Lifecycle + //#region Lifecycle constructor() { super(); @@ -155,9 +162,19 @@ export class AccordionItem extends LitElement { ); } - // #endregion + override willUpdate(changes: PropertyValues): void { + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.calciteAccordionItemExpand.emit(); + } else { + this.calciteAccordionItemCollapse.emit(); + } + } + } + + //#endregion - // #region Private Methods + //#region Private Methods private keyDownHandler(event: KeyboardEvent): void { if (event.target === this.el) { @@ -255,9 +272,9 @@ export class AccordionItem extends LitElement { }); } - // #endregion + //#endregion - // #region Rendering + //#region Rendering private renderActionsStart(): JsxNode { return ( @@ -355,5 +372,5 @@ export class AccordionItem extends LitElement { ); } - // #endregion + //#endregion } diff --git a/packages/calcite-components/src/components/action-bar/action-bar.e2e.ts b/packages/calcite-components/src/components/action-bar/action-bar.e2e.ts index ff744562a67..7d5efe311f5 100755 --- a/packages/calcite-components/src/components/action-bar/action-bar.e2e.ts +++ b/packages/calcite-components/src/components/action-bar/action-bar.e2e.ts @@ -611,5 +611,26 @@ describe("calcite-action-bar", () => { }, ); }); + + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-action-bar"); + + const expandSpy = await page.spyOnEvent("calciteActionBarExpand"); + const collapseSpy = await page.spyOnEvent("calciteActionBarCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); }); }); diff --git a/packages/calcite-components/src/components/action-bar/action-bar.tsx b/packages/calcite-components/src/components/action-bar/action-bar.tsx index 573ee782fa8..35bdeb94536 100755 --- a/packages/calcite-components/src/components/action-bar/action-bar.tsx +++ b/packages/calcite-components/src/components/action-bar/action-bar.tsx @@ -130,7 +130,7 @@ export class ActionBar extends LitElement { /** When `true`, the expand-toggling behavior is disabled. */ @property({ reflect: true }) expandDisabled = false; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Specifies the layout direction of the actions. */ @@ -190,6 +190,12 @@ export class ActionBar extends LitElement { //#region Events + /** Fires when the component's content area is collapsed. */ + calciteActionBarCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteActionBarExpand = createEvent({ cancelable: false }); + /** Fires when the `expanded` property is toggled. */ calciteActionBarToggle = createEvent({ cancelable: false }); @@ -219,10 +225,6 @@ export class ActionBar extends LitElement { this.overflowActions(); } - if (changes.has("expanded") && this.hasUpdated) { - this.expandedHandler(); - } - if (changes.has("layout") && (this.hasUpdated || this.layout !== "vertical")) { this.updateGroups(); } @@ -233,6 +235,15 @@ export class ActionBar extends LitElement { ) { this.overflowActionsDisabledHandler(this.overflowActionsDisabled); } + + if (changes.has("expanded") && this.hasUpdated) { + this.expandedHandler(); + if (this.expanded) { + this.calciteActionBarExpand.emit(); + } else { + this.calciteActionBarCollapse.emit(); + } + } } loaded(): void { diff --git a/packages/calcite-components/src/components/action-group/action-group.e2e.ts b/packages/calcite-components/src/components/action-group/action-group.e2e.ts index 1615317bd77..a253ce2654b 100755 --- a/packages/calcite-components/src/components/action-group/action-group.e2e.ts +++ b/packages/calcite-components/src/components/action-group/action-group.e2e.ts @@ -134,4 +134,25 @@ describe("calcite-action-group", () => { ); }); }); + + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-action-group"); + + const expandSpy = await page.spyOnEvent("calciteActionGroupExpand"); + const collapseSpy = await page.spyOnEvent("calciteActionGroupCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); }); diff --git a/packages/calcite-components/src/components/action-group/action-group.tsx b/packages/calcite-components/src/components/action-group/action-group.tsx index 46df96ff3c1..af1de488bc9 100755 --- a/packages/calcite-components/src/components/action-group/action-group.tsx +++ b/packages/calcite-components/src/components/action-group/action-group.tsx @@ -1,6 +1,15 @@ // @ts-strict-ignore import { PropertyValues } from "lit"; -import { LitElement, property, h, method, state, JsxNode, ToEvents } from "@arcgis/lumina"; +import { + LitElement, + property, + h, + method, + state, + JsxNode, + ToEvents, + createEvent, +} from "@arcgis/lumina"; import { SLOTS as ACTION_MENU_SLOTS } from "../action-menu/resources"; import { Layout, Scale } from "../interfaces"; import { FlipPlacement, LogicalPlacement, OverlayPositioning } from "../../utils/floating-ui"; @@ -57,7 +66,7 @@ export class ActionGroup extends LitElement { /** Indicates number of columns. */ @property({ type: Number, reflect: true }) columns: Columns; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Accessible name for the component. */ @@ -114,6 +123,16 @@ export class ActionGroup extends LitElement { //#endregion + //#region Events + + /** Fires when the component's content area is collapsed. */ + calciteActionGroupCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteActionGroupExpand = createEvent({ cancelable: false }); + + //#endregion + //#region Lifecycle override willUpdate(changes: PropertyValues): void { @@ -121,8 +140,18 @@ export class ActionGroup extends LitElement { To account for this semantics change, the checks for (this.hasUpdated || value != defaultValue) was added in this method Please refactor your code to reduce the need for this check. Docs: https://qawebgis.esri.com/arcgis-components/?path=/docs/lumina-transition-from-stencil--docs#watching-for-property-changes */ - if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) { - this.menuOpen = false; + + if (changes.has("expanded")) { + if (this.hasUpdated || this.expanded !== false) { + this.menuOpen = false; + } + if (this.hasUpdated) { + if (this.expanded) { + this.calciteActionGroupExpand.emit(); + } else { + this.calciteActionGroupCollapse.emit(); + } + } } } diff --git a/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts b/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts index 5b71b8058a5..c02594ef8aa 100755 --- a/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts +++ b/packages/calcite-components/src/components/action-menu/action-menu.e2e.ts @@ -622,6 +622,27 @@ describe("calcite-action-menu", () => { }); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-action-menu"); + + const expandSpy = await page.spyOnEvent("calciteActionMenuExpand"); + const collapseSpy = await page.spyOnEvent("calciteActionMenuCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("theme", () => { themed( html` diff --git a/packages/calcite-components/src/components/action-menu/action-menu.tsx b/packages/calcite-components/src/components/action-menu/action-menu.tsx index 63aff4ee8fa..eb54395af0c 100755 --- a/packages/calcite-components/src/components/action-menu/action-menu.tsx +++ b/packages/calcite-components/src/components/action-menu/action-menu.tsx @@ -37,13 +37,13 @@ const SUPPORTED_MENU_NAV_KEYS = ["ArrowUp", "ArrowDown", "End", "Home"]; * @slot tooltip - A slot for adding a tooltip for the menu. */ export class ActionMenu extends LitElement { - // #region Static Members + //#region Static Members static override styles = styles; - // #endregion + //#endregion - // #region Private Properties + //#region Private Properties private guid = guid(); @@ -121,22 +121,22 @@ export class ActionMenu extends LitElement { private focusSetter = useSetFocus()(this); - // #endregion + //#endregion - // #region State Properties + //#region State Properties @state() activeMenuItemIndex = -1; @state() menuButtonEl: Action["el"]; - // #endregion + //#endregion - // #region Public Properties + //#region Public Properties /** Specifies the appearance of the component. */ @property({ reflect: true }) appearance: Extract<"solid" | "transparent", Appearance> = "solid"; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Specifies the component's fallback slotted content `placement` when it's initial or specified `placement` has insufficient space available. */ @@ -176,9 +176,9 @@ export class ActionMenu extends LitElement { /** Specifies the size of the component's trigger `calcite-action`. */ @property({ reflect: true }) scale: Scale = "m"; - // #endregion + //#endregion - // #region Public Methods + //#region Public Methods /** * Sets focus on the component. @@ -194,16 +194,22 @@ export class ActionMenu extends LitElement { }, options); } - // #endregion + //#endregion - // #region Events + //#region Events + + /** Fires when the component's content area is collapsed. */ + calciteActionMenuCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteActionMenuExpand = createEvent({ cancelable: false }); /** Fires when the `open` property is toggled. */ calciteActionMenuOpen = createEvent({ cancelable: false }); - // #endregion + //#endregion - // #region Lifecycle + //#region Lifecycle override connectedCallback(): void { this.connectMenuButtonEl(); @@ -224,15 +230,23 @@ export class ActionMenu extends LitElement { ) { this.updateActions(this.actionElements); } + + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.calciteActionMenuExpand.emit(); + } else { + this.calciteActionMenuCollapse.emit(); + } + } } override disconnectedCallback(): void { this.disconnectMenuButtonEl(); } - // #endregion + //#endregion - // #region Private Methods + //#region Private Methods private expandedHandler(): void { this.open = false; @@ -453,9 +467,9 @@ export class ActionMenu extends LitElement { this.open = false; } - // #endregion + //#endregion - // #region Rendering + //#region Rendering private renderMenuButton(): JsxNode { const { appearance, label, scale, expanded } = this; @@ -533,5 +547,5 @@ export class ActionMenu extends LitElement { ); } - // #endregion + //#endregion } diff --git a/packages/calcite-components/src/components/action-pad/action-pad.e2e.ts b/packages/calcite-components/src/components/action-pad/action-pad.e2e.ts index 4da67f0ec1a..b02be0c9239 100755 --- a/packages/calcite-components/src/components/action-pad/action-pad.e2e.ts +++ b/packages/calcite-components/src/components/action-pad/action-pad.e2e.ts @@ -366,6 +366,27 @@ describe("calcite-action-pad", () => { } }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-action-pad"); + + const expandSpy = await page.spyOnEvent("calciteActionPadExpand"); + const collapseSpy = await page.spyOnEvent("calciteActionPadCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("theme", () => { describe("default", () => { themed("calcite-action-pad", { diff --git a/packages/calcite-components/src/components/action-pad/action-pad.tsx b/packages/calcite-components/src/components/action-pad/action-pad.tsx index 7b7d7ea2d86..177ea0b4235 100755 --- a/packages/calcite-components/src/components/action-pad/action-pad.tsx +++ b/packages/calcite-components/src/components/action-pad/action-pad.tsx @@ -71,7 +71,7 @@ export class ActionPad extends LitElement { /** When `true`, the expand-toggling behavior is disabled. */ @property({ reflect: true }) expandDisabled = false; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Indicates the layout of the component. */ @@ -118,6 +118,12 @@ export class ActionPad extends LitElement { //#region Events + /** Fires when the component's content area is collapsed. */ + calciteActionPadCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteActionPadExpand = createEvent({ cancelable: false }); + /** Fires when the `expanded` property is toggled. */ calciteActionPadToggle = createEvent({ cancelable: false }); @@ -154,6 +160,14 @@ export class ActionPad extends LitElement { if (changes.has("layout") && (this.hasUpdated || this.layout !== "vertical")) { this.updateGroups(); } + + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.calciteActionPadExpand.emit(); + } else { + this.calciteActionPadCollapse.emit(); + } + } } override disconnectedCallback(): void { diff --git a/packages/calcite-components/src/components/block-section/block-section.e2e.ts b/packages/calcite-components/src/components/block-section/block-section.e2e.ts index 5b58102b565..0a3f2b45ed8 100644 --- a/packages/calcite-components/src/components/block-section/block-section.e2e.ts +++ b/packages/calcite-components/src/components/block-section/block-section.e2e.ts @@ -261,6 +261,27 @@ describe("calcite-block-section", () => { expect(toggle.getAttribute("aria-expanded")).toBe("false"); } + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-block-section"); + + const expandSpy = await page.spyOnEvent("calciteBlockSectionExpand"); + const collapseSpy = await page.spyOnEvent("calciteBlockSectionCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("theme", () => { describe("default", () => { themed( diff --git a/packages/calcite-components/src/components/block-section/block-section.tsx b/packages/calcite-components/src/components/block-section/block-section.tsx index f0b7e63bbe8..2c7124c5694 100644 --- a/packages/calcite-components/src/components/block-section/block-section.tsx +++ b/packages/calcite-components/src/components/block-section/block-section.tsx @@ -1,4 +1,5 @@ // @ts-strict-ignore +import { PropertyValues } from "lit"; import { LitElement, property, createEvent, Fragment, h, method, JsxNode } from "@arcgis/lumina"; import { isActivationKey } from "../../utils/key"; import { FlipContext, Status } from "../interfaces"; @@ -40,7 +41,7 @@ export class BlockSection extends LitElement { //#region Public Properties - /** When `true`, the component is expanded to show child components. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** Specifies an icon to display at the end of the component. */ @@ -114,11 +115,31 @@ export class BlockSection extends LitElement { //#region Events + /** Fires when the component's content area is collapsed. */ + calciteBlockSectionCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteBlockSectionExpand = createEvent({ cancelable: false }); + /** Fires when the header has been clicked. */ calciteBlockSectionToggle = createEvent({ cancelable: false }); //#endregion + //#region Lifecycle + + override willUpdate(changes: PropertyValues): void { + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.calciteBlockSectionExpand.emit(); + } else { + this.calciteBlockSectionCollapse.emit(); + } + } + } + + //#endregion + //#region Private Methods private handleHeaderKeyDown(event: KeyboardEvent): void { diff --git a/packages/calcite-components/src/components/block/block.e2e.ts b/packages/calcite-components/src/components/block/block.e2e.ts index a968f63a933..e4e823ce2da 100644 --- a/packages/calcite-components/src/components/block/block.e2e.ts +++ b/packages/calcite-components/src/components/block/block.e2e.ts @@ -476,6 +476,27 @@ describe("calcite-block", () => { expect(article.getAttribute("aria-label")).toEqual(label); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-block"); + + const expandSpy = await page.spyOnEvent("calciteBlockExpand"); + const collapseSpy = await page.spyOnEvent("calciteBlockCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("translation support", () => { t9n("calcite-block"); }); diff --git a/packages/calcite-components/src/components/block/block.tsx b/packages/calcite-components/src/components/block/block.tsx index 77db2b9d197..b2edcebc574 100644 --- a/packages/calcite-components/src/components/block/block.tsx +++ b/packages/calcite-components/src/components/block/block.tsx @@ -102,7 +102,7 @@ export class Block extends LitElement implements InteractiveComponent, OpenClose */ @property({ reflect: true }) dragHandle = false; - /** When `true`, the component is expanded to show child components. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** @@ -220,12 +220,6 @@ export class Block extends LitElement implements InteractiveComponent, OpenClose //#region Events - /** - * - * @private - */ - calciteInternalBlockUpdateMoveToItems = createEvent({ cancelable: false }); - /** Fires when the component is requested to be closed and before the closing transition begins. */ calciteBlockBeforeClose = createEvent({ cancelable: false }); @@ -235,6 +229,12 @@ export class Block extends LitElement implements InteractiveComponent, OpenClose /** Fires when the component is closed and animation is complete. */ calciteBlockClose = createEvent({ cancelable: false }); + /** Fires when the component's content area is collapsed. */ + calciteBlockCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteBlockExpand = createEvent({ cancelable: false }); + /** Fires when the component is open and animation is complete. */ calciteBlockOpen = createEvent({ cancelable: false }); @@ -257,6 +257,12 @@ export class Block extends LitElement implements InteractiveComponent, OpenClose */ calciteBlockToggle = createEvent({ cancelable: false }); + /** + * + * @private + */ + calciteInternalBlockUpdateMoveToItems = createEvent({ cancelable: false }); + //#endregion //#region Lifecycle @@ -285,6 +291,14 @@ export class Block extends LitElement implements InteractiveComponent, OpenClose if (changes.has("sortHandleOpen") && (this.hasUpdated || this.sortHandleOpen !== false)) { this.sortHandleOpenHandler(); } + + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.calciteBlockExpand.emit(); + } else { + this.calciteBlockCollapse.emit(); + } + } } override updated(): void { diff --git a/packages/calcite-components/src/components/flow-item/flow-item.e2e.ts b/packages/calcite-components/src/components/flow-item/flow-item.e2e.ts index cdcee6bb940..544feccf9dc 100644 --- a/packages/calcite-components/src/components/flow-item/flow-item.e2e.ts +++ b/packages/calcite-components/src/components/flow-item/flow-item.e2e.ts @@ -423,6 +423,27 @@ describe("calcite-flow-item", () => { expect(await flowItem.getProperty("closed")).toBe(false); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-flow-item"); + + const expandSpy = await page.spyOnEvent("calciteFlowItemExpand"); + const collapseSpy = await page.spyOnEvent("calciteFlowItemCollapse"); + + item.setProperty("collapsed", true); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(0); + expect(collapseSpy).toHaveReceivedEventTimes(1); + + item.setProperty("collapsed", false); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("theme", () => { themed(html``, { "--calcite-flow-corner-radius": { diff --git a/packages/calcite-components/src/components/flow-item/flow-item.tsx b/packages/calcite-components/src/components/flow-item/flow-item.tsx index 78df6ae75db..064754503ff 100644 --- a/packages/calcite-components/src/components/flow-item/flow-item.tsx +++ b/packages/calcite-components/src/components/flow-item/flow-item.tsx @@ -188,6 +188,12 @@ export class FlowItem extends LitElement implements InteractiveComponent { /** Fires when the close button is clicked. */ calciteFlowItemClose = createEvent({ cancelable: false }); + /** Fires when the component's content area is collapsed. */ + calciteFlowItemCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteFlowItemExpand = createEvent({ cancelable: false }); + /** Fires when the content is scrolled. */ calciteFlowItemScroll = createEvent({ cancelable: false }); @@ -209,6 +215,13 @@ export class FlowItem extends LitElement implements InteractiveComponent { if (changes.has("selected") && (this.hasUpdated || this.selected !== false)) { this.calciteInternalFlowItemChange.emit(); } + if (changes.has("collapsed") && this.hasUpdated) { + if (this.collapsed) { + this.calciteFlowItemCollapse.emit(); + } else { + this.calciteFlowItemExpand.emit(); + } + } } override updated(): void { diff --git a/packages/calcite-components/src/components/list-item/list-item.e2e.ts b/packages/calcite-components/src/components/list-item/list-item.e2e.ts index d476f6d84ea..a120e118168 100755 --- a/packages/calcite-components/src/components/list-item/list-item.e2e.ts +++ b/packages/calcite-components/src/components/list-item/list-item.e2e.ts @@ -520,6 +520,27 @@ describe("calcite-list-item", () => { expect(icon).toBe(null); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-list-item"); + + const expandSpy = await page.spyOnEvent("calciteListItemExpand"); + const collapseSpy = await page.spyOnEvent("calciteListItemCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("themed", () => { describe(`selection-appearance="icon"`, () => { themed( diff --git a/packages/calcite-components/src/components/list-item/list-item.tsx b/packages/calcite-components/src/components/list-item/list-item.tsx index 52c29848928..4b1472e85dc 100644 --- a/packages/calcite-components/src/components/list-item/list-item.tsx +++ b/packages/calcite-components/src/components/list-item/list-item.tsx @@ -136,7 +136,7 @@ export class ListItem extends LitElement implements InteractiveComponent, Sortab */ @property({ reflect: true }) dragHandle = false; - /** When `true`, the item is expanded to show child components. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** @@ -338,6 +338,12 @@ export class ListItem extends LitElement implements InteractiveComponent, Sortab /** Fires when the close button is clicked. */ calciteListItemClose = createEvent({ cancelable: false }); + /** Fires when the component's content area is collapsed. */ + calciteListItemCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteListItemExpand = createEvent({ cancelable: false }); + /** Fires when the component is selected. */ calciteListItemSelect = createEvent({ cancelable: false }); @@ -401,10 +407,6 @@ export class ListItem extends LitElement implements InteractiveComponent, Sortab this.handleDisabledChange(); } - if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) { - this.handleExpandedChange(); - } - if (changes.has("selected") && (this.hasUpdated || this.selected !== false)) { this.handleSelectedChange(); } @@ -416,6 +418,15 @@ export class ListItem extends LitElement implements InteractiveComponent, Sortab if (changes.has("displayMode") && this.hasUpdated) { this.handleExpandableChange(this.defaultSlotEl.value); } + + if (changes.has("expanded") && this.hasUpdated) { + if (this.expanded) { + this.handleExpandedChange(); + this.calciteListItemExpand.emit(); + } else { + this.calciteListItemCollapse.emit(); + } + } } override updated(): void { diff --git a/packages/calcite-components/src/components/panel/panel.e2e.ts b/packages/calcite-components/src/components/panel/panel.e2e.ts index 342c7f0a54c..f466711f216 100644 --- a/packages/calcite-components/src/components/panel/panel.e2e.ts +++ b/packages/calcite-components/src/components/panel/panel.e2e.ts @@ -744,6 +744,27 @@ describe("calcite-panel", () => { }); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-panel"); + + const expandSpy = await page.spyOnEvent("calcitePanelExpand"); + const collapseSpy = await page.spyOnEvent("calcitePanelCollapse"); + + item.setProperty("collapsed", true); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(0); + expect(collapseSpy).toHaveReceivedEventTimes(1); + + item.setProperty("collapsed", false); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("theme", () => { themed( html`): void { + if (changes.has("collapsed") && this.hasUpdated) { + if (this.collapsed) { + this.calcitePanelCollapse.emit(); + } else { + this.calcitePanelExpand.emit(); + } + } + } + override updated(): void { updateHostInteraction(this); } diff --git a/packages/calcite-components/src/components/shell-panel/shell-panel.e2e.ts b/packages/calcite-components/src/components/shell-panel/shell-panel.e2e.ts index 431ed75b6a4..eba1605e203 100644 --- a/packages/calcite-components/src/components/shell-panel/shell-panel.e2e.ts +++ b/packages/calcite-components/src/components/shell-panel/shell-panel.e2e.ts @@ -565,6 +565,27 @@ describe("calcite-shell-panel", () => { t9n("calcite-shell-panel"); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-shell-panel"); + + const expandSpy = await page.spyOnEvent("calciteShellPanelExpand"); + const collapseSpy = await page.spyOnEvent("calciteShellPanelCollapse"); + + item.setProperty("collapsed", true); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(0); + expect(collapseSpy).toHaveReceivedEventTimes(1); + + item.setProperty("collapsed", false); + await page.waitForChanges(); + expect(await item.getProperty("collapsed")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("themed", () => { describe("default", () => { themed(html``, { diff --git a/packages/calcite-components/src/components/shell-panel/shell-panel.tsx b/packages/calcite-components/src/components/shell-panel/shell-panel.tsx index b23bb8bd108..bc8e8966acd 100755 --- a/packages/calcite-components/src/components/shell-panel/shell-panel.tsx +++ b/packages/calcite-components/src/components/shell-panel/shell-panel.tsx @@ -134,6 +134,12 @@ export class ShellPanel extends LitElement { /** @private */ calciteInternalShellPanelResizeStart = createEvent({ cancelable: false }); + /** Fires when the component's content area is collapsed. */ + calciteShellPanelCollapse = createEvent({ cancelable: false }); + + /** Fires when the component's content area is expanded. */ + calciteShellPanelExpand = createEvent({ cancelable: false }); + //#endregion //#region Lifecycle @@ -146,6 +152,13 @@ export class ShellPanel extends LitElement { if (changes.has("layout") && (this.hasUpdated || this.layout !== "vertical")) { this.setActionBarsLayout(this.actionBars); } + if (changes.has("collapsed") && this.hasUpdated) { + if (this.collapsed) { + this.calciteShellPanelCollapse.emit(); + } else { + this.calciteShellPanelExpand.emit(); + } + } } override disconnectedCallback(): void { diff --git a/packages/calcite-components/src/components/tree-item/tree-item.e2e.ts b/packages/calcite-components/src/components/tree-item/tree-item.e2e.ts index ef1ed49ab0d..22630b4555a 100644 --- a/packages/calcite-components/src/components/tree-item/tree-item.e2e.ts +++ b/packages/calcite-components/src/components/tree-item/tree-item.e2e.ts @@ -429,6 +429,27 @@ describe("calcite-tree-item", () => { expect(itemBounds.height).not.toBe(0); }); + it("should emit expanded/collapsed events when toggled", async () => { + const page = await newE2EPage(); + await page.setContent(html``); + const item = await page.find("calcite-tree-item"); + + const expandSpy = await page.spyOnEvent("calciteTreeItemExpand"); + const collapseSpy = await page.spyOnEvent("calciteTreeItemCollapse"); + + item.setProperty("expanded", true); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(true); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(0); + + item.setProperty("expanded", false); + await page.waitForChanges(); + expect(await item.getProperty("expanded")).toBe(false); + expect(expandSpy).toHaveReceivedEventTimes(1); + expect(collapseSpy).toHaveReceivedEventTimes(1); + }); + describe("themed", () => { describe(`selection-mode="none"`, () => { themed( diff --git a/packages/calcite-components/src/components/tree-item/tree-item.tsx b/packages/calcite-components/src/components/tree-item/tree-item.tsx index 5a43d106015..ae06db6639b 100644 --- a/packages/calcite-components/src/components/tree-item/tree-item.tsx +++ b/packages/calcite-components/src/components/tree-item/tree-item.tsx @@ -35,13 +35,13 @@ declare global { * @slot actions-end - A slot for adding actions to the end of the component. It is recommended to use two or fewer actions. */ export class TreeItem extends LitElement implements InteractiveComponent { - // #region Static Members + //#region Static Members static override styles = styles; - // #endregion + //#endregion - // #region Private Properties + //#region Private Properties private actionSlotWrapper = createRef(); @@ -53,9 +53,9 @@ export class TreeItem extends LitElement implements InteractiveComponent { private userChangedValue = false; - // #endregion + //#endregion - // #region State Properties + //#region State Properties @state() private hasEndActions = false; @@ -67,9 +67,9 @@ export class TreeItem extends LitElement implements InteractiveComponent { */ @state() updateAfterInitialRender = false; - // #endregion + //#endregion - // #region Public Properties + //#region Public Properties /** @private */ @property({ reflect: true }) depth = -1; @@ -77,7 +77,7 @@ export class TreeItem extends LitElement implements InteractiveComponent { /** When `true`, interaction is prevented and the component is displayed with lower opacity. */ @property({ reflect: true }) disabled = false; - /** When `true`, the component is expanded. */ + /** When `true`, expands the component and its contents. */ @property({ reflect: true }) expanded = false; /** @private */ @@ -116,16 +116,22 @@ export class TreeItem extends LitElement implements InteractiveComponent { /** @private */ @property({ reflect: true }) selectionMode: SelectionMode; - // #endregion + //#endregion - // #region Events + //#region Events /** @private */ calciteInternalTreeItemSelect = createEvent({ cancelable: false }); - // #endregion + /** Fires when the component's content area is collapsed. */ + calciteTreeItemCollapse = createEvent({ cancelable: false }); - // #region Lifecycle + /** Fires when the component's content area is expanded. */ + calciteTreeItemExpand = createEvent({ cancelable: false }); + + //#endregion + + //#region Lifecycle constructor() { super(); @@ -147,8 +153,17 @@ export class TreeItem extends LitElement implements InteractiveComponent { To account for this semantics change, the checks for (this.hasUpdated || value != defaultValue) was added in this method Please refactor your code to reduce the need for this check. Docs: https://qawebgis.esri.com/arcgis-components/?path=/docs/lumina-transition-from-stencil--docs#watching-for-property-changes */ - if (changes.has("expanded") && (this.hasUpdated || this.expanded !== false)) { - this.updateChildTree(); + if (changes.has("expanded")) { + if (this.hasUpdated || this.expanded !== false) { + this.updateChildTree(); + } + if (this.hasUpdated) { + if (this.expanded) { + this.calciteTreeItemExpand.emit(); + } else { + this.calciteTreeItemCollapse.emit(); + } + } } if (changes.has("selected") && (this.hasUpdated || this.selected !== false)) { @@ -168,9 +183,10 @@ export class TreeItem extends LitElement implements InteractiveComponent { this.updateAncestorTree(); } - // #endregion + //#endregion + + //#region Private Methods - // #region Private Methods private handleSelectedChange(value: boolean): void { if (this.selectionMode === "ancestors" && !this.userChangedValue) { if (value) { @@ -342,9 +358,9 @@ export class TreeItem extends LitElement implements InteractiveComponent { } } - // #endregion + //#endregion - // #region Rendering + //#region Rendering override render(): JsxNode { const rtl = getElementDir(this.el) === "rtl"; @@ -494,5 +510,5 @@ export class TreeItem extends LitElement implements InteractiveComponent { ); } - // #endregion + //#endregion }