Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/imodel-browser-react",
"comment": "Prevent Stratakit grid-level props from being passed to cards",
"type": "patch"
}
],
"packageName": "@itwin/imodel-browser-react"
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
import { BaseCardLoading } from "../../components/baseCard/BaseCardLoading";
import { NoResultsMUI } from "../../components/noResults/NoResultsMUI";
import { type ITwinTableOverridesMUI } from "../../types";
import { stripNonTileProps } from "../../utils/stripNonTileProps";
import { type ITwinTableMUIStrings, ITwinTableMUI } from "./ITwinTableMUI";
import { type ITwinTilePropsMUI, ITwinTileMUI } from "./ITwinTileMUI";

Expand Down Expand Up @@ -296,7 +297,7 @@ type ITwinHookedTileProps = ITwinTilePropsMUI & {
};
const noOp = () => ({} as Partial<ITwinTilePropsMUI>);
const ITwinHookedTile = (props: ITwinHookedTileProps) => {
const { useTileState = noOp, ...iTwinTileProps } = props;
const { useTileState = noOp, ...rest } = props;

const hookIdentity = React.useRef(useTileState);

Expand All @@ -306,8 +307,7 @@ const ITwinHookedTile = (props: ITwinHookedTileProps) => {
);
}

const tileState = useTileState(props.iTwin, iTwinTileProps);
// gridProps aren't used by ITwinTileMUI but are passed to useIndividualState
const { gridProps, ...tileProps } = props;
const tileProps = stripNonTileProps(rest);
const tileState = stripNonTileProps(useTileState(props.iTwin, rest));
return <ITwinTileMUI {...tileProps} {...tileState} />;
};
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
import { BaseCardLoading } from "../../components/baseCard/BaseCardLoading";
import { NoResultsMUI as NoResults } from "../../components/noResults/NoResultsMUI";
import { type IModelTableOverridesMUI } from "../../types";
import { stripNonTileProps } from "../../utils/stripNonTileProps";
import {
type IModelTileMUIProps,
IModelTileMUI,
Expand Down Expand Up @@ -453,7 +454,7 @@ type IModelHookedTileProps = IModelTileMUIProps & {
const noOp = () => ({} as Partial<IModelTileMUIProps>);

const IModelHookedTile = (props: IModelHookedTileProps) => {
const { useTileState = noOp, ...iModelTileProps } = props;
const { useTileState = noOp, ...rest } = props;

const hookIdentity = React.useRef(useTileState);

Expand All @@ -463,9 +464,9 @@ const IModelHookedTile = (props: IModelHookedTileProps) => {
);
}

const tileState = useTileState(props.iModel, iModelTileProps);

return <IModelTileMUI {...iModelTileProps} {...tileState} />;
const tileProps = stripNonTileProps(rest);
const tileState = stripNonTileProps(useTileState(props.iModel, rest));
return <IModelTileMUI {...tileProps} {...tileState} />;
};

function removeFromRecentsAction(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { stripNonTileProps } from "./stripNonTileProps";

describe("stripNonTileProps", () => {
it("removes gridProps, useTileState, and tileProps while preserving everything else", () => {
const input = {
id: "1",
title: "Tile",
onClick: () => undefined,
gridProps: { foo: "bar" },
useTileState: () => ({}),
tileProps: { className: "x" },
};

const result = stripNonTileProps(input);

expect(result).toEqual({
id: "1",
title: "Tile",
onClick: input.onClick,
});
expect(result).not.toHaveProperty("gridProps");
expect(result).not.toHaveProperty("useTileState");
expect(result).not.toHaveProperty("tileProps");
});

it("returns a new object without mutating the source", () => {
const input = { keep: 1, gridProps: { foo: "bar" } };

const result = stripNonTileProps(input);

expect(result).not.toBe(input);
expect(input).toHaveProperty("gridProps");
});

it("is a no-op when none of the stripped keys are present", () => {
const input = { a: 1, b: "two" };

expect(stripNonTileProps(input)).toEqual(input);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/

/**
* Strip props that may be passed to useIndividualState hooks but should not be
* forwarded to a tile/card component.
*/
export const stripNonTileProps = <T extends object>(
source: T
): Omit<T, "gridProps" | "useTileState" | "tileProps"> => {
const {
gridProps: _gridProps,
useTileState: _useTileState,
tileProps: _tileProps,
...stripped
} = source as T & {
gridProps?: unknown;
useTileState?: unknown;
tileProps?: unknown;
};
return stripped;
};
Loading