Merge with upstream

This commit is contained in:
Florian Scholdei
2020-10-08 00:47:45 +02:00
69 changed files with 3031 additions and 1020 deletions

View File

@@ -21,85 +21,86 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React from "react";
import React, { FC, useState } from "react";
import { connect } from "react-redux";
import { compose } from "redux";
import { RouteComponentProps, withRouter } from "react-router-dom";
import { WithTranslation, withTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Repository } from "@scm-manager/ui-types";
import { confirmAlert, DeleteButton, ErrorNotification, Level } from "@scm-manager/ui-components";
import { ConfirmAlert, DeleteButton, ErrorNotification, Level } from "@scm-manager/ui-components";
import { deleteRepo, getDeleteRepoFailure, isDeleteRepoPending } from "../modules/repos";
type Props = RouteComponentProps &
WithTranslation & {
loading: boolean;
error: Error;
repository: Repository;
confirmDialog?: boolean;
deleteRepo: (p1: Repository, p2: () => void) => void;
type Props = {
loading: boolean;
error: Error;
repository: Repository;
confirmDialog?: boolean;
deleteRepo: (p1: Repository, p2: () => void) => void;
};
const DeleteRepo: FC<Props> = ({ confirmDialog = true, repository, deleteRepo, loading, error }: Props) => {
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
const [t] = useTranslation("repos");
const history = useHistory();
const deleted = () => {
history.push("/repos/");
};
class DeleteRepo extends React.Component<Props> {
static defaultProps = {
confirmDialog: true
const deleteRepoCallback = () => {
deleteRepo(repository, deleted);
};
deleted = () => {
this.props.history.push("/repos/");
const confirmDelete = () => {
setShowConfirmAlert(true);
};
deleteRepo = () => {
this.props.deleteRepo(this.props.repository, this.deleted);
const isDeletable = () => {
return repository._links.delete;
};
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t("deleteRepo.confirmAlert.title"),
message: t("deleteRepo.confirmAlert.message"),
buttons: [
{
className: "is-outlined",
label: t("deleteRepo.confirmAlert.submit"),
onClick: () => this.deleteRepo()
},
{
label: t("deleteRepo.confirmAlert.cancel"),
onClick: () => null
}
]
});
};
const action = confirmDialog ? confirmDelete : deleteRepoCallback;
isDeletable = () => {
return this.props.repository._links.delete;
};
render() {
const { loading, error, confirmDialog, t } = this.props;
const action = confirmDialog ? this.confirmDelete : this.deleteRepo;
if (!this.isDeletable()) {
return null;
}
if (!isDeletable()) {
return null;
}
if (showConfirmAlert) {
return (
<>
<ErrorNotification error={error} />
<Level
left={
<p>
<strong>{t("deleteRepo.subtitle")}</strong>
<br />
{t("deleteRepo.description")}
</p>
<ConfirmAlert
title={t("deleteRepo.confirmAlert.title")}
message={t("deleteRepo.confirmAlert.message")}
buttons={[
{
className: "is-outlined",
label: t("deleteRepo.confirmAlert.submit"),
onClick: () => deleteRepoCallback()
},
{
label: t("deleteRepo.confirmAlert.cancel"),
onClick: () => null
}
right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />}
/>
</>
]}
close={() => setShowConfirmAlert(false)}
/>
);
}
}
return (
<>
<ErrorNotification error={error} />
<Level
left={
<p>
<strong>{t("deleteRepo.subtitle")}</strong>
<br />
{t("deleteRepo.description")}
</p>
}
right={<DeleteButton label={t("deleteRepo.button")} action={action} loading={loading} />}
/>
</>
);
};
const mapStateToProps = (state: any, ownProps: Props) => {
const { namespace, name } = ownProps.repository;
@@ -119,4 +120,4 @@ const mapDispatchToProps = (dispatch: any) => {
};
};
export default compose(connect(mapStateToProps, mapDispatchToProps), withRouter, withTranslation("repos"))(DeleteRepo);
export default connect(mapStateToProps, mapDispatchToProps)(DeleteRepo);

View File

@@ -26,7 +26,7 @@ import { WithTranslation, withTranslation } from "react-i18next";
import { Select } from "@scm-manager/ui-components";
type Props = WithTranslation & {
availableRoles: string[];
availableRoles?: string[];
handleRoleChange: (p: string) => void;
role: string;
label?: string;

View File

@@ -21,16 +21,19 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React from "react";
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
import React, { FC } from "react";
// eslint-disable-next-line no-restricted-imports
import { mount, shallow } from "@scm-manager/ui-tests/enzyme-router";
// eslint-disable-next-line no-restricted-imports
import "@scm-manager/ui-tests/enzyme";
// eslint-disable-next-line no-restricted-imports
import "@scm-manager/ui-tests/i18n";
import DeletePermissionButton from "./DeletePermissionButton";
import { confirmAlert } from "@scm-manager/ui-components";
jest.mock("@scm-manager/ui-components", () => ({
confirmAlert: jest.fn(),
ConfirmAlert: (({ children }) => <div className="modal">{children}</div>) as FC<never>,
DeleteButton: require.requireActual("@scm-manager/ui-components").DeleteButton
}));
@@ -40,6 +43,9 @@ describe("DeletePermissionButton", () => {
_links: {}
};
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-empty-function
const navLink = shallow(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
expect(navLink.text()).toBe("");
});
@@ -53,6 +59,9 @@ describe("DeletePermissionButton", () => {
}
};
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-empty-function
const deleteIcon = mount(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
expect(deleteIcon.html()).not.toBe("");
});
@@ -66,10 +75,13 @@ describe("DeletePermissionButton", () => {
}
};
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-empty-function
const button = mount(<DeletePermissionButton permission={permission} deletePermission={() => {}} />);
button.find(".fa-trash").simulate("click");
expect(confirmAlert.mock.calls.length).toBe(1);
expect(button.find(".modal")).toBeTruthy();
});
it("should call the delete permission function with delete url", () => {
@@ -82,11 +94,14 @@ describe("DeletePermissionButton", () => {
};
let calledUrl = null;
function capture(permission) {
calledUrl = permission._links.delete.href;
}
const button = mount(
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
// @ts-ignore
<DeletePermissionButton permission={permission} confirmDialog={false} deletePermission={capture} />
);
button.find(".fa-trash").simulate("click");

View File

@@ -21,12 +21,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React from "react";
import { WithTranslation, withTranslation } from "react-i18next";
import React, { FC, useState } from "react";
import { useTranslation } from "react-i18next";
import { Permission } from "@scm-manager/ui-types";
import { confirmAlert } from "@scm-manager/ui-components";
import { ConfirmAlert } from "@scm-manager/ui-components";
type Props = WithTranslation & {
type Props = {
permission: Permission;
namespace: string;
repoName: string;
@@ -35,53 +35,62 @@ type Props = WithTranslation & {
loading: boolean;
};
class DeletePermissionButton extends React.Component<Props> {
static defaultProps = {
confirmDialog: true
const DeletePermissionButton: FC<Props> = ({
confirmDialog = true,
permission,
namespace,
deletePermission,
repoName
}) => {
const [showConfirmAlert, setShowConfirmAlert] = useState(false);
const [t] = useTranslation("repos");
const deletePermissionCallback = () => {
deletePermission(permission, namespace, repoName);
};
deletePermission = () => {
this.props.deletePermission(this.props.permission, this.props.namespace, this.props.repoName);
const confirmDelete = () => {
setShowConfirmAlert(true);
};
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t("permission.delete-permission-button.confirm-alert.title"),
message: t("permission.delete-permission-button.confirm-alert.message"),
buttons: [
{
className: "is-outlined",
label: t("permission.delete-permission-button.confirm-alert.submit"),
onClick: () => this.deletePermission()
},
{
label: t("permission.delete-permission-button.confirm-alert.cancel"),
onClick: () => null
}
]
});
const isDeletable = () => {
return permission._links.delete;
};
isDeletable = () => {
return this.props.permission._links.delete;
};
const action = confirmDialog ? confirmDelete : deletePermissionCallback;
render() {
const { confirmDialog } = this.props;
const action = confirmDialog ? this.confirmDelete : this.deletePermission;
if (!isDeletable()) {
return null;
}
if (!this.isDeletable()) {
return null;
}
if (showConfirmAlert) {
return (
<a className="level-item" onClick={action}>
<span className="icon is-small">
<i className="fas fa-trash" />
</span>
</a>
<ConfirmAlert
title={t("permission.delete-permission-button.confirm-alert.title")}
message={t("permission.delete-permission-button.confirm-alert.message")}
buttons={[
{
className: "is-outlined",
label: t("permission.delete-permission-button.confirm-alert.submit"),
onClick: () => deletePermissionCallback()
},
{
label: t("permission.delete-permission-button.confirm-alert.cancel"),
onClick: () => null
}
]}
close={() => setShowConfirmAlert(false)}
/>
);
}
}
export default withTranslation("repos")(DeletePermissionButton);
return (
<a className="level-item" onClick={action}>
<span className="icon is-small">
<i className="fas fa-trash" />
</span>
</a>
);
};
export default DeletePermissionButton;