Skip to content

Commit

Permalink
Added user column to Named versions and Changeset tables + changeset …
Browse files Browse the repository at this point in the history
…information panel (#98)

- Added user column to both Named Versions and Changes table.
- Added Information panel for the changeset that includes- changeset id, description, created by, created date , application and Changed Files information.
  • Loading branch information
Pooja17-bentley authored Nov 16, 2023
1 parent 972542f commit a4fb572
Show file tree
Hide file tree
Showing 22 changed files with 623 additions and 71 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/manage-versions-react",
"comment": "Added user column to Named versions and Changeset tables",
"type": "minor"
}
],
"packageName": "@itwin/manage-versions-react"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@itwin/manage-versions-react",
"comment": "Added information panel for changeset",
"type": "minor"
}
],
"packageName": "@itwin/manage-versions-react"
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,19 @@ describe("ChangesetClient", () => {
}
);
});

it("should have correct url on getUsers", async () => {
mockHttpGet.mockResolvedValue({ changesets: MockedChangesetList() });

await changesetClient.getUsers(MOCKED_IMODEL_ID);
expect(mockHttpGet).toHaveBeenCalledWith(
`https://api.bentley.com/imodels/${MOCKED_IMODEL_ID}/users`,
{
headers: {
Prefer: "return=representation",
Accept: "application/vnd.bentley.itwin-platform.v2+json",
},
}
);
});
});
20 changes: 19 additions & 1 deletion packages/modules/manage-versions/src/clients/changesetClient.ts
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 { LogFunc } from "../components/ManageVersions/types";
import { Changeset, HttpHeaderNames } from "../models";
import { Changeset, HttpHeaderNames, User } from "../models";
import { RequestOptions } from "../models/requestOptions";
import { HttpClient } from "./httpClient";
import { UrlBuilder } from "./urlBuilder";
Expand Down Expand Up @@ -37,4 +37,22 @@ export class ChangesetClient {
)
.then((resp) => resp.changesets);
}

