diff --git a/common/changes/@itwin/appui-react/change-selection-count-padding_2024-07-26-16-49.json b/common/changes/@itwin/appui-react/change-selection-count-padding_2024-07-26-16-49.json new file mode 100644 index 00000000000..8f52a9b4ae0 --- /dev/null +++ b/common/changes/@itwin/appui-react/change-selection-count-padding_2024-07-26-16-49.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/appui-react", + "comment": "Adjust SelectionCount padding values.", + "type": "none" + } + ], + "packageName": "@itwin/appui-react" +} \ No newline at end of file diff --git a/common/changes/@itwin/appui-react/fix-893_2024-07-31-08-02.json b/common/changes/@itwin/appui-react/fix-893_2024-07-31-08-02.json new file mode 100644 index 00000000000..f83dcd14ae1 --- /dev/null +++ b/common/changes/@itwin/appui-react/fix-893_2024-07-31-08-02.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/appui-react", + "comment": "Fix an issue where styles of complex shorthand properties were not copied correctly to a popout window.", + "type": "none" + } + ], + "packageName": "@itwin/appui-react" +} \ No newline at end of file diff --git a/common/changes/@itwin/appui-react/link-discussions_2024-07-31-12-45.json b/common/changes/@itwin/appui-react/link-discussions_2024-07-31-12-45.json new file mode 100644 index 00000000000..3393c35368c --- /dev/null +++ b/common/changes/@itwin/appui-react/link-discussions_2024-07-31-12-45.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/appui-react", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/appui-react" +} \ No newline at end of file diff --git a/docs/changehistory/NextVersion.md b/docs/changehistory/NextVersion.md index 1eda19c281b..5e1f7a6ca6c 100644 --- a/docs/changehistory/NextVersion.md +++ b/docs/changehistory/NextVersion.md @@ -219,15 +219,17 @@ Table of contents: - Bump `AccuDrawWidget`, `SheetNavigationAid`, `StandardRotationNavigationAid` components to `@public`. [#888](https://github.com/iTwin/appui/pull/888) - No more transitions when toggling themes. [#905](https://github.com/iTwin/appui/pull/905) - Updated `ToolSettingsPopup` to not rely on event propagation for cancellation. [#928](https://github.com/iTwin/appui/pull/928) +- Adjusted `SelectionCount` styling to improve its visuals in various scenarios. [#936](https://github.com/iTwin/appui/pull/936) ### Fixes - Fixed `AccuDrawInputField` to correctly specify keyboard event modifiers in `UiFramework.keyboardShortcuts.processKey()`. [#894](https://github.com/iTwin/appui/pull/894) - Fixed icon alignment and warning status color of notification manager in `MessageCenterField` component. [#901](https://github.com/iTwin/appui/pull/901) - Fixed the unintentional "flying-in" of floating elements like Tooltips and ComboBox menus when the page first loads. [#905](https://github.com/iTwin/appui/pull/905) +- Fixed standard content tools throwing uncaught exception with transient elements. [#934](https://github.com/iTwin/appui/pull/934) - Fixed `ToolAssistanceField` icon size. [#937](https://github.com/iTwin/appui/pull/937) - Fixed [mixed-decls](https://sass-lang.com/documentation/breaking-changes/mixed-decls/) Sass warnings. [#939](https://github.com/iTwin/appui/pull/939) -- Fixed standard content tools throwing uncaught exception with transient elements. [#934](https://github.com/iTwin/appui/pull/934) +- Fixed an issue where styles of complex shorthand properties were not copied correctly to a popout window. [#940](https://github.com/iTwin/appui/pull/940) ## @itwin/components-react diff --git a/e2e-tests/tests/popout-widget.test.ts b/e2e-tests/tests/popout-widget.test.ts index 251b86f2231..66702e058fd 100644 --- a/e2e-tests/tests/popout-widget.test.ts +++ b/e2e-tests/tests/popout-widget.test.ts @@ -2,7 +2,7 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { test, expect, Locator, BrowserContext } from "@playwright/test"; +import { test, expect, Locator } from "@playwright/test"; import assert from "assert"; import { WidgetState, @@ -23,7 +23,7 @@ test.describe("popout widget", () => { await page.goto(`${baseURL}?frontstage=appui-test-providers:WidgetApi`); }); - test("should popout a widget", async ({ context, page }) => { + test("should popout a widget", async ({ page }) => { const widget = floatingWidgetLocator({ page, id: "appui-test-providers:ViewAttributesWidget", @@ -31,14 +31,14 @@ test.describe("popout widget", () => { const tab = tabLocator(page, "View Attributes"); await expect(tab).toBeVisible(); - const popoutPage = await popoutWidget(context, widget); + const popoutPage = await popoutWidget(widget); await expect(popoutPage).toHaveTitle(/View Attributes/); await expect(tab).not.toBeVisible(); expect(await popoutPage.title()).toEqual("View Attributes"); }); - test("should apply styles to popout", async ({ context, page }) => { + test("should apply styles to popout", async ({ page }) => { const widget = floatingWidgetLocator({ page, id: "appui-test-providers:ViewAttributesWidget", @@ -46,18 +46,17 @@ test.describe("popout widget", () => { const tab = tabLocator(page, "View Attributes"); await expect(tab).toBeVisible(); - const popoutPage = await popoutWidget(context, widget); + const popoutPage = await popoutWidget(widget); await expect(popoutPage.locator("body")).toHaveScreenshot(); }); test("should float a popout widget (after frontstage change)", async ({ - context, page, }) => { const tab = tabLocator(page, "View Attributes"); const widget = widgetLocator({ tab }); - const popoutPage = await popoutWidget(context, widget); + const popoutPage = await popoutWidget(widget); await expect.poll(async () => popoutPage.isClosed()).toBe(false); await openFrontstage(page, "appui-test-app:main-stage"); @@ -77,7 +76,7 @@ test.describe("popout widget", () => { const tab = tabLocator(page, "WT-2"); const widget = widgetLocator({ tab }); - const popoutPage = await popoutWidget(context, widget); + const popoutPage = await popoutWidget(widget); await expect.poll(async () => popoutPage.isClosed()).toBe(false); await openFrontstage(page, "appui-test-app:main-stage"); @@ -90,12 +89,12 @@ test.describe("popout widget", () => { await expect(locator).toBeVisible(); }); - test("should maintain popout widget bounds", async ({ context, page }) => { + test("should maintain popout widget bounds", async ({ page }) => { const tab = tabLocator(page, "View Attributes"); const widget = widgetLocator({ tab }); // Popout the widget w/ default size. - let popoutPage = await popoutWidget(context, widget); + let popoutPage = await popoutWidget(widget); await expect(popoutPage).toHaveTitle(/View Attributes/); const size = popoutPage.viewportSize(); @@ -114,7 +113,7 @@ test.describe("popout widget", () => { await tab.click(); await expect(tab).toHaveClass(/nz-active/); - popoutPage = await popoutWidget(context, widget); + popoutPage = await popoutWidget(widget); expect(popoutPage.viewportSize()).toEqual({ height: 400, width: 300, @@ -128,7 +127,7 @@ test.describe("popout widget", () => { const tab = tabLocator(page, "View Attributes"); const widget = widgetLocator({ tab }); - let popoutPage = await popoutWidget(context, widget); + let popoutPage = await popoutWidget(widget); await expect(popoutPage).toHaveTitle(/View Attributes/); // Update widget size and close the popout. @@ -150,24 +149,20 @@ test.describe("popout widget", () => { await page.reload(); - popoutPage = await popoutWidget(context, widget); + popoutPage = await popoutWidget(widget); expect(popoutPage.viewportSize()).toEqual({ height: 400, width: 300, }); }); - test("should close a popout (when floating a widget)", async ({ - context, - page, - }) => { + test("should close a popout (when floating a widget)", async ({ page }) => { const widget = floatingWidgetLocator({ page, id: "appui-test-providers:ViewAttributesWidget", }); - const popoutPage = await popoutWidget(context, widget); - await popoutPage.waitForLoadState(); // TODO: childWindow is only added after 'load' event + const popoutPage = await popoutWidget(widget); await expect.poll(async () => popoutPage.isClosed()).toBe(false); await setWidgetState( @@ -178,10 +173,7 @@ test.describe("popout widget", () => { await expect.poll(async () => popoutPage.isClosed()).toBe(true); }); - test("should unmount when popped out widget is closed", async ({ - context, - page, - }) => { + test("should unmount when popped out widget is closed", async ({ page }) => { const id = "appui-test-providers:PopoutMountUnmountWidget"; const widget = floatingWidgetLocator({ page, @@ -189,7 +181,7 @@ test.describe("popout widget", () => { }); await expect(widget).toBeVisible(); const widgetLifecycle = trackWidgetLifecycle(page, id); - const popoutPage = await popoutWidget(context, widget); + const popoutPage = await popoutWidget(widget); await expect.poll(async () => popoutPage.isClosed()).toBe(false); await popoutPage.close(); @@ -199,7 +191,20 @@ test.describe("popout widget", () => { }); }); -async function popoutWidget(context: BrowserContext, widget: Locator) { +test("should copy styles", async ({ baseURL, page }) => { + assert(baseURL); + await page.goto(`${baseURL}?frontstage=appui-test-app:TestPopout`); + + const tab = tabLocator(page, "Widget 1"); + const widget = widgetLocator({ tab }); + + const popoutPage = await popoutWidget(widget); + const borders = popoutPage.locator("#border-test"); + await expect(borders).toHaveScreenshot(); +}); + +async function popoutWidget(widget: Locator) { + const context = widget.page().context(); const popoutButton = popoutButtonLocator(widget); const [popoutPage] = await Promise.all([ context.waitForEvent("page"), diff --git a/e2e-tests/tests/popout-widget.test.ts-snapshots/should-copy-styles-1-chromium-linux.png b/e2e-tests/tests/popout-widget.test.ts-snapshots/should-copy-styles-1-chromium-linux.png new file mode 100644 index 00000000000..9f570f0977b Binary files /dev/null and b/e2e-tests/tests/popout-widget.test.ts-snapshots/should-copy-styles-1-chromium-linux.png differ diff --git a/e2e-tests/tests/settings/ui-settings-page.test.ts-snapshots/ui-settings-page-test-1-chromium-linux.png b/e2e-tests/tests/settings/ui-settings-page.test.ts-snapshots/ui-settings-page-test-1-chromium-linux.png index 1b836bfd56f..33f47fae19d 100644 Binary files a/e2e-tests/tests/settings/ui-settings-page.test.ts-snapshots/ui-settings-page-test-1-chromium-linux.png and b/e2e-tests/tests/settings/ui-settings-page.test.ts-snapshots/ui-settings-page-test-1-chromium-linux.png differ diff --git a/e2e-tests/tests/stage-panel.test.ts b/e2e-tests/tests/stage-panel.test.ts index cdf2b595189..7de9b7db59d 100644 --- a/e2e-tests/tests/stage-panel.test.ts +++ b/e2e-tests/tests/stage-panel.test.ts @@ -65,7 +65,7 @@ test.describe("WidgetApi", () => { test("should initialize defaults", async ({ baseURL, page }) => { assert(baseURL); - await page.goto(`${baseURL}?frontstage=appui-test-providers:TestFrontstage`); + await page.goto(`${baseURL}?frontstage=appui-test-app:TestPanel`); const panel = panelLocator({ page, side: "left" }); await expect(panel).toBeVisible(); @@ -74,9 +74,7 @@ test("should initialize defaults", async ({ baseURL, page }) => { test("should initialize size", async ({ baseURL, page }) => { assert(baseURL); - await page.goto( - `${baseURL}?frontstage=appui-test-providers:TestFrontstage&size=500` - ); + await page.goto(`${baseURL}?frontstage=appui-test-app:TestPanel&size=500`); const panel = panelLocator({ page, side: "left" }); await expect(panel).toBeVisible(); @@ -86,7 +84,7 @@ test("should initialize size", async ({ baseURL, page }) => { test("should initialize minimized", async ({ baseURL, page }) => { assert(baseURL); await page.goto( - `${baseURL}?frontstage=appui-test-providers:TestFrontstage&defaultState=${StagePanelState.Minimized}` + `${baseURL}?frontstage=appui-test-app:TestPanel&defaultState=${StagePanelState.Minimized}` ); const panel = panelLocator({ page, side: "left" }); @@ -97,9 +95,7 @@ test("should initialize minimized", async ({ baseURL, page }) => { test("should initialize resizable", async ({ baseURL, page }) => { assert(baseURL); - await page.goto( - `${baseURL}?frontstage=appui-test-providers:TestFrontstage&resizable=0` - ); + await page.goto(`${baseURL}?frontstage=appui-test-app:TestPanel&resizable=0`); const panel = panelLocator({ page, side: "left" }); const handle = handleLocator(panel); @@ -108,7 +104,7 @@ test("should initialize resizable", async ({ baseURL, page }) => { test("should resize (single panel)", async ({ baseURL, page }) => { assert(baseURL); - await page.goto(`${baseURL}?frontstage=appui-test-providers:TestFrontstage`); + await page.goto(`${baseURL}?frontstage=appui-test-app:TestPanel`); const panel = panelLocator({ page, side: "left" }); const handle = handleLocator(panel); diff --git a/test-apps/appui-test-app/appui-test-providers/src/appui-test-providers.ts b/test-apps/appui-test-app/appui-test-providers/src/appui-test-providers.ts index bf535d8e0d5..06b483e42a9 100644 --- a/test-apps/appui-test-app/appui-test-providers/src/appui-test-providers.ts +++ b/test-apps/appui-test-app/appui-test-providers/src/appui-test-providers.ts @@ -23,7 +23,6 @@ export * from "./ui/frontstages/CustomFrontstageProvider"; export * from "./ui/frontstages/PopoutWindowsFrontstage"; export * from "./ui/frontstages/registerCustomFrontstage"; export * from "./ui/frontstages/SynchronizedFloatingViewport"; -export * from "./ui/frontstages/TestFrontstageProvider"; export * from "./ui/frontstages/WidgetApiStage"; export * from "./ui/ViewportContent"; diff --git a/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPanelFrontstage.tsx b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPanelFrontstage.tsx new file mode 100644 index 00000000000..595d957583f --- /dev/null +++ b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPanelFrontstage.tsx @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import * as React from "react"; +import { Frontstage } from "@itwin/appui-react"; +import { createTestFrontstage } from "./createTestFrontstage"; + +/** Used in e2e tests to test different panel configurations. */ +export const createTestPanelFrontstage = () => { + { + const urlParams = new URLSearchParams(window.location.search); + const size = urlParams.get("size"); + const defaultState = urlParams.get("defaultState"); + const resizable = urlParams.get("resizable"); + + const frontstage = createTestFrontstage({ + id: "appui-test-app:TestPanel", + }); + + return { + ...frontstage, + leftPanel: { + sizeSpec: size ? Number(size) : undefined, + defaultState: defaultState ? Number(defaultState) : undefined, + resizable: resizable ? Boolean(Number(resizable)) : undefined, + sections: { + start: [ + { + id: "widget-1", + label: "Widget 1", + content: ( + <> + Frontstage provided widget: widget-1 + + ), + }, + ], + }, + }, + } satisfies Frontstage; + } +}; diff --git a/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.scss b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.scss new file mode 100644 index 00000000000..0b64c0b4c0a --- /dev/null +++ b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.scss @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +#border-test { + --border-color: red; + border: 5px solid var(--border-color); + border-bottom: 5px solid blue; + height: 20px; + width: 100px; +} diff --git a/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.tsx b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.tsx new file mode 100644 index 00000000000..641baa2777f --- /dev/null +++ b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/TestPopoutFrontstage.tsx @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Bentley Systems, Incorporated. All rights reserved. + * See LICENSE.md in the project root for license terms and full copyright notice. + *--------------------------------------------------------------------------------------------*/ +import "./TestPopoutFrontstage.scss"; +import * as React from "react"; +import { Frontstage } from "@itwin/appui-react"; +import { createTestFrontstage } from "./createTestFrontstage"; + +export const createTestPopoutFrontstage = () => { + { + const frontstage = createTestFrontstage({ + id: "appui-test-app:TestPopout", + }); + + return { + ...frontstage, + leftPanel: { + sections: { + start: [ + { + id: "widget-1", + label: "Widget 1", + canPopout: true, + content: ( + <> +
Widget 1 content
+
+ + ), + }, + ], + }, + }, + } satisfies Frontstage; + } +}; + +(() => { + const sheet = new CSSStyleSheet(); + // Shorthand `border` property from `adoptedStyleSheets` will not be copied to a popout widget correctly. + sheet.replaceSync(` + #border-test { + --border-top-color: yellow; + border-top: 5px solid var(--border-top-color); + border-right: 5px solid green; + } + `); + document.adoptedStyleSheets.push(sheet); +})(); diff --git a/test-apps/appui-test-app/appui-test-providers/src/ui/frontstages/TestFrontstageProvider.tsx b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/createTestFrontstage.tsx similarity index 64% rename from test-apps/appui-test-app/appui-test-providers/src/ui/frontstages/TestFrontstageProvider.tsx rename to test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/createTestFrontstage.tsx index 80d72cbb72e..fc2879f7818 100644 --- a/test-apps/appui-test-app/appui-test-providers/src/ui/frontstages/TestFrontstageProvider.tsx +++ b/test-apps/appui-test-app/standalone/src/frontend/appui/frontstages/createTestFrontstage.tsx @@ -3,13 +3,16 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import * as React from "react"; -import { ContentGroup } from "@itwin/appui-react"; +import { ContentGroup, Frontstage } from "@itwin/appui-react"; import { StandardContentLayouts } from "@itwin/appui-abstract"; +interface CreateTestFrontstageArgs { + id: string; +} + /** Used in e2e tests to test different configurations. */ -export const testFrontstageProvider = (() => { +export const createTestFrontstage = ({ id }: CreateTestFrontstageArgs) => { { - const id = "appui-test-providers:TestFrontstage"; const contentGroup = new ContentGroup({ id: "test-group", layout: StandardContentLayouts.singleView, @@ -33,32 +36,22 @@ export const testFrontstageProvider = (() => { ], }); - const urlParams = new URLSearchParams(window.location.search); - const size = urlParams.get("size"); - const defaultState = urlParams.get("defaultState"); - const resizable = urlParams.get("resizable"); return { id, version: Math.random(), contentGroup, leftPanel: { - sizeSpec: size ? Number(size) : undefined, - defaultState: defaultState ? Number(defaultState) : undefined, - resizable: resizable ? Boolean(Number(resizable)) : undefined, sections: { start: [ { id: "widget-1", label: "Widget 1", - content: ( - <> - Frontstage provided widget: widget-1 - - ), + canPopout: true, + content: <>Widget 1 content, }, ], }, }, - }; + } satisfies Frontstage; } -})(); +}; diff --git a/test-apps/appui-test-app/standalone/src/frontend/index.tsx b/test-apps/appui-test-app/standalone/src/frontend/index.tsx index 596f2040226..92b2da0ea69 100644 --- a/test-apps/appui-test-app/standalone/src/frontend/index.tsx +++ b/test-apps/appui-test-app/standalone/src/frontend/index.tsx @@ -102,7 +102,6 @@ import { previewFeaturesToggleProvider, registerCustomFrontstage, SynchronizedFloatingViewportStage, - testFrontstageProvider, WidgetApiStage, WidgetContentProvider, } from "@itwin/appui-test-providers"; @@ -122,6 +121,8 @@ import { createElementStackingFrontstage, createElementStackingProvider, } from "./appui/frontstages/ElementStacking"; +import { createTestPanelFrontstage } from "./appui/frontstages/TestPanelFrontstage"; +import { createTestPopoutFrontstage } from "./appui/frontstages/TestPopoutFrontstage"; // Initialize my application gateway configuration for the frontend RpcConfiguration.developmentMode = true; @@ -278,6 +279,7 @@ export class SampleAppIModelApp { public static async initialize() { // eslint-disable-next-line deprecation/deprecation await UiFramework.initialize(undefined, undefined); + UiFramework.visibility.autoHideUi = false; IModelApp.toolAdmin.defaultToolId = SelectionTool.toolId; @@ -389,7 +391,8 @@ export class SampleAppIModelApp { CustomContentFrontstage.register(AppUiTestProviders.localizationNamespace); WidgetApiStage.register(AppUiTestProviders.localizationNamespace); ContentLayoutStage.register(AppUiTestProviders.localizationNamespace); - UiFramework.frontstages.addFrontstage(testFrontstageProvider); + UiFramework.frontstages.addFrontstage(createTestPanelFrontstage()); + UiFramework.frontstages.addFrontstage(createTestPopoutFrontstage()); registerCustomFrontstage(); SynchronizedFloatingViewportStage.register( AppUiTestProviders.localizationNamespace diff --git a/ui/appui-react/src/appui-react/childwindow/CopyStyles.ts b/ui/appui-react/src/appui-react/childwindow/CopyStyles.ts index 7319b1caf46..53fd78b5ad6 100644 --- a/ui/appui-react/src/appui-react/childwindow/CopyStyles.ts +++ b/ui/appui-react/src/appui-react/childwindow/CopyStyles.ts @@ -2,39 +2,35 @@ * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -/** - * Copies the source CSS into the destination - * @param targetDoc - target document - * @param sourceDoc - source document + +/** Copies the CSS style sheets from source document into the target document. * @internal */ export function copyStyles( targetDoc: Document, sourceDoc: Document = document ) { - const stylesheets = sourceDoc.adoptedStyleSheets - ? Array.from([...sourceDoc.styleSheets, ...sourceDoc.adoptedStyleSheets]) - : Array.from(sourceDoc.styleSheets); + const styleSheets = Array.from(sourceDoc.styleSheets); + styleSheets.forEach(({ ownerNode }) => { + // Copy `link` and `style` elements. + if (!ownerNode) return; + const clonedNode = targetDoc.importNode(ownerNode, true); + targetDoc.head.appendChild(clonedNode); + }); - stylesheets.forEach((stylesheet) => { - const css = stylesheet; - if (stylesheet.href) { - const newStyleElement = targetDoc.createElement("link"); - newStyleElement.rel = "stylesheet"; - newStyleElement.href = stylesheet.href; - targetDoc.head.appendChild(newStyleElement); - } else { - if (css && css.cssRules && css.cssRules.length > 0) { - const newStyleElement = targetDoc.createElement("style"); - Array.from(css.cssRules).forEach((rule) => { - newStyleElement.appendChild(targetDoc.createTextNode(rule.cssText)); - }); - targetDoc.head.appendChild(newStyleElement); - } - } + const adoptedStyleSheets = Array.from(sourceDoc.adoptedStyleSheets ?? []); + adoptedStyleSheets.forEach((styleSheet) => { + // Adopted stylesheet have no ownerNode and can't be shared between multiple documents. + if (!targetDoc.defaultView) return; + const newStyleSheet = new targetDoc.defaultView.CSSStyleSheet(); + Array.from(styleSheet.cssRules).forEach((rule, index) => { + // `cssText` might not serialize complex shorthand properties correctly: https://github.com/iTwin/appui/issues/893 + newStyleSheet.insertRule(rule.cssText, index); + }); + targetDoc.adoptedStyleSheets.push(newStyleSheet); }); - // copy sprites + // Copy sprites. const svgSymbolParent = sourceDoc.getElementById("__SVG_SPRITE_NODE__"); if (svgSymbolParent) { targetDoc.body.appendChild(svgSymbolParent.cloneNode(true)); diff --git a/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx b/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx index b2847b5777c..71d904d8e81 100644 --- a/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx +++ b/ui/appui-react/src/appui-react/preview/PreviewFeatures.tsx @@ -47,17 +47,24 @@ interface KnownPreviewFeatures { * Discuss or upvote this feature: https://github.com/iTwin/appui/discussions/723 */ widgetActionDropdown: { threshold: number }; - /** If `true`, the [[Toolbar]] component will be replaced by a new iTwinUI based toolbar. */ + /** If `true`, the [[Toolbar]] component will be replaced by a new iTwinUI based toolbar. + * + * Discuss or upvote this feature: https://github.com/iTwin/appui/discussions/924 + */ newToolbars: boolean; /** If `true`, popout widgets will not be rendered in a separate element tree, instead widget content will be re-parented to a popout content container. * Alternatively, an array of widget ids can be specified to only re-parent specific widgets. * @note Use {@link useTransientState} to save and restore DOM transient state when re-parenting widgets. * @note There is a known limitation where iTwinUI v2 popover elements will be rendered in the main window. Prefer using iTwinUI v3 when using this feature. + * + * Discuss or upvote this feature: https://github.com/iTwin/appui/discussions/925 */ reparentPopoutWidgets: boolean | WidgetDef["id"][]; /** If `true`, additional UI elements are rendered to allow the end user of the layout to control widget visibility. * Alternatively, an array of widget ids can be specified to only control specific widgets. * @note Use {@link UiItemsManager} APIs to manage what widgets are available to the end-user. + * + * Discuss or upvote this feature: https://github.com/iTwin/appui/discussions/859 */ controlWidgetVisibility: boolean | WidgetDef["id"][]; } diff --git a/ui/appui-react/src/appui-react/statusbar/StatusBar.scss b/ui/appui-react/src/appui-react/statusbar/StatusBar.scss index ad973ddd2a6..e23f62255a7 100644 --- a/ui/appui-react/src/appui-react/statusbar/StatusBar.scss +++ b/ui/appui-react/src/appui-react/statusbar/StatusBar.scss @@ -21,7 +21,7 @@ } } -.uifw-statusbar-space-between { +.uifw-statusBar-space-between { width: 100%; height: 100%; display: flex; @@ -29,29 +29,29 @@ justify-content: space-between; } -.uifw-statusbar-left { +.uifw-statusBar-left { display: flex; flex-direction: row; justify-content: flex-start; } -.uifw-statusbar-center { +.uifw-statusBar-center { display: flex; flex-direction: row; justify-content: center; } -.uifw-statusbar-right { +.uifw-statusBar-right { display: flex; flex-direction: row; justify-content: flex-end; } -.uifw-statusbar-docked { +.uifw-statusBar-docked { width: 100%; background-color: var(--iui-color-background); - .uifw-statusbar-item-container { + .uifw-statusBar-item-container { @include for-medium-desktop-up { padding-left: var(--iui-size-m); } @@ -67,20 +67,20 @@ &:has(.uifw-statusBar-separator) { padding-left: 0; - + .uifw-statusbar-item-container { + + .uifw-statusBar-item-container { padding-left: 0; } } } - .uifw-statusbar-left .uifw-statusbar-item-container { + .uifw-statusBar-left .uifw-statusBar-item-container { &:first-child { padding-left: 0; } } } -.uifw-statusbar-item-container { +.uifw-statusBar-item-container { height: 100%; display: flex; flex-direction: row; diff --git a/ui/appui-react/src/appui-react/statusbar/StatusBar.tsx b/ui/appui-react/src/appui-react/statusbar/StatusBar.tsx index 7394b6564f5..dc47a4a2ed7 100644 --- a/ui/appui-react/src/appui-react/statusbar/StatusBar.tsx +++ b/ui/appui-react/src/appui-react/statusbar/StatusBar.tsx @@ -64,7 +64,7 @@ export function StatusBarSpaceBetween(props: CommonDivProps) { return (
); } @@ -77,7 +77,7 @@ export function StatusBarLeftSection(props: CommonDivProps) { return (
); } @@ -90,7 +90,7 @@ export function StatusBarCenterSection(props: CommonDivProps) { return (
); } @@ -103,7 +103,7 @@ export function StatusBarRightSection(props: CommonDivProps) { return (
); } diff --git a/ui/appui-react/src/appui-react/statusbar/StatusBarComposer.tsx b/ui/appui-react/src/appui-react/statusbar/StatusBarComposer.tsx index 6ea202a818a..afc7157e878 100644 --- a/ui/appui-react/src/appui-react/statusbar/StatusBarComposer.tsx +++ b/ui/appui-react/src/appui-react/statusbar/StatusBarComposer.tsx @@ -39,6 +39,7 @@ import { import { StatusBarItemsManager } from "./StatusBarItemsManager"; import { useDefaultStatusBarItems } from "./useDefaultStatusBarItems"; import { useUiItemsProviderStatusBarItems } from "./useUiItemsProviderStatusBarItems"; +import { StatusBarCornerComponentContext } from "./StatusBarCornerComponentContext"; /** Private function to generate a value that will allow the proper order to be maintained when items are placed in overflow panel */ function getCombinedSectionItemPriority(item: StatusBarItem) { @@ -82,7 +83,7 @@ export function DockedStatusBarItem(props: StatusBarItemProps) { const { onResize } = useStatusBarEntry(); const ref = useResizeObserver(onResize); const className = classnames( - "uifw-statusbar-item-container", + "uifw-statusBar-item-container", props.className ); return ( @@ -344,6 +345,11 @@ export function StatusBarComposer(props: StatusBarComposerProps) { const combinedItems = combineItems(defaultItems, addonItems); return sortItems(combinedItems); }, [defaultItems, addonItems]); + const itemsNotInOverflow = React.useMemo(() => { + return statusBarItems.filter( + (item) => !isItemInOverflow(item.id, overflown) + ); + }, [overflown, statusBarItems]); const calculateOverflow = React.useCallback(() => { const widths = verifiedMapEntries(entryWidths.current); @@ -432,18 +438,31 @@ export function StatusBarComposer(props: StatusBarComposerProps) { providerId={providerId} section={getSectionName(section)} > - {isStatusBarCustomItem(item) && item.content} - {isStatusBarActionItem(item) && ( - - )} - {isStatusBarLabelItem(item) && ( - - )} + + {isStatusBarCustomItem(item) && item.content} + {isStatusBarActionItem(item) && ( + + )} + {isStatusBarLabelItem(item) && ( + + )} + ); }, - [handleEntryResize] + [handleEntryResize, itemsNotInOverflow, overflown] ); const getSectionItems = React.useCallback( @@ -530,7 +549,7 @@ export function StatusBarComposer(props: StatusBarComposerProps) { [getOverflowItems] ); - const containerClassName = classnames("uifw-statusbar-docked", className); + const containerClassName = classnames("uifw-statusBar-docked", className); return (
(undefined); diff --git a/ui/appui-react/src/appui-react/statusfields/SelectionCount.scss b/ui/appui-react/src/appui-react/statusfields/SelectionCount.scss index cd45b2dc17c..a6d1d5b4ba1 100644 --- a/ui/appui-react/src/appui-react/statusfields/SelectionCount.scss +++ b/ui/appui-react/src/appui-react/statusfields/SelectionCount.scss @@ -6,9 +6,18 @@ gap: var(--iui-size-2xs); display: flex; align-items: center; - padding: 0 var(--iui-size-2xs) 0 var(--iui-size-2xs); + min-width: var(--iui-size-2xl); + padding-left: var(--iui-size-2xs); .icon { color: var(--iui-color-icon); } + + &.uifw-left-corner { + margin-left: var(--iui-size-m); + } + + &.uifw-right-corner { + margin-right: var(--iui-size-m); + } } diff --git a/ui/appui-react/src/appui-react/statusfields/SelectionCount.tsx b/ui/appui-react/src/appui-react/statusfields/SelectionCount.tsx index fbf6afa7400..bc98a4bc741 100644 --- a/ui/appui-react/src/appui-react/statusfields/SelectionCount.tsx +++ b/ui/appui-react/src/appui-react/statusfields/SelectionCount.tsx @@ -6,12 +6,13 @@ * @module StatusBar */ +import * as React from "react"; import type { IModelConnection } from "@itwin/core-frontend"; import type { CommonProps } from "@itwin/core-react"; import { Icon } from "@itwin/core-react"; import { SvgCursor } from "@itwin/itwinui-icons-react"; import classnames from "classnames"; -import * as React from "react"; +import { StatusBarCornerComponentContext } from "../statusbar/StatusBarCornerComponentContext"; import "./SelectionCount.scss"; /** Properties for the [[SelectionCountField]] component. @@ -27,8 +28,11 @@ export interface SelectionCountFieldProps extends CommonProps { * @beta */ export function SelectionCountField(props: SelectionCountFieldProps) { + const cornerContext = React.useContext(StatusBarCornerComponentContext); const className = classnames( "uifw-statusFields-selectionCount", + cornerContext === "left-corner" && "uifw-left-corner", + cornerContext === "right-corner" && "uifw-right-corner", props.className ); return ( diff --git a/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarItemsProvider.tsx b/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarItemsProvider.tsx index b7a87282901..124a1f45982 100644 --- a/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarItemsProvider.tsx +++ b/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarItemsProvider.tsx @@ -12,8 +12,7 @@ import type { StatusBarItem } from "../statusbar/StatusBarItem"; import { UiItemsManager } from "./UiItemsManager"; import { BaseUiItemsProvider } from "./BaseUiItemsProvider"; -/** - * Provide standard statusbar fields for the SimpleStatusbarWidget +/** Provide standard status bar fields. * @public */ export class StandardStatusbarItemsProvider extends BaseUiItemsProvider { diff --git a/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarUiItemsProvider.tsx b/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarUiItemsProvider.tsx index 9ce8811b04b..68e2697cc52 100644 --- a/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarUiItemsProvider.tsx +++ b/ui/appui-react/src/appui-react/ui-items-provider/StandardStatusbarUiItemsProvider.tsx @@ -20,9 +20,8 @@ import type { UiItemsProvider } from "./UiItemsProvider"; import type { StatusBarItem } from "../statusbar/StatusBarItem"; import { StatusBarSection } from "../statusbar/StatusBarItem"; -/** - * Defines what items to include from the provider. If any items are - * specified then only those items will be added to statusbar. +/** Defines what items to include from the provider. + * @note When this object is used, only explicitly enabled items will be added to the status bar. I.e. `{ messageCenter: true }` will only add message center field to the statusbar. * @public */ export interface DefaultStatusbarItems { @@ -38,8 +37,7 @@ export interface DefaultStatusbarItems { selectionInfo?: boolean; } -/** - * Provide standard statusbar fields for the SimpleStatusbarWidget +/** Provide standard status bar fields. * @beta */ export class StandardStatusbarUiItemsProvider implements UiItemsProvider { @@ -47,6 +45,7 @@ export class StandardStatusbarUiItemsProvider implements UiItemsProvider { return "appui-react:StandardStatusbarUiItemsProvider"; } + /** Creates a provider. If the `defaultItems` argument is not set, all default fields are added. Otherwise, only the fields that are set to `true` are added. */ constructor(private _defaultItems?: DefaultStatusbarItems) {} public provideStatusBarItems( diff --git a/ui/appui-react/src/test/statusbar/StatusBar.test.tsx b/ui/appui-react/src/test/statusbar/StatusBar.test.tsx index 0be8f7eabb3..11b45205a09 100644 --- a/ui/appui-react/src/test/statusbar/StatusBar.test.tsx +++ b/ui/appui-react/src/test/statusbar/StatusBar.test.tsx @@ -65,7 +65,7 @@ describe("StatusBar", () => { Hello ); expect( - container.querySelectorAll("div.uifw-statusbar-space-between").length + container.querySelectorAll("div.uifw-statusBar-space-between").length ).toEqual(1); }); @@ -74,7 +74,7 @@ describe("StatusBar", () => { Hello ); expect( - container.querySelectorAll("div.uifw-statusbar-left").length + container.querySelectorAll("div.uifw-statusBar-left").length ).toEqual(1); }); @@ -83,7 +83,7 @@ describe("StatusBar", () => { Hello ); expect( - container.querySelectorAll("div.uifw-statusbar-center").length + container.querySelectorAll("div.uifw-statusBar-center").length ).toEqual(1); }); @@ -92,7 +92,7 @@ describe("StatusBar", () => { Hello ); expect( - container.querySelectorAll("div.uifw-statusbar-right").length + container.querySelectorAll("div.uifw-statusBar-right").length ).toEqual(1); }); }); diff --git a/ui/appui-react/src/test/statusbar/StatusBarComposer.test.tsx b/ui/appui-react/src/test/statusbar/StatusBarComposer.test.tsx index fa09b7515bd..94e218fd8c8 100644 --- a/ui/appui-react/src/test/statusbar/StatusBarComposer.test.tsx +++ b/ui/appui-react/src/test/statusbar/StatusBarComposer.test.tsx @@ -129,7 +129,7 @@ describe("StatusBarComposer", () => { render(); expect(screen.getByRole("presentation")).to.satisfy( - childStructure(".uifw-statusbar-space-between") + childStructure(".uifw-statusBar-space-between") ); }); @@ -158,13 +158,13 @@ describe("StatusBarComposer", () => { render(); expect(screen.getByTestId("item1").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-left .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-left .uifw-statusBar-item-container") ); expect(screen.getByTestId("item2").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-center .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-center .uifw-statusBar-item-container") ); expect(screen.getByTestId("item3").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-right .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-right .uifw-statusBar-item-container") ); }); @@ -181,7 +181,7 @@ describe("StatusBarComposer", () => { const { rerender } = render(); expect(screen.getByTestId("item1").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-left .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-left .uifw-statusBar-item-container") ); const items2: StatusBarItem[] = [ @@ -197,7 +197,7 @@ describe("StatusBarComposer", () => { expect(screen.queryByTestId("item1")).toEqual(null); expect(screen.getByTestId("item2").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-center .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-center .uifw-statusBar-item-container") ); }); @@ -253,7 +253,7 @@ describe("StatusBarComposer", () => { expect(screen.queryByTestId("item1")).toEqual(null); expect(screen.getByTestId("item2").parentElement).to.satisfy( - selectorMatches(".uifw-statusbar-left .uifw-statusbar-item-container") + selectorMatches(".uifw-statusBar-left .uifw-statusBar-item-container") ); }); @@ -297,9 +297,9 @@ describe("StatusBarComposer", () => { await waitFor(() => expect(screen.getByRole("presentation")).to.satisfy( childStructure([ - ".uifw-statusbar-left .uifw-statusbar-item-container:only-child", - ".uifw-statusbar-center .icon-visibility-hide-2", - ".uifw-statusbar-center .icon-hand-2", + ".uifw-statusBar-left .uifw-statusBar-item-container:only-child", + ".uifw-statusBar-center .icon-visibility-hide-2", + ".uifw-statusBar-center .icon-hand-2", ]) ) ); @@ -308,7 +308,7 @@ describe("StatusBarComposer", () => { await waitFor(() => expect(screen.getByRole("presentation")).to.satisfy( - childStructure(".uifw-statusbar-center:empty") + childStructure(".uifw-statusBar-center:empty") ) ); }); @@ -351,10 +351,10 @@ describe("StatusBarComposer", () => { expect(screen.getByRole("presentation")).to.satisfy( childStructure([ - ".uifw-statusbar-left .uifw-statusbar-item-container:only-child", - ".uifw-statusbar-center .icon-visibility-hide-2", - ".uifw-statusbar-center .icon-hand-2", - ".uifw-statusbar-center .icon-hand-2-condition", + ".uifw-statusBar-left .uifw-statusBar-item-container:only-child", + ".uifw-statusBar-center .icon-visibility-hide-2", + ".uifw-statusBar-center .icon-hand-2", + ".uifw-statusBar-center .icon-hand-2-condition", ]) ); @@ -362,7 +362,7 @@ describe("StatusBarComposer", () => { await waitFor(() => { expect(screen.getByRole("presentation")).to.not.satisfy( - childStructure([".uifw-statusbar-center .icon-hand-2-condition"]) + childStructure([".uifw-statusBar-center .icon-hand-2-condition"]) ); }); @@ -370,7 +370,7 @@ describe("StatusBarComposer", () => { await waitFor(() => expect(screen.getByRole("presentation")).to.satisfy( - childStructure(".uifw-statusbar-center:empty") + childStructure(".uifw-statusBar-center:empty") ) ); }); @@ -409,9 +409,9 @@ describe("StatusBarComposer", () => { expect(screen.getByRole("presentation")).to.satisfy( childStructure([ - ".main-test :not(.uifw-statusbar-left).left-test > .uifw-statusbar-item-container", - ".main-test :not(.uifw-statusbar-center).center-test > .uifw-statusbar-item-container", - ".main-test :not(.uifw-statusbar-right).right-test > .uifw-statusbar-item-container", + ".main-test :not(.uifw-statusBar-left).left-test > .uifw-statusBar-item-container", + ".main-test :not(.uifw-statusBar-center).center-test > .uifw-statusBar-item-container", + ".main-test :not(.uifw-statusBar-right).right-test > .uifw-statusBar-item-container", ]) ); }); @@ -436,9 +436,9 @@ describe("StatusBarComposer", () => { // make sure we have enough size to render without overflow vi.spyOn(Element.prototype, "getBoundingClientRect").mockImplementation( function (this: HTMLElement) { - if (this.classList.contains("uifw-statusbar-docked")) { + if (this.classList.contains("uifw-statusBar-docked")) { return DOMRect.fromRect({ width: 1000 }); - } else if (this.classList.contains("uifw-statusbar-item-container")) { + } else if (this.classList.contains("uifw-statusBar-item-container")) { return DOMRect.fromRect({ width: 40 }); } else if (this instanceof HTMLButtonElement) { return DOMRect.fromRect({ width: 40 }); @@ -466,7 +466,7 @@ describe("StatusBarComposer", () => { const wrapper = render(); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(1); const uiProvider = new TestUiProvider(); @@ -474,7 +474,7 @@ describe("StatusBarComposer", () => { UiItemsManager.register(uiProvider); }); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(5); await wrapper.findByText("visible"); @@ -486,7 +486,7 @@ describe("StatusBarComposer", () => { UiItemsManager.unregister(uiProvider.id); }); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(1); wrapper.unmount(); }); @@ -509,9 +509,9 @@ describe("StatusBarComposer", () => { // make sure we have enough size to render without overflow vi.spyOn(Element.prototype, "getBoundingClientRect").mockImplementation( function (this: HTMLElement) { - if (this.classList.contains("uifw-statusbar-docked")) { + if (this.classList.contains("uifw-statusBar-docked")) { return DOMRect.fromRect({ width: 1600 }); - } else if (this.classList.contains("uifw-statusbar-item-container")) { + } else if (this.classList.contains("uifw-statusBar-item-container")) { return DOMRect.fromRect({ width: 40 }); } else if (this instanceof HTMLButtonElement) { return DOMRect.fromRect({ width: 40 }); @@ -545,7 +545,7 @@ describe("StatusBarComposer", () => { const wrapper = render(); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(1); const uiProvider = new TestUiProvider(true); @@ -553,14 +553,14 @@ describe("StatusBarComposer", () => { UiItemsManager.register(uiProvider); }); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(5); act(() => { UiItemsManager.unregister(uiProvider.id); }); expect( - wrapper.container.querySelectorAll(".uifw-statusbar-item-container") + wrapper.container.querySelectorAll(".uifw-statusBar-item-container") ).toHaveLength(1); wrapper.unmount(); }); @@ -568,9 +568,9 @@ describe("StatusBarComposer", () => { it("will render 4 items without overflow", () => { vi.spyOn(Element.prototype, "getBoundingClientRect").mockImplementation( function (this: HTMLElement) { - if (this.classList.contains("uifw-statusbar-docked")) { + if (this.classList.contains("uifw-statusBar-docked")) { return DOMRect.fromRect({ width: 168 }); // 4*42 - } else if (this.classList.contains("uifw-statusbar-item-container")) { + } else if (this.classList.contains("uifw-statusBar-item-container")) { return DOMRect.fromRect({ width: 40 }); } else if (this instanceof HTMLButtonElement) { return DOMRect.fromRect({ width: 40 }); @@ -620,7 +620,7 @@ describe("StatusBarComposer", () => { expect(renderedComponent).toBeTruthy(); expect( renderedComponent.container.querySelectorAll( - ".uifw-statusbar-item-container" + ".uifw-statusBar-item-container" ) ).lengthOf(4); @@ -656,7 +656,7 @@ describe("StatusBarComposer", () => { ); expect( renderedComponent.container.querySelectorAll( - ".uifw-statusbar-item-container" + ".uifw-statusBar-item-container" ) ).lengthOf(3); }); @@ -664,9 +664,9 @@ describe("StatusBarComposer", () => { it("will render 1 item with overflow - 4 in overflow panel", async () => { vi.spyOn(Element.prototype, "getBoundingClientRect").mockImplementation( function (this: HTMLElement) { - if (this.classList.contains("uifw-statusbar-docked")) { + if (this.classList.contains("uifw-statusBar-docked")) { return DOMRect.fromRect({ width: 84 }); // 2*42 - } else if (this.classList.contains("uifw-statusbar-item-container")) { + } else if (this.classList.contains("uifw-statusBar-item-container")) { return DOMRect.fromRect({ width: 40 }); } else if (this instanceof HTMLButtonElement) { return DOMRect.fromRect({ width: 40 }); @@ -721,7 +721,7 @@ describe("StatusBarComposer", () => { expect(renderedComponent).toBeTruthy(); expect( renderedComponent.container.querySelectorAll( - ".uifw-statusbar-item-container" + ".uifw-statusBar-item-container" ) ).lengthOf(1); const overflow = renderedComponent.getByRole("button"); @@ -731,12 +731,12 @@ describe("StatusBarComposer", () => { "uifw-statusbar-overflow-panel" ); expect( - containerInPortal.querySelectorAll(".uifw-statusbar-item-container") + containerInPortal.querySelectorAll(".uifw-statusBar-item-container") ).lengthOf(4); fireEvent.click(overflow); expect( renderedComponent.container.querySelectorAll( - ".uifw-statusbar-item-container" + ".uifw-statusBar-item-container" ) ).lengthOf(1); });