diff --git a/packages/react/src/component-tests/IcToggleButton/IcToggleButton.cy.tsx b/packages/react/src/component-tests/IcToggleButton/IcToggleButton.cy.tsx index c8611755ed..0ab056ae20 100644 --- a/packages/react/src/component-tests/IcToggleButton/IcToggleButton.cy.tsx +++ b/packages/react/src/component-tests/IcToggleButton/IcToggleButton.cy.tsx @@ -13,6 +13,7 @@ import { } from "../utils/constants"; import { setThresholdBasedOnEnv } from "../../../cypress/utils/helpers"; import { + ConditionalBadge, Dark, IconPlacementRight, IconPlacementTop, @@ -94,6 +95,14 @@ describe("IcToggleButton end-to-end tests", () => { cy.clickOnButton(IC_TOGGLE_BUTTON_SELECTOR); cy.get(WIN_CONSOLE_SPY).should(NOT_BE_CALLED_ONCE); }); + + it("should render elements passed into slots after initial render", () => { + mount(); + cy.checkHydrated(IC_TOGGLE_BUTTON_SELECTOR); + cy.get("ic-badge").should("not.exist"); + cy.get("button").click(); + cy.get("ic-badge").should("be.visible"); + }); }); describe("IcToggleButton visual regression and a11y tests", () => { diff --git a/packages/react/src/component-tests/IcToggleButton/IcToggleButtonTestData.tsx b/packages/react/src/component-tests/IcToggleButton/IcToggleButtonTestData.tsx index 03a43817f5..ee330d9d77 100644 --- a/packages/react/src/component-tests/IcToggleButton/IcToggleButtonTestData.tsx +++ b/packages/react/src/component-tests/IcToggleButton/IcToggleButtonTestData.tsx @@ -1,5 +1,5 @@ -import React, { ReactElement } from "react"; -import { IcToggleButton } from "../../components"; +import React, { ReactElement, useState } from "react"; +import { IcBadge, IcToggleButton } from "../../components"; import { SlottedSVG } from "../../react-component-lib/slottedSVG"; const ReusableSlottedIcon = (): ReactElement => ( @@ -165,3 +165,29 @@ export const Light = (): ReactElement => { ); }; + +export const ConditionalBadge = (): ReactElement => { + const [count, setCount] = useState(0); + const incrementCount = () => setCount((previous) => previous + 1); + + return ( +
+ + Button with badge + {count > 0 && ( + + )} + + +
+ ); +}; diff --git a/packages/react/src/stories/ic-button.stories.mdx b/packages/react/src/stories/ic-button.stories.mdx index 3a8fc2aa6a..2df400365a 100644 --- a/packages/react/src/stories/ic-button.stories.mdx +++ b/packages/react/src/stories/ic-button.stories.mdx @@ -6,7 +6,7 @@ import { Description, } from "@storybook/addon-docs"; import { useRef } from "react"; -import { IcButton, IcPopoverMenu, IcMenuItem } from "../components"; +import { IcButton, IcPopoverMenu, IcMenuItem, IcBadge } from "../components"; import readme from "../../../web-components/src/components/ic-button/readme.md"; import { SlottedSVG } from "../react-component-lib/slottedSVG"; import { iconProps } from "../component-tests/IcButton/IcButtonTestData" @@ -24,6 +24,8 @@ export const defaultArgs = { size: "default", variant: "secondary", fullWidth: false, + hasIcon: true, + hasBadge: false, }; ### Primary @@ -1150,6 +1152,12 @@ export const IconBtnGroup = (iconProps) => { fullWidth: { control: { type: "boolean" }, }, + hasIcon: { + control: { type: "boolean" }, + }, + hasBadge: { + control: { type: "boolean" }, + }, }} name="Playground with icon" > @@ -1163,7 +1171,7 @@ export const IconBtnGroup = (iconProps) => { fullWidth={args.fullWidth} > {args.message} - { > - + } + {args.hasBadge && } )} diff --git a/packages/react/src/stories/ic-toggle-button.stories.mdx b/packages/react/src/stories/ic-toggle-button.stories.mdx index cf737fd1df..da772fb5e7 100644 --- a/packages/react/src/stories/ic-toggle-button.stories.mdx +++ b/packages/react/src/stories/ic-toggle-button.stories.mdx @@ -259,6 +259,8 @@ export const defaultWithIconArgs = { iconPlacement: "left", toggleChecked: false, accessibleLabel: "Custom Button Ally Label", + hasIcon: true, + hasBadge: false, }; @@ -283,6 +285,12 @@ export const defaultWithIconArgs = { fullWidth: { control: { type: "boolean" }, }, + hasIcon: { + control: { type: "boolean" }, + }, + hasBadge: { + control: { type: "boolean"}, + } }} name="Playground - default with icon" > @@ -299,7 +307,7 @@ export const defaultWithIconArgs = { toggleChecked={args.toggleChecked} iconPlacement={args.iconPlacement} > - - + } + {args.hasBadge && } )} diff --git a/packages/web-components/src/components/ic-button/ic-button.tsx b/packages/web-components/src/components/ic-button/ic-button.tsx index f614b7dcc7..511b9b5068 100644 --- a/packages/web-components/src/components/ic-button/ic-button.tsx +++ b/packages/web-components/src/components/ic-button/ic-button.tsx @@ -300,7 +300,6 @@ export class Button { componentDidLoad(): void { this.updateTheme(); - if (typeof MutationObserver !== "undefined") { if (this.describedById) { this.mutationObserver = new MutationObserver(this.mutationCallback); @@ -316,6 +315,7 @@ export class Button { ); this.hostMutationObserver.observe(this.el, { attributes: true, + childList: true, }); } } @@ -435,7 +435,10 @@ export class Button { // triggered when attributes of host element change private hostMutationCallback = (mutationList: MutationRecord[]): void => { let forceComponentUpdate = false; - mutationList.forEach(({ attributeName }) => { + mutationList.forEach(({ attributeName, type }) => { + if (type === "childList") { + forceComponentUpdate = true; + } const attribute = this.el.getAttribute(attributeName); if (attributeName === "title") this.title = attribute; else if (attributeName === "aria-label") this.ariaLabel = attribute; diff --git a/packages/web-components/src/components/ic-toggle-button/ic-toggle-button.tsx b/packages/web-components/src/components/ic-toggle-button/ic-toggle-button.tsx index a70484141e..2f43df8969 100644 --- a/packages/web-components/src/components/ic-toggle-button/ic-toggle-button.tsx +++ b/packages/web-components/src/components/ic-toggle-button/ic-toggle-button.tsx @@ -7,6 +7,7 @@ import { EventEmitter, Listen, h, + forceUpdate, } from "@stencil/core"; import { isSlotUsed, @@ -29,6 +30,7 @@ import { IcSizes, IcThemeForeground } from "../../utils/types"; }) export class ToggleButton { private iconPosition: "left" | "right" | "top"; + private hostMutationObserver: MutationObserver = null; @Element() el: HTMLIcToggleButtonElement; @@ -108,8 +110,29 @@ export class ToggleButton { ], "Toggle button" ); + if (typeof MutationObserver !== "undefined") { + this.hostMutationObserver = new MutationObserver( + this.hostMutationCallback + ); + this.hostMutationObserver.observe(this.el, { + childList: true, + }); + } } + disconnectedCallback(): void { + if ( + this.hostMutationObserver !== null && + this.hostMutationObserver !== undefined + ) { + this.hostMutationObserver.disconnect(); + } + } + + private hostMutationCallback = (): void => { + forceUpdate(this); + }; + @Listen("click", { capture: true }) handleHostClick(e: Event): void { if (this.disabled) {