mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-06 13:35:44 +01:00
merge 2.0.0-m3
This commit is contained in:
133
scm-ui-components/packages/ui-components/src/LinkPaginator.js
Normal file
133
scm-ui-components/packages/ui-components/src/LinkPaginator.js
Normal file
@@ -0,0 +1,133 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import {translate} from "react-i18next";
|
||||
import type {PagedCollection} from "@scm-manager/ui-types";
|
||||
import {Button} from "./buttons";
|
||||
|
||||
type Props = {
|
||||
collection: PagedCollection,
|
||||
page: number,
|
||||
|
||||
// context props
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class LinkPaginator extends React.Component<Props> {
|
||||
|
||||
renderFirstButton() {
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-link"}
|
||||
label={"1"}
|
||||
disabled={false}
|
||||
link={"1"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderPreviousButton(label?: string) {
|
||||
const { page } = this.props;
|
||||
const previousPage = page - 1;
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-previous"}
|
||||
label={label ? label : previousPage.toString()}
|
||||
disabled={!this.hasLink("prev")}
|
||||
link={`${previousPage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
hasLink(name: string) {
|
||||
const { collection } = this.props;
|
||||
return collection._links[name];
|
||||
}
|
||||
|
||||
renderNextButton(label?: string) {
|
||||
const { page } = this.props;
|
||||
const nextPage = page + 1;
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-next"}
|
||||
label={label ? label : nextPage.toString()}
|
||||
disabled={!this.hasLink("next")}
|
||||
link={`${nextPage}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderLastButton() {
|
||||
const { collection } = this.props;
|
||||
return (
|
||||
<Button
|
||||
className={"pagination-link"}
|
||||
label={`${collection.pageTotal}`}
|
||||
disabled={false}
|
||||
link={`${collection.pageTotal}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
separator() {
|
||||
return <span className="pagination-ellipsis">…</span>;
|
||||
}
|
||||
|
||||
currentPage(page: number) {
|
||||
return (
|
||||
<Button
|
||||
className="pagination-link is-current"
|
||||
label={page}
|
||||
disabled={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
pageLinks() {
|
||||
const { collection } = this.props;
|
||||
|
||||
const links = [];
|
||||
const page = collection.page + 1;
|
||||
const pageTotal = collection.pageTotal;
|
||||
if (page > 1) {
|
||||
links.push(this.renderFirstButton());
|
||||
}
|
||||
if (page > 3) {
|
||||
links.push(this.separator());
|
||||
}
|
||||
if (page > 2) {
|
||||
links.push(this.renderPreviousButton());
|
||||
}
|
||||
|
||||
links.push(this.currentPage(page));
|
||||
|
||||
if (page + 1 < pageTotal) {
|
||||
links.push(this.renderNextButton());
|
||||
}
|
||||
if (page + 2 < pageTotal)
|
||||
//if there exists pages between next and last
|
||||
links.push(this.separator());
|
||||
if (page < pageTotal) {
|
||||
links.push(this.renderLastButton());
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<nav className="pagination is-centered" aria-label="pagination">
|
||||
{this.renderPreviousButton(t("paginator.previous"))}
|
||||
<ul className="pagination-list">
|
||||
{this.pageLinks().map((link, index) => {
|
||||
return <li key={index}>{link}</li>;
|
||||
})}
|
||||
</ul>
|
||||
{this.renderNextButton(t("paginator.next"))}
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(LinkPaginator);
|
||||
@@ -18,8 +18,10 @@ class Paginator extends React.Component<Props> {
|
||||
createAction = (linkType: string) => () => {
|
||||
const { collection, onPageChange } = this.props;
|
||||
if (onPageChange) {
|
||||
const link = collection._links[linkType].href;
|
||||
onPageChange(link);
|
||||
const link = collection._links[linkType];
|
||||
if (link && link.href) {
|
||||
onPageChange(link.href);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -3,10 +3,13 @@ import React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import "./tests/enzyme";
|
||||
import "./tests/i18n";
|
||||
|
||||
import ReactRouterEnzymeContext from "react-router-enzyme-context";
|
||||
import Paginator from "./Paginator";
|
||||
|
||||
describe("paginator rendering tests", () => {
|
||||
|
||||
const options = new ReactRouterEnzymeContext();
|
||||
|
||||
const dummyLink = {
|
||||
href: "https://dummy"
|
||||
};
|
||||
@@ -18,7 +21,10 @@ describe("paginator rendering tests", () => {
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
for (let button of buttons) {
|
||||
@@ -37,7 +43,10 @@ describe("paginator rendering tests", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
@@ -73,7 +82,10 @@ describe("paginator rendering tests", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
@@ -112,7 +124,10 @@ describe("paginator rendering tests", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
@@ -148,7 +163,10 @@ describe("paginator rendering tests", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
@@ -189,7 +207,10 @@ describe("paginator rendering tests", () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const paginator = shallow(
|
||||
<Paginator collection={collection} />,
|
||||
options.get()
|
||||
);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
|
||||
@@ -244,7 +265,8 @@ describe("paginator rendering tests", () => {
|
||||
};
|
||||
|
||||
const paginator = mount(
|
||||
<Paginator collection={collection} onPageChange={callMe} />
|
||||
<Paginator collection={collection} onPageChange={callMe} />,
|
||||
options.get()
|
||||
);
|
||||
paginator.find("Button.pagination-previous").simulate("click");
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
import { withRouter } from "react-router-dom";
|
||||
|
||||
export type ButtonProps = {
|
||||
label: string,
|
||||
@@ -16,7 +16,10 @@ export type ButtonProps = {
|
||||
|
||||
type Props = ButtonProps & {
|
||||
type: string,
|
||||
color: string
|
||||
color: string,
|
||||
|
||||
// context prop
|
||||
history: any
|
||||
};
|
||||
|
||||
class Button extends React.Component<Props> {
|
||||
@@ -25,14 +28,22 @@ class Button extends React.Component<Props> {
|
||||
color: "default"
|
||||
};
|
||||
|
||||
renderButton = () => {
|
||||
onClick = (event: Event) => {
|
||||
const { action, link, history } = this.props;
|
||||
if (action) {
|
||||
action(event);
|
||||
} else if (link) {
|
||||
history.push(link);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
label,
|
||||
loading,
|
||||
disabled,
|
||||
type,
|
||||
color,
|
||||
action,
|
||||
fullWidth,
|
||||
className
|
||||
} = this.props;
|
||||
@@ -42,7 +53,7 @@ class Button extends React.Component<Props> {
|
||||
<button
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={action ? action : (event: Event) => {}}
|
||||
onClick={this.onClick}
|
||||
className={classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
@@ -56,14 +67,6 @@ class Button extends React.Component<Props> {
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { link } = this.props;
|
||||
if (link) {
|
||||
return <Link to={link}>{this.renderButton()}</Link>;
|
||||
} else {
|
||||
return this.renderButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Button;
|
||||
export default withRouter(Button);
|
||||
|
||||
@@ -15,9 +15,11 @@ export { default as Logo } from "./Logo.js";
|
||||
export { default as MailLink } from "./MailLink.js";
|
||||
export { default as Notification } from "./Notification.js";
|
||||
export { default as Paginator } from "./Paginator.js";
|
||||
export { default as LinkPaginator } from "./LinkPaginator.js";
|
||||
export { default as ProtectedRoute } from "./ProtectedRoute.js";
|
||||
export { default as Help } from "./Help.js";
|
||||
export { default as LabelWithHelpIcon } from "./LabelWithHelpIcon.js";
|
||||
export { getPageFromMatch } from "./urls";
|
||||
|
||||
export { apiClient, NOT_FOUND_ERROR, UNAUTHORIZED_ERROR } from "./apiclient.js";
|
||||
|
||||
|
||||
@@ -7,7 +7,8 @@ import { Route, Link } from "react-router-dom";
|
||||
type Props = {
|
||||
to: string,
|
||||
label: string,
|
||||
activeOnlyWhenExact?: boolean
|
||||
activeOnlyWhenExact?: boolean,
|
||||
activeWhenMatch?: (route: any) => boolean
|
||||
};
|
||||
|
||||
class NavLink extends React.Component<Props> {
|
||||
@@ -15,11 +16,17 @@ class NavLink extends React.Component<Props> {
|
||||
activeOnlyWhenExact: true
|
||||
};
|
||||
|
||||
|
||||
isActive(route: any) {
|
||||
const { activeWhenMatch } = this.props;
|
||||
return route.match || (activeWhenMatch && activeWhenMatch(route));
|
||||
}
|
||||
|
||||
renderLink = (route: any) => {
|
||||
const { to, label } = this.props;
|
||||
return (
|
||||
<li>
|
||||
<Link className={route.match ? "is-active" : ""} to={to}>
|
||||
<Link className={this.isActive(route) ? "is-active" : ""} to={to}>
|
||||
{label}
|
||||
</Link>
|
||||
</li>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import type { Repository } from "@scm-manager/ui-types";
|
||||
import { getProtocolLinkByType, getTypePredicate } from "./repositories";
|
||||
import { getProtocolLinkByType } from "./repositories";
|
||||
|
||||
describe("getProtocolLinkByType tests", () => {
|
||||
|
||||
|
||||
@@ -4,3 +4,11 @@ export const contextPath = window.ctxPath || "";
|
||||
export function withContextPath(path: string) {
|
||||
return contextPath + path;
|
||||
}
|
||||
|
||||
export function getPageFromMatch(match: any) {
|
||||
let page = parseInt(match.params.page, 10);
|
||||
if (isNaN(page) || !page) {
|
||||
page = 1;
|
||||
}
|
||||
return page;
|
||||
}
|
||||
|
||||
27
scm-ui-components/packages/ui-components/src/urls.test.js
Normal file
27
scm-ui-components/packages/ui-components/src/urls.test.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// @flow
|
||||
import { getPageFromMatch } from "./urls";
|
||||
|
||||
describe("tests for getPageFromMatch", () => {
|
||||
function createMatch(page: string) {
|
||||
return {
|
||||
params: {
|
||||
page
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it("should return 1 for NaN", () => {
|
||||
const match = createMatch("any");
|
||||
expect(getPageFromMatch(match)).toBe(1);
|
||||
});
|
||||
|
||||
it("should return 1 for 0", () => {
|
||||
const match = createMatch("0");
|
||||
expect(getPageFromMatch(match)).toBe(1);
|
||||
});
|
||||
|
||||
it("should return the given number", () => {
|
||||
const match = createMatch("42");
|
||||
expect(getPageFromMatch(match)).toBe(42);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user