finish delete implementation and restructure components

This commit is contained in:
Sebastian Sdorra
2018-08-06 10:08:28 +02:00
parent f0794c9d15
commit ffbfdff52e
17 changed files with 183 additions and 14 deletions

View File

@@ -28,5 +28,14 @@
},
"repository-form": {
"submit": "Speichern"
},
"delete-nav-action": {
"label": "Delete",
"confirm-alert": {
"title": "Delete repository",
"message": "Do you really want to delete the repository?",
"submit": "Yes",
"cancel": "No"
}
}
}

View File

@@ -0,0 +1,59 @@
//@flow
import React from "react";
import { translate } from "react-i18next";
import { confirmAlert } from "../../components/modals/ConfirmAlert";
import { NavAction } from "../../components/navigation";
import type { Repository } from "../types/Repositories";
type Props = {
repository: Repository,
confirmDialog?: boolean,
delete: Repository => void,
// context props
t: string => string
};
class DeleteNavAction extends React.Component<Props> {
static defaultProps = {
confirmDialog: true
};
delete = () => {
this.props.delete(this.props.repository);
};
confirmDelete = () => {
const { t } = this.props;
confirmAlert({
title: t("delete-nav-action.confirm-alert.title"),
message: t("delete-nav-action.confirm-alert.message"),
buttons: [
{
label: t("delete-nav-action.confirm-alert.submit"),
onClick: () => this.delete()
},
{
label: t("delete-nav-action.confirm-alert.cancel"),
onClick: () => null
}
]
});
};
isDeletable = () => {
return this.props.repository._links.delete;
};
render() {
const { confirmDialog, t } = this.props;
const action = confirmDialog ? this.confirmDelete : this.delete();
if (!this.isDeletable()) {
return null;
}
return <NavAction label={t("delete-nav-action.label")} action={action} />;
}
}
export default translate("repos")(DeleteNavAction);

View File

@@ -0,0 +1,79 @@
import React from "react";
import { mount, shallow } from "enzyme";
import "../../tests/enzyme";
import "../../tests/i18n";
import DeleteNavAction from "./DeleteNavAction";
import { confirmAlert } from "../../components/modals/ConfirmAlert";
jest.mock("../../components/modals/ConfirmAlert");
describe("DeleteNavAction", () => {
it("should render nothing, if the delete link is missing", () => {
const repository = {
_links: {}
};
const navLink = shallow(
<DeleteNavAction repository={repository} delete={() => {}} />
);
expect(navLink.text()).toBe("");
});
it("should render the navLink", () => {
const repository = {
_links: {
delete: {
href: "/repositories"
}
}
};
const navLink = mount(
<DeleteNavAction repository={repository} delete={() => {}} />
);
expect(navLink.text()).not.toBe("");
});
it("should open the confirm dialog on navLink click", () => {
const repository = {
_links: {
delete: {
href: "/repositorys"
}
}
};
const navLink = mount(
<DeleteNavAction repository={repository} delete={() => {}} />
);
navLink.find("a").simulate("click");
expect(confirmAlert.mock.calls.length).toBe(1);
});
it("should call the delete repository function with delete url", () => {
const repository = {
_links: {
delete: {
href: "/repos"
}
}
};
let calledUrl = null;
function capture(repository) {
calledUrl = repository._links.delete.href;
}
const navLink = mount(
<DeleteNavAction
repository={repository}
confirmDialog={false}
delete={capture}
/>
);
navLink.find("a").simulate("click");
expect(calledUrl).toBe("/repos");
});
});

View File