public async getUsers(imodelId: string): Promise<User[]> {
return this._http
.get(
`${UrlBuilder.buildGetUsersUrl(
imodelId,
this._serverEnvironmentPrefix
)}`,
{
headers: {
[HttpHeaderNames.Prefer]: "return=representation",
[HttpHeaderNames.Accept]:
"application/vnd.bentley.itwin-platform.v2+json",
},
}
)
.then((resp) => resp.users);
}
}
9 changes: 9 additions & 0 deletions packages/modules/manage-versions/src/clients/urlBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,13 @@ export class UrlBuilder {
serverEnvironmentPrefix
)}/imodels/${imodelId}/changesets`;
}

public static buildGetUsersUrl(
imodelId: string,
serverEnvironmentPrefix?: string
) {
return `${this.getBaseUrl(
serverEnvironmentPrefix
)}/imodels/${imodelId}/users`;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
.iac-info-panel {
width: 400px;
min-width: 400px;
.iac-info-panel-body {
.iac-info-panel-container {
padding: var(--iui-size-xs);

& + div {
border-top: 1px solid var(--iui-color-border-subtle);
}
}
.iac-info-panel-details {
padding: var(--iui-size-xs);
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
.iac-info-panel-data-value {
flex: 2;
min-width: 50%;
}
}

.iac-info-panel-property {
display: flex;
flex: 1;
margin-right: var(--iui-size-m);
justify-content: right;
color: var(--iui-color-text-disabled);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { render, screen } from "@testing-library/react";
import React from "react";

import { ConfigProvider } from "../../common/configContext";
import { MOCKED_CONFIG_PROPS, MockedChangeset } from "../../mocks";
import { localeDateWithTimeFormat } from "../../models/utils";
import { defaultStrings } from "../ManageVersions/ManageVersions";
import {
ChangesetInfoPanelProps,
ChangesetInformationPanel,
} from "./ChangesetInformationPanel";

const mockedCreatedDateTime = localeDateWithTimeFormat(
new Date(MockedChangeset().pushDateTime)
);

const renderComponent = (initialProps?: Partial<ChangesetInfoPanelProps>) => {
const props: ChangesetInfoPanelProps = {
changeset: MockedChangeset(),
onClose: jest.fn(),
stringOverrides: defaultStrings.informationPanelStringOverrides,
...initialProps,
};
return render(
<ConfigProvider {...MOCKED_CONFIG_PROPS}>
<ChangesetInformationPanel {...props} />
</ConfigProvider>
);
};

describe("ChangesetInformationPanel test", () => {
it("should show required details in information-panel", () => {
const { container } = renderComponent();
const expectedValues = [
{ property: "Created By: ", value: MockedChangeset().createdBy },
{ property: "Date Created: ", value: mockedCreatedDateTime },
{ property: "Application: ", value: MockedChangeset().application.name },
{
property: "Changed Files: ",
value: MockedChangeset().synchronizationInfo.changedFiles.join(","),
},
];

const changeset_desc = container.querySelector(
".iac-info-panel-container"
) as Element;

const info_panel_details = container.querySelectorAll(
".iac-info-panel-details"
);
expect(info_panel_details.length).toBe(4);
info_panel_details.forEach((detailsElement, index) => {
const info_panel_property = detailsElement.querySelector(
".iac-info-panel-property"
) as Element;
const info_panel_property_value = detailsElement.querySelector(
".iac-info-panel-data-value"
) as Element;
expect(info_panel_property.textContent).toBe(
expectedValues[index].property
);
expect(info_panel_property_value.textContent).toBe(
expectedValues[index].value
);
});

expect(changeset_desc.textContent).toBe(MockedChangeset().description);
});

it("should have close icon in the panel header", () => {
renderComponent();
const closeButton = screen.getByLabelText("Close");
expect(closeButton).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import "./ChangesetInformationPanel.scss";

import {
InformationPanel,
InformationPanelBody,
InformationPanelHeader,
Text,
} from "@itwin/itwinui-react";
import React from "react";

import { Changeset } from "../../models/changeset";
import {
informationPanelDefaultStrings,
localeDateWithTimeFormat,
} from "../../models/utils";
import { InformationPanelStringOverrides } from "../ManageVersions/types";

export interface ChangesetInfoPanelProps {
changeset: Changeset;
onClose: (e: React.MouseEvent<Element, MouseEvent>) => void;
stringOverrides?: InformationPanelStringOverrides;
}

export const ChangesetInformationPanel = (props: ChangesetInfoPanelProps) => {
const {
changeset,
onClose,
stringOverrides = informationPanelDefaultStrings,
} = props;
const files: string[] = changeset.synchronizationInfo?.changedFiles
? changeset.synchronizationInfo.changedFiles
: [stringOverrides.noValue];

const createdDateTime = localeDateWithTimeFormat(
new Date(changeset.pushDateTime)
);

const renderProperty = (property: string, value: string | undefined) => {
return (
<div className="iac-info-panel-details">
<span className="iac-info-panel-property">{`${property}: `}</span>
<span className="iac-info-panel-data-value">{value}</span>
</div>
);
};

return (
<InformationPanel className={"iac-info-panel"} resizable={false} isOpen>
<InformationPanelHeader onClose={onClose}>
<Text variant="subheading">
{stringOverrides.title + changeset.index}
</Text>
</InformationPanelHeader>
<InformationPanelBody>
<div className="iac-info-panel-body">
<Text className="iac-info-panel-container">
{changeset.description}
</Text>
<div className="iac-info-panel-container">
{renderProperty(
stringOverrides.createdBy,
changeset.createdBy ?? ""
)}
{renderProperty(stringOverrides.createdDate, createdDateTime ?? "")}
{renderProperty(
stringOverrides.application,
changeset.application.name ?? stringOverrides.noValue
)}
</div>

<div className="iac-info-panel-container">
<Text variant="leading">
{stringOverrides.connectionAttributes}
</Text>
{renderProperty(stringOverrides.changedFiles, files.join(","))}
</div>
</div>
</InformationPanelBody>
</InformationPanel>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@
.iui-table-body .iui-table-cell {
word-break: break-word;
}
}

.iac-create-version-icon-hidden {
visibility: hidden;
}

.iac-changes-tab-actions {
gap: var(--iui-size-2xs);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 { render, screen, within } from "@testing-library/react";
import { fireEvent, render, screen, within } from "@testing-library/react";
import React from "react";

import { ConfigProvider } from "../../../common/configContext";
Expand Down Expand Up @@ -39,20 +39,22 @@ describe("ChangesTab", () => {

rows.forEach((row, index) => {
const cells = row.querySelectorAll(".iui-table-cell");
expect(cells.length).toBe(5);
expect(cells.length).toBe(6);
expect(cells[0].textContent).toContain(MockedChangeset(index).index);
expect(cells[1].textContent).toContain(
MockedChangeset(index).description
);
expect(cells[2].textContent).toContain(
expect(cells[2].textContent).toContain(MockedChangeset(index).createdBy);
expect(cells[3].textContent).toContain(
MockedChangeset(index).synchronizationInfo.changedFiles.join(", ")
);
expect(cells[3].textContent).toContain(
expect(cells[4].textContent).toContain(
new Date(MockedChangeset(index).pushDateTime).toLocaleString()
);
within(cells[4] as HTMLElement).getByTitle(
within(cells[5] as HTMLElement).getByTitle(
defaultStrings.createNamedVersion
);
within(cells[5] as HTMLElement).getByTitle("Information Panel");
});
});

Expand All @@ -77,7 +79,7 @@ describe("ChangesTab", () => {
});

it("should not show create version icon when changeset already has a version", () => {
const { container } = renderComponent({
const { container, queryByTitle } = renderComponent({
changesets: [
MockedChangeset(1, {
_links: { namedVersion: { href: "https://test.url" } },
Expand All @@ -87,9 +89,25 @@ describe("ChangesTab", () => {
const rows = container.querySelectorAll(".iui-table-body .iui-table-row");
expect(rows.length).toBe(1);

const createVersionicon = screen.queryByTitle(
defaultStrings.createNamedVersion
const createVersionIcon = queryByTitle(defaultStrings.createNamedVersion);
const classAttribute = (createVersionIcon as HTMLElement).getAttribute(
"class"
);
expect(createVersionicon).toBeFalsy();
expect(classAttribute).toContain("iac-create-version-icon-hidden");
});

it("should show information panel icon for each changeset row", () => {
const { container } = renderComponent({
changesets: MockedChangesetList(),
});
const rowgroup = screen.getAllByRole("rowgroup")[0];
const infoIcons = within(rowgroup).queryAllByTitle("Information Panel");
const rows = within(rowgroup).queryAllByRole("row");

expect(infoIcons.length).toBe(rows.length);
//should open information panel
fireEvent.click(infoIcons[0]);
const panel = container.querySelector(".iac-info-panel");
expect(panel).toBeTruthy();
});
});
Loading

0 comments on commit a4fb572

Please sign in to comment.