Skip to content

Commit 10dd53e

Browse files
authored
Add sorting to iTwin Grid columns (#192)
* Add sorting capability to ITwinGrid component * Refactor sorting logic in IModelGridInternal to simplify state management * Update table column label from "Last Modified" to "Created Date" * Update ITwinGrid to use lastModifiedDateTime instead of createdDateTime for Last Modified column * Fix table rows not taking the full width * Add patch to fix sorting in iTwinGrid/iModelGrid table
1 parent 2266dc2 commit 10dd53e

6 files changed

Lines changed: 72 additions & 78 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@itwin/imodel-browser-react",
5+
"comment": "Fix sort in iModel/iTwin grid tables",
6+
"type": "major"
7+
}
8+
],
9+
"packageName": "@itwin/imodel-browser-react"
10+
}

packages/apps/storybook/src/imodel-browser/IModelGrid.stories.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,11 @@ OverrideCellData.args = {
6868
name: (props) =>
6969
props.value.includes("a") ? (
7070
<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
71-
<IconButton size="small" styleType="borderless">
71+
<IconButton
72+
aria-label="apple-icon"
73+
size="small"
74+
styleType="borderless"
75+
>
7276
<SvgApple />
7377
</IconButton>
7478
{props.value}

packages/modules/imodel-browser/src/containers/ITwinGrid/ITwinGrid.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ export interface ITwinGridProps {
7878
filterOptions?: ITwinFilterOptions;
7979
/**
8080
* Set the `$orderby` parameter when fetching iTwins from the iTwin API, e.g. `displayName ASC`.
81-
*
8281
* This only has an effect when the `requestType` is empty - it does not apply to "favorites" or "recents".
83-
*
8482
* See https://developer.bentley.com/apis/itwins/operations/get-my-itwins/#odata-queries for details.
8583
*/
8684
orderbyOptions?: string;
@@ -266,6 +264,7 @@ export const ITwinGrid = ({
266264
: strings.noITwins
267265
}
268266
isLoading={fetchStatus === DataStatus.Fetching}
267+
isSortable
269268
onBottomReached={fetchMore}
270269
autoResetFilters={false}
271270
autoResetSortBy={false}

packages/modules/imodel-browser/src/containers/ITwinGrid/useITwinTableConfig.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export const useITwinTableConfig = ({
5353
id: ITwinCellColumn.Favorite,
5454
Header: strings.tableColumnFavorites,
5555
accessor: "id",
56+
disableSortBy: true,
5657
width: 70,
5758
Cell: (props: CellProps<ITwinFull>) => {
5859
const isFavorite = iTwinFavorites.has(props.value);
@@ -115,10 +116,10 @@ export const useITwinTableConfig = ({
115116
{
116117
id: ITwinCellColumn.LastModified,
117118
Header: strings.tableColumnLastModified,
118-
accessor: "createdDateTime",
119-
maxWidth: 350,
119+
// Use lastModifiedDateTime (not createdDateTime) so this column reflects the last update, matching the "Last modified" header.
120+
accessor: "lastModifiedDateTime",
120121
Cell: (props: CellProps<ITwinFull>) => {
121-
const date = props.data[props.row.index].createdDateTime;
122+
const date = props.data[props.row.index].lastModifiedDateTime;
122123
return cellOverrides.LastModified
123124
? cellOverrides.LastModified(props)
124125
: date

packages/modules/imodel-browser/src/containers/iModelGrid/IModelGrid.tsx

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ import { IModelGhostTile } from "../iModelTiles/IModelGhostTile";
2929
import { IModelTile, IModelTileProps } from "../iModelTiles/IModelTile";
3030
import styles from "./IModelGrid.module.scss";
3131
import { DEFAULT_PAGE_SIZE, useIModelData } from "./useIModelData";
32-
import { useIModelTableConfig } from "./useIModelTableConfig";
32+
import {
33+
IModelTableStrings,
34+
useIModelTableConfig,
35+
} from "./useIModelTableConfig";
3336

3437
export interface IModelGridProps {
3538
/**
@@ -57,19 +60,9 @@ export interface IModelGridProps {
5760
/** Static props to apply over each tile, mainly used for tileProps, overrides IModelGrid provided values */
5861
tileOverrides?: Partial<IModelTileProps>;
5962
/** Strings displayed by the browser */
60-
stringsOverrides?: {
61-
/** Displayed for table name header. */
62-
tableColumnName?: string;
63-
/** Displayed for table description header. */
64-
tableColumnDescription?: string;
65-
/** Displayed for table lastModified header. */
66-
tableColumnLastModified?: string;
67-
/** Displayed on table while loading data. */
68-
tableLoadingData?: string;
63+
stringsOverrides?: Partial<IModelTableStrings> & {
6964
/** Displayed after successful fetch search, but no iModel is returned. */
7065
noIModelSearch?: string;
71-
/** Displayed after successful fetch search, but no iModel is returned, along with noIModelSearch text. */
72-
noIModelSearchSubtext?: string;
7366
/** Displayed after successful fetch, but no iModels are returned. */
7467
noIModels?: string;
7568
/** Displayed when the component is mounted and there is no iTwin or asset Id. */
@@ -78,6 +71,10 @@ export interface IModelGridProps {
7871
noAuthentication?: string;
7972
/** Generic message displayed if an error occurs while fetching. */
8073
error?: string;
74+
/** Displayed on table while loading data. */
75+
tableLoadingData?: string;
76+
/** Displayed after successful fetch search, but no iModel is returned, along with noIModelSearch text. */
77+
noIModelSearchSubtext?: string;
8178
/** Displayed in context menu for removing iModel from recents. */
8279
removeFromRecents?: string;
8380
};
@@ -142,11 +139,11 @@ export const IModelGrid = (props: IModelGridProps) => {
142139
serverEnvironmentPrefix={props.apiOverrides?.serverEnvironmentPrefix}
143140
disabled={props.tileOverrides?.hideFavoriteIcon}
144141
>
145-
<ITwinGridInternal {...props} />
142+
<IModelGridInternal {...props} />
146143
</IModelFavoritesProvider>
147144
);
148145
};
149-
const ITwinGridInternal = ({
146+
const IModelGridInternal = ({
150147
accessToken,
151148
apiOverrides,
152149
iModelActions,
@@ -172,31 +169,27 @@ const ITwinGridInternal = ({
172169
disableAddToRecents = false,
173170
}: IModelGridProps) => {
174171
const [sort, setSort] = React.useState<IModelSortOptions>(sortOptions);
175-
const [isSortOnTable, setIsSortOnTable] = React.useState(false);
176172

177173
React.useEffect(() => {
178-
if (!isSortOnTable) {
179-
const defaultTableSort: IModelSortOptions = {
180-
sortType: "name",
181-
descending: false,
182-
};
183-
setSort(
184-
viewMode === "cells"
185-
? defaultTableSort
186-
: {
187-
sortType: sortOptions.sortType,
188-
descending: sortOptions.descending,
189-
}
190-
);
191-
}
192-
}, [isSortOnTable, sortOptions.descending, sortOptions.sortType, viewMode]);
174+
setSort(
175+
viewMode === "cells"
176+
? {
177+
sortType: "name",
178+
descending: false,
179+
}
180+
: {
181+
sortType: sortOptions.sortType,
182+
descending: sortOptions.descending,
183+
}
184+
);
185+
}, [sortOptions.descending, sortOptions.sortType, viewMode]);
193186

194187
const strings = _mergeStrings(
195188
{
196189
tableColumnFavorites: "",
197190
tableColumnName: "Name",
198191
tableColumnDescription: "Description",
199-
tableColumnLastModified: "Last Modified",
192+
tableColumnCreatedDate: "Created Date",
200193
tableLoadingData: "Loading...",
201194
noIModelSearch: "No results found",
202195
noIModelSearchSubtext:
@@ -349,7 +342,6 @@ const ITwinGridInternal = ({
349342
apiOverrides={tileApiOverrides}
350343
useTileState={useIndividualState}
351344
refetchIModels={refetchIModels}
352-
{...cellOverrides}
353345
{...tileOverrides}
354346
tileProps={
355347
tileOverrides
@@ -402,23 +394,6 @@ const ITwinGridInternal = ({
402394
}
403395
isLoading={fetchStatus === DataStatus.Fetching}
404396
isSortable
405-
onSort={(state) => {
406-
const sortBy =
407-
state.sortBy.length > 0 ? state.sortBy[0] : undefined;
408-
setIsSortOnTable(sortBy?.id !== undefined);
409-
if (
410-
!sortBy ||
411-
sortBy.desc === undefined ||
412-
(sortBy.id !== "name" && sortBy.id !== "createdDateTime")
413-
) {
414-
return;
415-
}
416-
setSort({
417-
sortType: sortBy.id,
418-
descending: sortBy.desc,
419-
});
420-
}}
421-
manualSortBy
422397
onBottomReached={fetchMore}
423398
autoResetFilters={false}
424399
autoResetSortBy={false}

packages/modules/imodel-browser/src/containers/iModelGrid/useIModelTableConfig.tsx

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,24 @@ import {
1515
ContextMenuBuilderItem,
1616
} from "../../utils/_buildMenuOptions";
1717

18+
export interface IModelTableStrings {
19+
/** Displayed for table name header. */
20+
tableColumnName: string;
21+
/** Displayed for table description header. */
22+
tableColumnDescription: string;
23+
/** Displayed for table created date header. */
24+
tableColumnCreatedDate: string;
25+
/** Displayed for table favorites header. */
26+
tableColumnFavorites: string;
27+
/** Text for adding an iModel to favorites. */
28+
addToFavorites: string;
29+
/** Text for removing an iModel from favorites. */
30+
removeFromFavorites: string;
31+
}
1832
export interface useIModelTableConfigProps {
1933
iModelActions: ContextMenuBuilderItem<IModelFull>[] | undefined;
2034
onThumbnailClick: ((iModel: IModelFull) => void) | undefined;
21-
strings: {
22-
tableColumnName: string;
23-
tableColumnDescription: string;
24-
tableColumnLastModified: string;
25-
noIModelSearch: string;
26-
noIModels: string;
27-
noContext: string;
28-
noAuthentication: string;
29-
error: string;
30-
tableColumnFavorites: string;
31-
addToFavorites: string;
32-
removeFromFavorites: string;
33-
};
35+
strings: IModelTableStrings;
3436
refetchIModels: () => void;
3537
cellOverrides?: IModelCellOverrides;
3638
}
@@ -60,6 +62,7 @@ export const useIModelTableConfig = ({
6062
id: IModelCellColumn.Favorite,
6163
Header: strings.tableColumnFavorites,
6264
accessor: "id",
65+
disableSortBy: true,
6366
width: 70,
6467
Cell: (props: CellProps<IModelFull>) => {
6568
const isFavorite = favoritesContext?.favorites.has(props.value);
@@ -90,9 +93,11 @@ export const useIModelTableConfig = ({
9093
maxWidth: 350,
9194
Cell: (props: CellProps<IModelFull>) => (
9295
<div data-tip={props.row.original.name}>
93-
<span>
94-
{cellOverrides.name ? cellOverrides.name(props) : props.value}
95-
</span>
96+
{cellOverrides.name ? (
97+
cellOverrides.name(props)
98+
) : (
99+
<span>{props.value}</span>
100+
)}
96101
</div>
97102
),
98103
},
@@ -103,17 +108,17 @@ export const useIModelTableConfig = ({
103108
disableSortBy: true,
104109
Cell: (props: CellProps<IModelFull>) => (
105110
<div data-tip={props.row.original.description}>
106-
<span>
107-
{cellOverrides.description
108-
? cellOverrides.description(props)
109-
: props.value}
110-
</span>
111+
{cellOverrides.description ? (
112+
cellOverrides.description(props)
113+
) : (
114+
<span>{props.value}</span>
115+
)}
111116
</div>
112117
),
113118
},
114119
{
115120
id: IModelCellColumn.CreatedDateTime,
116-
Header: strings.tableColumnLastModified,
121+
Header: strings.tableColumnCreatedDate,
117122
accessor: "createdDateTime",
118123
maxWidth: 350,
119124
Cell: (props: CellProps<IModelFull>) => {
@@ -169,7 +174,7 @@ export const useIModelTableConfig = ({
169174
strings.tableColumnFavorites,
170175
strings.tableColumnName,
171176
strings.tableColumnDescription,
172-
strings.tableColumnLastModified,
177+
strings.tableColumnCreatedDate,
173178
strings.addToFavorites,
174179
strings.removeFromFavorites,
175180
favoritesContext,

0 commit comments

Comments
 (0)