@@ -1,12 +1,12 @@
// @flow
import React from "react";
import { translate } from "react-i18next";
import { InputField, Select } from "../../components/forms";
import { SubmitButton } from "../../components/buttons";
import type { Repository } from "../types/Repositories";
import { InputField, Select } from "../../../components/forms/index";
import { SubmitButton } from "../../../components/buttons/index";
import type { Repository } from "../../types/Repositories";
import * as validator from "./repositoryValidation";
import type { RepositoryType } from "../types/RepositoryTypes";
import Textarea from "../../components/forms/Textarea";
import type { RepositoryType } from "../../types/RepositoryTypes";
import Textarea from "../../../components/forms/Textarea";
type Props = {
submitForm: Repository => void,

View File

@@ -0,0 +1,2 @@
import RepositoryForm from "./RepositoryForm";
export default RepositoryForm;

View File

@@ -1,5 +1,5 @@
// @flow
import * as generalValidator from "../../components/validation";
import * as generalValidator from "../../../components/validation";
export const isNameValid = (name: string) => {
return generalValidator.isNameValid(name);

View File

@@ -2,12 +2,12 @@
import React from "react";
import { Link } from "react-router-dom";
import injectSheet from "react-jss";
import type { Repository } from "../types/Repositories";
import DateFromNow from "../../components/DateFromNow";
import type { Repository } from "../../types/Repositories";
import DateFromNow from "../../../components/DateFromNow";
import RepositoryEntryLink from "./RepositoryEntryLink";
import classNames from "classnames";
import icon from "../../images/blib.jpg";
import icon from "../../../images/blib.jpg";
// TODO we need a variable or something central for the hover

View File

@@ -1,6 +1,6 @@
//@flow
import React from "react";
import type { RepositoryGroup } from "../types/Repositories";
import type { RepositoryGroup } from "../../types/Repositories";
import injectSheet from "react-jss";
import classNames from "classnames";
import RepositoryEntry from "./RepositoryEntry";

View File

@@ -1,7 +1,7 @@
//@flow
import React from "react";
import type { Repository } from "../types/Repositories";
import type { Repository } from "../../types/Repositories";
import groupByNamespace from "./groupByNamespace";
import RepositoryGroupEntry from "./RepositoryGroupEntry";

View File

@@ -1,5 +1,5 @@
// @flow
import type { Repository, RepositoryGroup } from "../types/Repositories";
import type { Repository, RepositoryGroup } from "../../types/Repositories";
export default function groupByNamespace(
repositories: Repository[]

View File

@@ -0,0 +1,2 @@
import RepositoryList from "./RepositoryList";
export default RepositoryList;

View File

@@ -3,7 +3,7 @@ import React from "react";
import { connect } from "react-redux";
import { translate } from "react-i18next";
import { Page } from "../../components/layout";
import RepositoryForm from "../components/RepositoryForm";
import RepositoryForm from "../components/form";
import type { RepositoryType } from "../types/RepositoryTypes";
import {
fetchRepositoryTypesIfNeeded,

View File

@@ -15,7 +15,7 @@ import {
} from "../modules/repos";
import { translate } from "react-i18next";
import { Page } from "../../components/layout";
import RepositoryList from "../components/RepositoryList";
import RepositoryList from "../components/list";
import Paginator from "../../components/Paginator";
import { withRouter } from "react-router-dom";
import type { History } from "history";

View File

@@ -1,6 +1,7 @@
//@flow
import React from "react";
import {
deleteRepo,
fetchRepo,
getFetchRepoFailure,
getRepository,
@@ -15,6 +16,9 @@ import ErrorPage from "../../components/ErrorPage";
import { translate } from "react-i18next";
import { Navigation, NavLink, Section } from "../../components/navigation";
import RepositoryDetails from "../components/RepositoryDetails";
import DeleteNavAction from "../components/DeleteNavAction";
import type { History } from "history";
type Props = {
namespace: string,
@@ -25,9 +29,11 @@ type Props = {
// dispatch functions
fetchRepo: (namespace: string, name: string) => void,
deleteRepo: (repository: Repository, () => void) => void,
// context props
t: string => string,
history: History,
match: any
};
@@ -49,6 +55,14 @@ class RepositoryRoot extends React.Component<Props> {
return this.stripEndingSlash(this.props.match.url);
};
deleted = () => {
this.props.history.push("/repos");
};
delete = (repository: Repository) => {
this.props.deleteRepo(repository, this.deleted);
};
render() {
const { loading, error, repository, t } = this.props;
@@ -81,6 +95,7 @@ class RepositoryRoot extends React.Component<Props> {
<div className="column">
<Navigation>
<Section label={t("repository-root.actions-label")}>
<DeleteNavAction repository={repository} delete={this.delete} />
<NavLink to="/repos" label={t("repository-root.back-label")} />
</Section>
</Navigation>
@@ -109,6 +124,9 @@ const mapDispatchToProps = dispatch => {
return {
fetchRepo: (namespace: string, name: string) => {
dispatch(fetchRepo(namespace, name));
},
deleteRepo: (repository: Repository, callback: () => void) => {
dispatch(deleteRepo(repository, callback));
}
};
};