Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8cc3097
feat(iModelGrid): add sort state persistence to IModelTable and IMode…
arome Jul 10, 2026
137e2f6
feat(iTwinTable): add sort state persistence to ITwinTable and ITwinG…
arome Jul 10, 2026
5eb72db
feat: implement controlled sort model for IModelGrid and ITwinGrid co…
arome Jul 10, 2026
553f764
feat(iModelBrowser): add controlled table sorting for iTwin/iModel ta…
arome Jul 10, 2026
e279c84
feat: enhance sorting functionality in IModelGrid components
arome Jul 30, 2026
9342a06
feat: enhance sorting capabilities for IModel and ITwin tables with n…
arome Jul 30, 2026
7896e66
Merge remote-tracking branch 'origin/main' into omar/save-sort-imodel…
arome Jul 31, 2026
13484ef
fix: update TypedGridSortItem to use GridSortModel for improved type …
arome Jul 31, 2026
69f0806
feat: add Jest virtual folders and update TypeScript configurations f…
arome Jul 31, 2026
72a569d
Update omar-save-sort-imodel-table_2026-07-10-18-00.json
arome Jul 31, 2026
367c765
feat: enhance sorting functionality in ITwinGrid and IModelGrid compo…
arome Aug 3, 2026
bb84e3c
feat: add sorting by created date to IModelGrid components and update…
arome Aug 3, 2026
8742b39
revert changes
arome Aug 3, 2026
98a338f
feat: add support for sorting by createdDateTime in useIModelSort hoo…
arome Aug 3, 2026
d4f134e
feat: update sorting functionality in IModelGrid and ITwinGrid compon…
arome Aug 3, 2026
01e1d56
feat: enhance sorting functionality in IModelGrid and ITwinGrid compo…
arome Aug 5, 2026
a154fa5
feat: refactor sorting implementation in IModelGrid and ITwinGrid com…
arome Aug 5, 2026
467fc86
revert to single sorting for mui grid
arome Aug 5, 2026
4a23a21
feat: enhance client-side sorting to support dynamic sort types in cl…
arome Aug 5, 2026
7110959
feat: refactor sorting implementation in ITwinTableMUI to use sortOpt…
arome Aug 5, 2026
849d44d
Merge branch 'main' into omar/save-sort-imodel-table
arome Aug 5, 2026
64f0c6b
Update packages/modules/imodel-browser/src/mui/containers/iModelGrid/…
arome Aug 7, 2026
7a4c61f
fix: update onSortOptionsChange to require sortOptions parameter in I…
arome Aug 7, 2026
2e9b176
feat: implement client-side sorting for ITwinGrid with support for fa…
arome Aug 7, 2026
a92d7df
Merge branch 'main' into omar/save-sort-imodel-table
arome Aug 13, 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
24 changes: 23 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,27 @@
"search.exclude": {
"**/CHANGELOG.md": true
},
"js/ts.preferences.autoImportSpecifierExcludeRegexes": ["^@mui/[^/]+$"]
"js/ts.preferences.autoImportSpecifierExcludeRegexes": ["^@mui/[^/]+$"],
"jest.virtualFolders": [
{
"name": "create-imodel",
"rootPath": "packages/modules/create-imodel"
},
{
"name": "delete-imodel",
"rootPath": "packages/modules/delete-imodel"
},
{
"name": "delete-itwin",
"rootPath": "packages/modules/delete-itwin"
},
{
"name": "imodel-browser",
"rootPath": "packages/modules/imodel-browser"
},
{
"name": "manage-versions",
"rootPath": "packages/modules/manage-versions"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/imodel-browser-react",
"comment": "Controlled table sorting for iTwin/iModel grids",
"type": "minor"
}
],
"packageName": "@itwin/imodel-browser-react"
}
50 changes: 25 additions & 25 deletions packages/apps/storybook/.storybook/itwinTheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ export const lightTheme = create({
base: "light",
brandTitle: "iTwin Admin Components",

colorPrimary: '#0071B8', // --iui-color-background-accent
colorSecondary: '#0071B8', // --iui-color-background-accent
colorPrimary: "#0071B8", // --iui-color-background-accent
colorSecondary: "#0071B8", // --iui-color-background-accent

// UI
appBg: '#FFFFFF', // --iui-color-background
appContentBg: '#FFFFFF', // --iui-color-background
appBorderColor: '#b3bcc1', // --iui-color-border
appBg: "#FFFFFF", // --iui-color-background
appContentBg: "#FFFFFF", // --iui-color-background
appBorderColor: "#b3bcc1", // --iui-color-border
appBorderRadius: 9,

// Typography
Expand All @@ -24,31 +24,31 @@ export const lightTheme = create({
'"Noto Sans Mono", ui-monospace, "SF Mono", SFMono-Regular, "DejaVu Sans Mono", Menlo, Consolas, monospace',

// Text colors
textColor: '#242424', //--iui-color-text
textColor: "#242424", //--iui-color-text

// Toolbar default and active colors
barTextColor: '#242424', //--iui-color-text
barSelectedColor: '#0071B8', // --iui-color-background-accent
barBg: '#FFFFFF', // --iui-color-background
barTextColor: "#242424", //--iui-color-text
barSelectedColor: "#0071B8", // --iui-color-background-accent
barBg: "#FFFFFF", // --iui-color-background

// Form colors
inputBg: '#FFFFFF', // --iui-color-background
inputBorder: '#b3bcc1', // --iui-color-border
inputTextColor: '#242424', //--iui-color-text
inputBg: "#FFFFFF", // --iui-color-background
inputBorder: "#b3bcc1", // --iui-color-border
inputTextColor: "#242424", //--iui-color-text
inputBorderRadius: 3,
});

export const darkTheme = create({
base: "dark",
brandTitle: "iTwin Admin Components",

colorPrimary: '#0071B8', // --iui-color-background-accent
colorSecondary: '#94befa', // --iui-color-text-accent
colorPrimary: "#0071B8", // --iui-color-background-accent
colorSecondary: "#94befa", // --iui-color-text-accent

// UI
appBg: '#333c41', // --iui-color-background
appContentBg: '#333c41', // --iui-color-background
appBorderColor: '#49555d', // --iui-color-border
appBg: "#333c41", // --iui-color-background
appContentBg: "#333c41", // --iui-color-background
appBorderColor: "#49555d", // --iui-color-border
appBorderRadius: 9,

// Typography
Expand All @@ -58,16 +58,16 @@ export const darkTheme = create({
'"Noto Sans Mono", ui-monospace, "SF Mono", SFMono-Regular, "DejaVu Sans Mono", Menlo, Consolas, monospace',

// Text colors
textColor: '#dbdbdb', //--iui-color-text
textColor: "#dbdbdb", //--iui-color-text

// Toolbar default and active colors
barTextColor: '#dbdbdb', //--iui-color-text
barSelectedColor: '#94befa', // --iui-color-text-accent
barBg: '#333c41', // --iui-color-background
barTextColor: "#dbdbdb", //--iui-color-text
barSelectedColor: "#94befa", // --iui-color-text-accent
barBg: "#333c41", // --iui-color-background

// Form colors
inputBg: '#333c41', // --iui-color-background
inputBorder: '#49555d', // --iui-color-border
inputTextColor: '#dbdbdb', //--iui-color-text
inputBorderRadius: 3
inputBg: "#333c41", // --iui-color-background
inputBorder: "#49555d", // --iui-color-border
inputTextColor: "#dbdbdb", //--iui-color-text
inputBorderRadius: 3,
});
4 changes: 4 additions & 0 deletions packages/apps/storybook/src/addon/preset.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
module.exports = {
managerEntries: [require.resolve("./manager.jsx")],
};
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import { LabeledInput, LabeledSelect } from "@itwin/itwinui-react";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";

import { iTwinAndAccessTokenArgTypes } from "../utils/storyHelp";

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import { UpdateIModel } from "@itwin/create-imodel-react";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React from "react";

import { accessTokenArgTypes } from "../utils/storyHelp";

function base64ToArrayBuffer(base64: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DeleteIModel, DeleteIModelProps } from "@itwin/delete-imodel-react";
import { Button } from "@itwin/itwinui-react";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React, { useState } from "react";

import { accessTokenArgTypes } from "../utils/storyHelp";

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DeleteITwin, DeleteITwinProps } from "@itwin/delete-itwin-react";
import { Button } from "@itwin/itwinui-react";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import React, { useState } from "react";

import { accessTokenArgTypes } from "../utils/storyHelp";

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import type { Meta, StoryObj } from "@storybook/react-webpack5";

import { accessTokenArgTypes } from "../utils/storyHelp";
import { Authentiated } from "./Authenticated";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import type { Meta, StoryObj } from "@storybook/react-webpack5";

import { ITwinAwareComponent } from "./ITwinAwareComponent";

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Avatar from "@mui/material/Avatar";
import AvatarGroup from "@mui/material/AvatarGroup";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import { action } from "storybook/actions";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import svgGeo from "@stratakit/icons/geospatial-features.svg";
import svgPin from "@stratakit/icons/pin.svg";
Expand All @@ -16,6 +15,7 @@ import svgStatusError from "@stratakit/icons/status-error.svg";
import svgStatusSuccess from "@stratakit/icons/status-success.svg";
import svgStatusWarning from "@stratakit/icons/status-warning.svg";
import React from "react";
import { action } from "storybook/actions";

import {
BaseCard,
Expand Down Expand Up @@ -255,7 +255,11 @@ export const OpenSitePlus: StoryObj<typeof BaseCardStory> = {
icon: svgGeo,
},
{ key: "share", label: "Share", onClick: action("menu: share clicked") },
{ key: "delete", label: "Delete", onClick: action("menu: delete clicked") },
{
key: "delete",
label: "Delete",
onClick: action("menu: delete clicked"),
},
],
actions: [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import {
type IModelFull,
type IModelGridProps as IModelGridMUIProps,
type IModelSortOptionsMUI,
DataStatus,
IModelCellColumn,
IModelGrid as ExternalComponent,
Expand Down Expand Up @@ -69,7 +70,6 @@ export default {

const baseArgs: IModelGridMUIProps = {
apiOverrides: { serverEnvironmentPrefix: "qa" },
sortOptions: { sortType: "name", descending: false },
actions: [
{
key: "open",
Expand All @@ -91,6 +91,7 @@ const baseArgs: IModelGridMUIProps = {
onClick: (iModel) => action("Details for " + iModel?.displayName)(iModel),
},
],
onSortOptionsChange: undefined,
};

export const Primary: StoryObj<typeof IModelGridMUI> = {
Expand Down Expand Up @@ -153,6 +154,91 @@ export const TableViewWithOverrides: StoryObj<typeof IModelGridMUI> = {
},
};

const TableWithControlledSortRender = (args: IModelGridMUIProps) => {
const [sortOptions, setSortOptions] = React.useState<
IModelSortOptionsMUI | undefined
>({ field: "name", direction: "desc" });
const field = sortOptions?.field;
const direction = sortOptions?.direction ?? "asc";

return (
<div>
<div
style={{
display: "flex",
gap: 8,
marginBottom: 8,
alignItems: "center",
}}
>
<Typography variant="body2">Sort by:</Typography>
<Chip
label="Name"
clickable
variant={field === "name" ? "filled" : "outlined"}
onClick={() => setSortOptions({ field: "name", direction })}
/>
<Chip
label="Last Modified"
clickable
variant={
field === "lastChangesetPushDateTime" ? "filled" : "outlined"
}
onClick={() =>
setSortOptions({ field: "lastChangesetPushDateTime", direction })
}
/>
<Chip
label="Created Date"
clickable
variant={field === "createdDateTime" ? "filled" : "outlined"}
onClick={() =>
setSortOptions({ field: "createdDateTime", direction })
}
/>
<Chip
label={direction === "desc" ? "↓ Descending" : "↑ Ascending"}
clickable
variant="outlined"
onClick={() =>
setSortOptions(
(prev) =>
prev && {
...prev,
direction: prev.direction === "desc" ? "asc" : "desc",
}
)
}
/>
</div>
<ExternalComponent
{...args}
sortOptions={sortOptions}
onSortOptionsChange={(newSortOptions) => {
action("sort options changed")(newSortOptions);
setSortOptions(newSortOptions);
}}
/>
</div>
);
};

export const TableWithControlledSort: StoryObj<typeof IModelGridMUI> = {
Comment thread
arome marked this conversation as resolved.
render: (args) => <TableWithControlledSortRender {...args} />,
args: {
...baseArgs,
viewMode: "cells",
},
parameters: {
docs: {
description: {
story:
"The sort state is fully controlled by the parent via `sortOptions` and `onSortOptionsChange`, so it can be saved anywhere the consumer wants.",
},
},
},
};

const OverrideApiDataWithLoadMoreRender = (args: IModelGridMUIProps) => {
const [data, setData] = React.useState<IModelFull[]>(initialData);
const [isLoading, setIsLoading] = React.useState(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import Avatar from "@mui/material/Avatar";
import AvatarGroup from "@mui/material/AvatarGroup";
import Box from "@mui/material/Box";
import Chip from "@mui/material/Chip";
import { action } from "storybook/actions";
import type { Meta, StoryObj } from "@storybook/react-webpack5";
import SvgDelete from "@stratakit/icons/delete.svg";
import svgRoad from "@stratakit/icons/road.svg";
import SvgShare from "@stratakit/icons/share.svg";
import React from "react";
import { action } from "storybook/actions";

import bridgeThumbnail from "../../utils/bridge.jpg";
import overpassThumbnail from "../../utils/overpass.jpg";
Expand Down
Loading
Loading