Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f68e3ba
initial
MartynasStrazdas Jul 2, 2026
8141849
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 3, 2026
be2238d
adjustments to context menu
MartynasStrazdas Jul 3, 2026
f1fd456
update button sizes
MartynasStrazdas Jul 3, 2026
19ee22d
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 3, 2026
44aa53e
fix context window behavior
MartynasStrazdas Jul 9, 2026
7bfd1cd
change
MartynasStrazdas Jul 9, 2026
f9514b1
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 9, 2026
7eb4597
fixes
MartynasStrazdas Jul 9, 2026
9364d1d
fixes
MartynasStrazdas Jul 9, 2026
cf198d2
update to use catalog
MartynasStrazdas Jul 9, 2026
6724fc8
lock file update
MartynasStrazdas Jul 9, 2026
ac9cc23
rerender all component tests
MartynasStrazdas Jul 9, 2026
1653e5b
fixes
MartynasStrazdas Jul 9, 2026
ee6683d
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 9, 2026
2c66480
test: update visual regression screenshots
imodeljs-admin Jul 9, 2026
57a5253
define our own onClick
MartynasStrazdas Jul 9, 2026
af2fe9d
use Menu for context window
MartynasStrazdas Jul 10, 2026
5d3d9a2
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 10, 2026
b69f666
test: update visual regression screenshots
imodeljs-admin Jul 10, 2026
fceb885
chore: add types for @stratakit/mui in tsconfig files
MartynasStrazdas Jul 13, 2026
70ea14d
fix lint issues because of the addition of types from stratakit
MartynasStrazdas Jul 13, 2026
d358a13
format
MartynasStrazdas Jul 13, 2026
35a90f4
test: update visual regression screenshots
imodeljs-admin Jul 13, 2026
666055e
spinner size adjustment
MartynasStrazdas Jul 13, 2026
d3ff108
remove itwin ui from test app stateless tree
MartynasStrazdas Jul 13, 2026
6801de9
Merge branch 'next' into mast/move-to-MUI
MartynasStrazdas Jul 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/early-facts-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@itwin/presentation-hierarchies-react": minor
Comment thread
MartynasStrazdas marked this conversation as resolved.
---

Moved the package to use `@mui/material` and `@stratakit/mui`.

**Breaking:** Removed `@stratakit/foundations`, `@stratakit/icons` optional peer dependencies. Added `@mui/material` and `@stratakit/mui` as optional peer dependencies.
Comment thread
MartynasStrazdas marked this conversation as resolved.
2 changes: 2 additions & 0 deletions apps/test-app/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
"@itwin/unified-selection": "workspace:*",
"@itwin/unified-selection-react": "workspace:*",
"@rolldown/plugin-babel": "catalog:build-tools",
"@mui/material": "^9.0.0",
"@stratakit/foundations": "catalog:stratakit",
"@stratakit/icons": "catalog:stratakit",
"@stratakit/mui": "catalog:stratakit",
"@stratakit/structures": "catalog:stratakit",
"@tanstack/react-table": "^8.21.3",
"@test-app/common": "workspace:*",
Expand Down
17 changes: 9 additions & 8 deletions packages/hierarchies-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"@itwin/presentation-hierarchies": "workspace:*",
"@itwin/presentation-shared": "workspace:*",
"@itwin/unified-selection": "workspace:^",
"@stratakit/bricks": "catalog:stratakit",
"@stratakit/icons": "catalog:stratakit",
"@stratakit/structures": "catalog:stratakit",
"@tanstack/react-virtual": "^3.13.24",
"classnames": "catalog:react",
Expand All @@ -76,25 +76,26 @@
"rxjs": "catalog:rxjs"
},
"peerDependencies": {
"@stratakit/foundations": "^0.4.7",
"@stratakit/icons": "^0.3.1",
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
"@mui/material": "^9.0.0",
"@stratakit/mui": "^0.5.0",
"react": "^18.0.0 || ^19.0.0",
"react-dom": "^18.0.0 || ^19.0.0"
},
"peerDependenciesMeta": {
"@stratakit/foundations": {
"@mui/material": {
"optional": true
},
"@stratakit/icons": {
"@stratakit/mui": {
"optional": true
}
},
"devDependencies": {
"@itwin/build-tools": "catalog:build-tools",
"@itwin/eslint-plugin": "catalog:build-tools",
"@mui/material": "^9.0.0",
"@rolldown/plugin-babel": "catalog:build-tools",
"@stratakit/foundations": "catalog:stratakit",
"@stratakit/icons": "catalog:stratakit",
"@stratakit/mui": "^0.5.0",
Comment thread
MartynasStrazdas marked this conversation as resolved.
Outdated
"@testing-library/dom": "catalog:test-tools",
"@testing-library/react": "catalog:test-tools",
"@testing-library/user-event": "catalog:test-tools",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import { Anchor, Text } from "@stratakit/bricks";
import { Link, Typography } from "@mui/material";
import { unstable_ErrorRegion as ErrorRegion } from "@stratakit/structures";
import { MAX_LIMIT_OVERRIDE } from "../internal/Utils.js";
import { useTranslation } from "../LocalizationContext.js";
Expand Down Expand Up @@ -123,11 +123,11 @@ function ErrorItemContainer({ treeNode, message, actions, scrollToElement }: Err
actions={actions
?.filter(({ condition }) => condition())
.map(({ label, action }) => (
<Text key={label} variant="body-sm">
<Anchor onClick={action} render={<button />}>
<Typography key={label} variant="caption" component="span" sx={{ display: "block" }}>
Comment thread
saskliutas marked this conversation as resolved.
Outdated
<Link component="button" onClick={action}>
Comment thread
Copilot marked this conversation as resolved.
Outdated
{label}
</Anchor>
</Text>
</Link>
</Typography>
))}
/>
);
Expand All @@ -146,9 +146,9 @@ function MessageWithLink({ linkLabel, scrollToElement, message }: MessageWithLin
return (
<div>
{firstPart}{" "}
<Anchor onClick={scrollToElement} render={<button />}>
<Link component="button" onClick={scrollToElement} variant="caption" sx={{ verticalAlign: "baseline" }}>
Comment thread
MartynasStrazdas marked this conversation as resolved.
Outdated
{linkLabel}
</Anchor>
</Link>
{secondPart ? " " : ""}
{secondPart ? secondPart : null}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

import { Button, Text } from "@stratakit/bricks";
import { Icon } from "@stratakit/foundations";
import { Button, Typography } from "@mui/material";
import { Icon } from "@stratakit/mui";
import { useTranslation } from "../LocalizationContext.js";

import errorSvg from "@stratakit/icons/status-error.svg";
Expand Down Expand Up @@ -73,14 +73,14 @@ function RootErrorContainer({ actions, message }: RootErrorContainerProps) {
}}
>
<Icon href={errorSvg} size="large" />
<Text variant={"body-sm"} style={{ textAlign: "center" }}>
<Typography variant="caption" style={{ textAlign: "center" }}>
{message}
</Text>
</Typography>
{actions
?.filter(({ condition }) => condition())
.map((action) => {
return (
<Button key={action.label} onClick={() => action.action()}>
<Button key={action.label} onClick={() => action.action()} size="small">
{action.label}
</Button>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
*--------------------------------------------------------------------------------------------*/

import { memo } from "react";
import { DropdownMenu, Tree } from "@stratakit/structures";
import { ListItemIcon, MenuItem, Typography } from "@mui/material";
import { Icon } from "@stratakit/mui";
import { Tree } from "@stratakit/structures";

import type React from "react";
import type { ComponentPropsWithoutRef, NamedExoticComponent } from "react";

/**
Expand Down Expand Up @@ -63,7 +66,13 @@ export const TreeActionBase: NamedExoticComponent<TreeActionBaseProps> = memo(fu
}

if (variant === "context-menu") {
return <DropdownMenu.Item {...actionProps} />;
const { label, icon, onClick } = actionProps;
return (
<MenuItem dense onClick={onClick as unknown as React.MouseEventHandler<HTMLLIElement>}>
Comment thread
saskliutas marked this conversation as resolved.
Outdated
{icon ? <ListItemIcon>{typeof icon === "string" ? <Icon href={icon} /> : icon}</ListItemIcon> : null}
<Typography variant="caption">{label}</Typography>
</MenuItem>
);
}

return <Tree.ItemAction {...actionProps} dot={dot} visible={variant === "inline" ? visible : undefined} />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,19 @@ import {
useRef,
useState,
} from "react";
import { Description, IconButton, Spinner, Text, TextBox, VisuallyHidden } from "@stratakit/bricks";
import { DropdownMenu, unstable_Popover as Popover, Tree } from "@stratakit/structures";
import {
CircularProgress,
ClickAwayListener,
FormHelperText,
IconButton,
MenuList,
Paper,
Popper,
TextField,
Typography,
} from "@mui/material";
import { Icon } from "@stratakit/mui";
import { unstable_Popover as Popover, Tree } from "@stratakit/structures";
import { useTranslation } from "../LocalizationContext.js";
import { TreeActionBase } from "./TreeAction.js";
import { useTreeNodeRenameContext } from "./TreeNodeRenameAction.js";
Expand Down Expand Up @@ -187,32 +198,39 @@ export const StrataKitTreeNodeRenderer: FC<PropsWithRef<TreeNodeRendererProps &
}}
/>
</Popover>
<DropdownMenu.Provider
open={contextMenuProps !== undefined}
setOpen={(open) => {
if (!open) {
setContextMenuProps(undefined);
}
}}
key={`${node.id}-${contextMenuProps?.position.x ?? ""}-${contextMenuProps?.position.y ?? ""}`}
>
{contextMenuProps ? (
<DropdownMenu.Button
render={(renderProps) => (
<div style={{ position: "fixed", top: contextMenuProps.position.y, left: contextMenuProps.position.x }}>
<VisuallyHidden {...renderProps}>{translate("more")}</VisuallyHidden>
</div>
)}
/>
) : null}
{/* `autoFocus` prop is coming from ariakit and is not native HTML `autoFocus` prop. */}
{/* `focusable` is needed for `autoFocus` to work. StrataKit exposes only `autoFocus` and does not set `focusable` internally. */}
{/* eslint-disable jsx-a11y/no-autofocus */}
{/* @ts-expect-error focusable is passed through */}
<DropdownMenu.Content focusable autoFocus={true}>
{contextMenuProps?.actions}
</DropdownMenu.Content>
</DropdownMenu.Provider>
{contextMenuProps && (
<Popper
open
anchorEl={{
getBoundingClientRect: () =>
DOMRect.fromRect({
x: contextMenuProps.position.x,
y: contextMenuProps.position.y,
width: 0,
height: 0,
}),
}}
placement="bottom-start"
style={{ zIndex: 1300 }}
>
<ClickAwayListener mouseEvent="onMouseDown" onClickAway={() => setContextMenuProps(undefined)}>
<Paper
Comment thread
saskliutas marked this conversation as resolved.
Outdated
elevation={8}
sx={{ bgcolor: "background.paper" }}
onKeyDown={(e) => e.key === "Escape" && setContextMenuProps(undefined)}
>
<MenuList
dense
autoFocusItem
aria-label={translate("more")}
onClick={() => setContextMenuProps(undefined)}
>
{contextMenuProps.actions}
</MenuList>
</Paper>
</ClickAwayListener>
</Popper>
)}
</>
);
}),
Expand All @@ -225,15 +243,14 @@ export const PlaceholderNode: FC<
>
> = memo(
forwardRef<HTMLElement, Pick<StrataKitTreeItemProps, "style" | "aria-level" | "aria-posinset" | "aria-setsize">>(
// eslint-disable-next-line @typescript-eslint/no-shadow
function PlaceholderNode({ ...props }, forwardedRef) {
const translate = useTranslation();
return (
<Tree.Item
{...props}
ref={forwardedRef}
label={translate("loading")}
unstable_decorations={<Spinner size={"small"} title={translate("loading")} />}
unstable_decorations={<CircularProgress size={20} title={translate("loading")} />}
/>
);
},
Expand Down Expand Up @@ -288,37 +305,37 @@ function LabelEditor({
return (
<div key={initialLabel} className="phr-node-label-editor">
<div className="phr-node-label-editor-input-row">
<TextBox.Root style={{ width: "100%" }} className={hasError ? "with-error" : undefined}>
<TextBox.Input
id={inputId}
ref={inputRef}
aria-label={translate("newLabel")}
value={newLabelValue}
onChange={(event) => {
setNewLabelValue(event.target.value);
setHasError(false);
}}
onKeyUp={(event) => {
if (event.key === "Enter") {
handleLabelChange();
} else if (event.key === "Escape") {
cancelLabelChange();
}
}}
/>
</TextBox.Root>
<IconButton icon={dismissSvg} label={translate("cancel")} onClick={cancelLabelChange} />
<IconButton
icon={checkmarkSvg}
label={translate("confirm")}
onClick={handleLabelChange}
disabled={!canRename}
<TextField
fullWidth
error={hasError}
size="small"
id={inputId}
inputRef={inputRef}
slotProps={{ htmlInput: { "aria-label": translate("newLabel") } }}
value={newLabelValue}
Comment thread
MartynasStrazdas marked this conversation as resolved.
onChange={(event) => {
setNewLabelValue(event.target.value);
setHasError(false);
}}
onKeyUp={(event) => {
if (event.key === "Enter") {
handleLabelChange();
} else if (event.key === "Escape") {
cancelLabelChange();
}
}}
/>
<IconButton aria-label={translate("cancel")} onClick={cancelLabelChange} size="small">
<Icon href={dismissSvg} />
</IconButton>
<IconButton aria-label={translate("confirm")} onClick={handleLabelChange} disabled={!canRename} size="small">
<Icon href={checkmarkSvg} />
</IconButton>
</div>
{labelValidationHint !== undefined ? (
<Description id={inputId} tone={hasError ? "critical" : "neutral"} style={{ display: "flex" }}>
<Text variant="caption-lg">{labelValidationHint}</Text>
</Description>
<FormHelperText id={inputId} error={hasError} style={{ display: "flex" }}>
<Typography variant="caption">{labelValidationHint}</Typography>
</FormHelperText>
) : undefined}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import axe from "axe-core";
import { expect } from "vitest";
import { render } from "vitest-browser-react";
import { Root } from "@stratakit/foundations";
import { Root } from "@stratakit/mui";
import { LocalizationContextProvider } from "../../presentation-hierarchies-react/LocalizationContext.js";
import localeEn from "../../public/locales/en/PresentationHierarchies_1.0.json" with { type: "json" };

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ function createDefaultProps(overrides?: Partial<StrataKitTreeRendererProps>): St
};
}

async function renderTree(props: StrataKitTreeRendererProps, colorScheme: "light" | "dark") {
async function renderTree(
props: StrataKitTreeRendererProps,
colorScheme: "light" | "dark",
options?: { height?: number },
) {
return renderWithTheme(
<div style={{ height: 100, width: 300 }}>
<div style={{ height: options?.height ?? 100, width: 300 }}>
<StrataKitTreeRenderer {...props} />
</div>,
{ colorScheme },
Expand Down Expand Up @@ -172,6 +176,7 @@ COLOR_SCHEMES.forEach((colorScheme) => {
});

it("renders tree with context menu actions", async () => {
await page.viewport(300, 200);
const props = createDefaultProps({
rootNodes: [
createTreeNode({ id: "node-1", label: "Node with context menu" }),
Expand All @@ -182,11 +187,10 @@ COLOR_SCHEMES.forEach((colorScheme) => {
<TreeActionBase key="action-2" label="Context Action 2" icon={placeholderSvg} onClick={vi.fn()} />,
],
});
const { locator } = await renderTree(props, colorScheme);
const { locator } = await renderTree(props, colorScheme, { height: 200 });
await locator.getByText("Node with context menu").click({ button: "right" });
await expect.element(page.getByText("Context Action 1")).toBeVisible();
// `aria-required-children` is being triggered because anchor element of context menu is rendered next to `treeitem` under `tree` and
// it expects that `tree` contains only `treeitem` children.
await expect.element(page.getByText("Context Action 2")).toBeVisible();
await validateSnapshot(locator, { skipA11y: ["aria-hidden-focus", "aria-required-children"] });
Comment thread
grigasp marked this conversation as resolved.
Outdated
});

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Comment thread
grigasp marked this conversation as resolved.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions packages/hierarchies-react/vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export default defineConfig({
},
{
plugins: [react()],
optimizeDeps: {
include: ["@mui/material"],
},
test: {
name: "hierarchies-react-components",
include: ["src/test/components/**/*.test.{ts,tsx}"],
Expand Down
Loading
Loading