mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-08 06:25:45 +01:00
re integrate @scm-manager/ui-types into @scm-manager/ui-components to reduce maven modules
This commit is contained in:
1
pom.xml
1
pom.xml
@@ -69,6 +69,7 @@
|
||||
<module>scm-annotation-processor</module>
|
||||
<module>scm-core</module>
|
||||
<module>scm-test</module>
|
||||
<module>scm-ui-components</module>
|
||||
<module>scm-plugins</module>
|
||||
<module>scm-dao-xml</module>
|
||||
<module>scm-ui</module>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
.*/node_modules/module-deps/.*
|
||||
|
||||
[include]
|
||||
../ui-types/.*
|
||||
|
||||
[libs]
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
"@scm-manager/ui-extensions": "^0.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scm-manager/ui-bundler": "^0.0.7"
|
||||
"@scm-manager/ui-bundler": "^0.0.11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,33 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.github.sdorra</groupId>
|
||||
<artifactId>buildfrontend-maven-plugin</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<configuration>
|
||||
<node>
|
||||
<version>8.11.4</version>
|
||||
</node>
|
||||
<pkgManager>
|
||||
<type>YARN</type>
|
||||
<version>1.9.4</version>
|
||||
</pkgManager>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>link-components</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>install-link</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<pkg>@scm-manager/ui-components</pkg>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//@flow
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { Image } from "@scm-manager/ui-components";
|
||||
|
||||
type Props = {
|
||||
};
|
||||
@@ -7,8 +8,7 @@ type Props = {
|
||||
class GitAvatar extends React.Component<Props> {
|
||||
|
||||
render() {
|
||||
// TODO we have to use Image from ui-components
|
||||
return <img src="/scm/images/git-logo.png" alt="Git Logo" />;
|
||||
return <Image src="/images/git-logo.png" alt="Git Logo" />;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//@flow
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
import { Repository } from "@scm-manager/ui-types";
|
||||
|
||||
// TODO flow types ???
|
||||
type Props = {
|
||||
repository: Object
|
||||
repository: Repository
|
||||
}
|
||||
|
||||
class ProtocolInformation extends React.Component<Props> {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//@flow
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import ProtocolInformation from './ProtocolInformation';
|
||||
import GitAvatar from './GitAvatar';
|
||||
import ProtocolInformation from "./ProtocolInformation";
|
||||
import GitAvatar from "./GitAvatar";
|
||||
|
||||
const gitPredicate = (props: Object) => {
|
||||
return props.repository && props.repository.type === "git";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
6
scm-ui-components/.babelrc
Normal file
6
scm-ui-components/.babelrc
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react", "@babel/preset-flow"],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
||||
3
scm-ui-components/.eslintrc
Normal file
3
scm-ui-components/.eslintrc
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "@scm-manager/eslint-config"
|
||||
}
|
||||
8
scm-ui-components/.flowconfig
Normal file
8
scm-ui-components/.flowconfig
Normal file
@@ -0,0 +1,8 @@
|
||||
[ignore]
|
||||
.*/node_modules/module-deps/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[options]
|
||||
23
scm-ui-components/flow-typed/npm/classnames_v2.x.x.js
vendored
Normal file
23
scm-ui-components/flow-typed/npm/classnames_v2.x.x.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// flow-typed signature: cf86673cc32d185bdab1d2ea90578d37
|
||||
// flow-typed version: 614bf49aa8/classnames_v2.x.x/flow_>=v0.25.x
|
||||
|
||||
type $npm$classnames$Classes =
|
||||
| string
|
||||
| { [className: string]: * }
|
||||
| false
|
||||
| void
|
||||
| null;
|
||||
|
||||
declare module "classnames" {
|
||||
declare module.exports: (
|
||||
...classes: Array<$npm$classnames$Classes | $npm$classnames$Classes[]>
|
||||
) => string;
|
||||
}
|
||||
|
||||
declare module "classnames/bind" {
|
||||
declare module.exports: $Exports<"classnames">;
|
||||
}
|
||||
|
||||
declare module "classnames/dedupe" {
|
||||
declare module.exports: $Exports<"classnames">;
|
||||
}
|
||||
1108
scm-ui-components/flow-typed/npm/jest_v23.x.x.js
vendored
Normal file
1108
scm-ui-components/flow-typed/npm/jest_v23.x.x.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
331
scm-ui-components/flow-typed/npm/moment_v2.3.x.js
vendored
Normal file
331
scm-ui-components/flow-typed/npm/moment_v2.3.x.js
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
// flow-typed signature: 23b805356f90ad9384dd88489654e380
|
||||
// flow-typed version: e9374c5fe9/moment_v2.3.x/flow_>=v0.25.x
|
||||
|
||||
type moment$MomentOptions = {
|
||||
y?: number | string,
|
||||
year?: number | string,
|
||||
years?: number | string,
|
||||
M?: number | string,
|
||||
month?: number | string,
|
||||
months?: number | string,
|
||||
d?: number | string,
|
||||
day?: number | string,
|
||||
days?: number | string,
|
||||
date?: number | string,
|
||||
h?: number | string,
|
||||
hour?: number | string,
|
||||
hours?: number | string,
|
||||
m?: number | string,
|
||||
minute?: number | string,
|
||||
minutes?: number | string,
|
||||
s?: number | string,
|
||||
second?: number | string,
|
||||
seconds?: number | string,
|
||||
ms?: number | string,
|
||||
millisecond?: number | string,
|
||||
milliseconds?: number | string
|
||||
};
|
||||
|
||||
type moment$MomentObject = {
|
||||
years: number,
|
||||
months: number,
|
||||
date: number,
|
||||
hours: number,
|
||||
minutes: number,
|
||||
seconds: number,
|
||||
milliseconds: number
|
||||
};
|
||||
|
||||
type moment$MomentCreationData = {
|
||||
input: string,
|
||||
format: string,
|
||||
locale: Object,
|
||||
isUTC: boolean,
|
||||
strict: boolean
|
||||
};
|
||||
|
||||
type moment$CalendarFormat = string | ((moment: moment$Moment) => string);
|
||||
|
||||
type moment$CalendarFormats = {
|
||||
sameDay?: moment$CalendarFormat,
|
||||
nextDay?: moment$CalendarFormat,
|
||||
nextWeek?: moment$CalendarFormat,
|
||||
lastDay?: moment$CalendarFormat,
|
||||
lastWeek?: moment$CalendarFormat,
|
||||
sameElse?: moment$CalendarFormat
|
||||
};
|
||||
|
||||
declare class moment$LocaleData {
|
||||
months(moment: moment$Moment): string,
|
||||
monthsShort(moment: moment$Moment): string,
|
||||
monthsParse(month: string): number,
|
||||
weekdays(moment: moment$Moment): string,
|
||||
weekdaysShort(moment: moment$Moment): string,
|
||||
weekdaysMin(moment: moment$Moment): string,
|
||||
weekdaysParse(weekDay: string): number,
|
||||
longDateFormat(dateFormat: string): string,
|
||||
isPM(date: string): boolean,
|
||||
meridiem(hours: number, minutes: number, isLower: boolean): string,
|
||||
calendar(
|
||||
key:
|
||||
| "sameDay"
|
||||
| "nextDay"
|
||||
| "lastDay"
|
||||
| "nextWeek"
|
||||
| "prevWeek"
|
||||
| "sameElse",
|
||||
moment: moment$Moment
|
||||
): string,
|
||||
relativeTime(
|
||||
number: number,
|
||||
withoutSuffix: boolean,
|
||||
key: "s" | "m" | "mm" | "h" | "hh" | "d" | "dd" | "M" | "MM" | "y" | "yy",
|
||||
isFuture: boolean
|
||||
): string,
|
||||
pastFuture(diff: any, relTime: string): string,
|
||||
ordinal(number: number): string,
|
||||
preparse(str: string): any,
|
||||
postformat(str: string): any,
|
||||
week(moment: moment$Moment): string,
|
||||
invalidDate(): string,
|
||||
firstDayOfWeek(): number,
|
||||
firstDayOfYear(): number
|
||||
}
|
||||
declare class moment$MomentDuration {
|
||||
humanize(suffix?: boolean): string,
|
||||
milliseconds(): number,
|
||||
asMilliseconds(): number,
|
||||
seconds(): number,
|
||||
asSeconds(): number,
|
||||
minutes(): number,
|
||||
asMinutes(): number,
|
||||
hours(): number,
|
||||
asHours(): number,
|
||||
days(): number,
|
||||
asDays(): number,
|
||||
months(): number,
|
||||
asWeeks(): number,
|
||||
weeks(): number,
|
||||
asMonths(): number,
|
||||
years(): number,
|
||||
asYears(): number,
|
||||
add(value: number | moment$MomentDuration | Object, unit?: string): this,
|
||||
subtract(value: number | moment$MomentDuration | Object, unit?: string): this,
|
||||
as(unit: string): number,
|
||||
get(unit: string): number,
|
||||
toJSON(): string,
|
||||
toISOString(): string,
|
||||
isValid(): boolean
|
||||
}
|
||||
declare class moment$Moment {
|
||||
static ISO_8601: string,
|
||||
static (
|
||||
string?: string,
|
||||
format?: string | Array<string>,
|
||||
strict?: boolean
|
||||
): moment$Moment,
|
||||
static (
|
||||
string?: string,
|
||||
format?: string | Array<string>,
|
||||
locale?: string,
|
||||
strict?: boolean
|
||||
): moment$Moment,
|
||||
static (
|
||||
initDate: ?Object | number | Date | Array<number> | moment$Moment | string
|
||||
): moment$Moment,
|
||||
static unix(seconds: number): moment$Moment,
|
||||
static utc(): moment$Moment,
|
||||
static utc(number: number | Array<number>): moment$Moment,
|
||||
static utc(
|
||||
str: string,
|
||||
str2?: string | Array<string>,
|
||||
str3?: string
|
||||
): moment$Moment,
|
||||
static utc(moment: moment$Moment): moment$Moment,
|
||||
static utc(date: Date): moment$Moment,
|
||||
static parseZone(): moment$Moment,
|
||||
static parseZone(rawDate: string): moment$Moment,
|
||||
static parseZone(
|
||||
rawDate: string,
|
||||
format: string | Array<string>
|
||||
): moment$Moment,
|
||||
static parseZone(
|
||||
rawDate: string,
|
||||
format: string,
|
||||
strict: boolean
|
||||
): moment$Moment,
|
||||
static parseZone(
|
||||
rawDate: string,
|
||||
format: string,
|
||||
locale: string,
|
||||
strict: boolean
|
||||
): moment$Moment,
|
||||
isValid(): boolean,
|
||||
invalidAt(): 0 | 1 | 2 | 3 | 4 | 5 | 6,
|
||||
creationData(): moment$MomentCreationData,
|
||||
millisecond(number: number): this,
|
||||
milliseconds(number: number): this,
|
||||
millisecond(): number,
|
||||
milliseconds(): number,
|
||||
second(number: number): this,
|
||||
seconds(number: number): this,
|
||||
second(): number,
|
||||
seconds(): number,
|
||||
minute(number: number): this,
|
||||
minutes(number: number): this,
|
||||
minute(): number,
|
||||
minutes(): number,
|
||||
hour(number: number): this,
|
||||
hours(number: number): this,
|
||||
hour(): number,
|
||||
hours(): number,
|
||||
date(number: number): this,
|
||||
dates(number: number): this,
|
||||
date(): number,
|
||||
dates(): number,
|
||||
day(day: number | string): this,
|
||||
days(day: number | string): this,
|
||||
day(): number,
|
||||
days(): number,
|
||||
weekday(number: number): this,
|
||||
weekday(): number,
|
||||
isoWeekday(number: number): this,
|
||||
isoWeekday(): number,
|
||||
dayOfYear(number: number): this,
|
||||
dayOfYear(): number,
|
||||
week(number: number): this,
|
||||
weeks(number: number): this,
|
||||
week(): number,
|
||||
weeks(): number,
|
||||
isoWeek(number: number): this,
|
||||
isoWeeks(number: number): this,
|
||||
isoWeek(): number,
|
||||
isoWeeks(): number,
|
||||
month(number: number): this,
|
||||
months(number: number): this,
|
||||
month(): number,
|
||||
months(): number,
|
||||
quarter(number: number): this,
|
||||
quarter(): number,
|
||||
year(number: number): this,
|
||||
years(number: number): this,
|
||||
year(): number,
|
||||
years(): number,
|
||||
weekYear(number: number): this,
|
||||
weekYear(): number,
|
||||
isoWeekYear(number: number): this,
|
||||
isoWeekYear(): number,
|
||||
weeksInYear(): number,
|
||||
isoWeeksInYear(): number,
|
||||
get(string: string): number,
|
||||
set(unit: string, value: number): this,
|
||||
set(options: { [unit: string]: number }): this,
|
||||
static max(...dates: Array<moment$Moment>): moment$Moment,
|
||||
static max(dates: Array<moment$Moment>): moment$Moment,
|
||||
static min(...dates: Array<moment$Moment>): moment$Moment,
|
||||
static min(dates: Array<moment$Moment>): moment$Moment,
|
||||
add(
|
||||
value: number | moment$MomentDuration | moment$Moment | Object,
|
||||
unit?: string
|
||||
): this,
|
||||
subtract(
|
||||
value: number | moment$MomentDuration | moment$Moment | string | Object,
|
||||
unit?: string
|
||||
): this,
|
||||
startOf(unit: string): this,
|
||||
endOf(unit: string): this,
|
||||
local(): this,
|
||||
utc(): this,
|
||||
utcOffset(
|
||||
offset: number | string,
|
||||
keepLocalTime?: boolean,
|
||||
keepMinutes?: boolean
|
||||
): this,
|
||||
utcOffset(): number,
|
||||
format(format?: string): string,
|
||||
fromNow(removeSuffix?: boolean): string,
|
||||
from(
|
||||
value: moment$Moment | string | number | Date | Array<number>,
|
||||
removePrefix?: boolean
|
||||
): string,
|
||||
toNow(removePrefix?: boolean): string,
|
||||
to(
|
||||
value: moment$Moment | string | number | Date | Array<number>,
|
||||
removePrefix?: boolean
|
||||
): string,
|
||||
calendar(refTime?: any, formats?: moment$CalendarFormats): string,
|
||||
diff(
|
||||
date: moment$Moment | string | number | Date | Array<number>,
|
||||
format?: string,
|
||||
floating?: boolean
|
||||
): number,
|
||||
valueOf(): number,
|
||||
unix(): number,
|
||||
daysInMonth(): number,
|
||||
toDate(): Date,
|
||||
toArray(): Array<number>,
|
||||
toJSON(): string,
|
||||
toISOString(
|
||||
keepOffset?: boolean
|
||||
): string,
|
||||
toObject(): moment$MomentObject,
|
||||
isBefore(
|
||||
date?: moment$Moment | string | number | Date | Array<number>,
|
||||
units?: ?string
|
||||
): boolean,
|
||||
isSame(
|
||||
date?: moment$Moment | string | number | Date | Array<number>,
|
||||
units?: ?string
|
||||
): boolean,
|
||||
isAfter(
|
||||
date?: moment$Moment | string | number | Date | Array<number>,
|
||||
units?: ?string
|
||||
): boolean,
|
||||
isSameOrBefore(
|
||||
date?: moment$Moment | string | number | Date | Array<number>,
|
||||
units?: ?string
|
||||
): boolean,
|
||||
isSameOrAfter(
|
||||
date?: moment$Moment | string | number | Date | Array<number>,
|
||||
units?: ?string
|
||||
): boolean,
|
||||
isBetween(
|
||||
fromDate: moment$Moment | string | number | Date | Array<number>,
|
||||
toDate?: ?moment$Moment | string | number | Date | Array<number>,
|
||||
granularity?: ?string,
|
||||
inclusion?: ?string
|
||||
): boolean,
|
||||
isDST(): boolean,
|
||||
isDSTShifted(): boolean,
|
||||
isLeapYear(): boolean,
|
||||
clone(): moment$Moment,
|
||||
static isMoment(obj: any): boolean,
|
||||
static isDate(obj: any): boolean,
|
||||
static locale(locale: string, localeData?: Object): string,
|
||||
static updateLocale(locale: string, localeData?: ?Object): void,
|
||||
static locale(locales: Array<string>): string,
|
||||
locale(locale: string, customization?: Object | null): moment$Moment,
|
||||
locale(): string,
|
||||
static months(): Array<string>,
|
||||
static monthsShort(): Array<string>,
|
||||
static weekdays(): Array<string>,
|
||||
static weekdaysShort(): Array<string>,
|
||||
static weekdaysMin(): Array<string>,
|
||||
static months(): string,
|
||||
static monthsShort(): string,
|
||||
static weekdays(): string,
|
||||
static weekdaysShort(): string,
|
||||
static weekdaysMin(): string,
|
||||
static localeData(key?: string): moment$LocaleData,
|
||||
static duration(
|
||||
value: number | Object | string,
|
||||
unit?: string
|
||||
): moment$MomentDuration,
|
||||
static isDuration(obj: any): boolean,
|
||||
static normalizeUnits(unit: string): string,
|
||||
static invalid(object: any): moment$Moment
|
||||
}
|
||||
|
||||
declare module "moment" {
|
||||
declare module.exports: Class<moment$Moment>;
|
||||
}
|
||||
137
scm-ui-components/flow-typed/npm/react-jss_vx.x.x.js
vendored
Normal file
137
scm-ui-components/flow-typed/npm/react-jss_vx.x.x.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
// flow-typed signature: ba35d02d668b0d0a3e04a63a6847974e
|
||||
// flow-typed version: <<STUB>>/react-jss_v8.6.1/flow_v0.79.1
|
||||
|
||||
/**
|
||||
* This is an autogenerated libdef stub for:
|
||||
*
|
||||
* 'react-jss'
|
||||
*
|
||||
* Fill this stub out by replacing all the `any` types.
|
||||
*
|
||||
* Once filled out, we encourage you to share your work with the
|
||||
* community by sending a pull request to:
|
||||
* https://github.com/flowtype/flow-typed
|
||||
*/
|
||||
|
||||
declare module 'react-jss' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* We include stubs for each file inside this npm package in case you need to
|
||||
* require those files directly. Feel free to delete any files that aren't
|
||||
* needed.
|
||||
*/
|
||||
declare module 'react-jss/dist/react-jss' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/dist/react-jss.min' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/compose' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/compose.test' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/contextTypes' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/createHoc' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/getDisplayName' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/index' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/index.test' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/injectSheet' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/injectSheet.test' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/jss' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/JssProvider' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/JssProvider.test' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/ns' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
declare module 'react-jss/lib/propTypes' {
|
||||
declare module.exports: any;
|
||||
}
|
||||
|
||||
// Filename aliases
|
||||
declare module 'react-jss/dist/react-jss.js' {
|
||||
declare module.exports: $Exports<'react-jss/dist/react-jss'>;
|
||||
}
|
||||
declare module 'react-jss/dist/react-jss.min.js' {
|
||||
declare module.exports: $Exports<'react-jss/dist/react-jss.min'>;
|
||||
}
|
||||
declare module 'react-jss/lib/compose.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/compose'>;
|
||||
}
|
||||
declare module 'react-jss/lib/compose.test.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/compose.test'>;
|
||||
}
|
||||
declare module 'react-jss/lib/contextTypes.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/contextTypes'>;
|
||||
}
|
||||
declare module 'react-jss/lib/createHoc.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/createHoc'>;
|
||||
}
|
||||
declare module 'react-jss/lib/getDisplayName.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/getDisplayName'>;
|
||||
}
|
||||
declare module 'react-jss/lib/index.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/index'>;
|
||||
}
|
||||
declare module 'react-jss/lib/index.test.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/index.test'>;
|
||||
}
|
||||
declare module 'react-jss/lib/injectSheet.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/injectSheet'>;
|
||||
}
|
||||
declare module 'react-jss/lib/injectSheet.test.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/injectSheet.test'>;
|
||||
}
|
||||
declare module 'react-jss/lib/jss.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/jss'>;
|
||||
}
|
||||
declare module 'react-jss/lib/JssProvider.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/JssProvider'>;
|
||||
}
|
||||
declare module 'react-jss/lib/JssProvider.test.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/JssProvider.test'>;
|
||||
}
|
||||
declare module 'react-jss/lib/ns.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/ns'>;
|
||||
}
|
||||
declare module 'react-jss/lib/propTypes.js' {
|
||||
declare module.exports: $Exports<'react-jss/lib/propTypes'>;
|
||||
}
|
||||
43
scm-ui-components/package.json
Normal file
43
scm-ui-components/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@scm-manager/ui-components",
|
||||
"version": "0.0.1",
|
||||
"description": "UI Components for SCM-Manager and its plugins",
|
||||
"main": "src/index.js",
|
||||
"repository": "https://bitbucket.org/sdorra/scm-manager",
|
||||
"author": "Sebastian Sdorra <s.sdorra@gmail.com>",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"update-index": "create-index -r src",
|
||||
"eslint-fix": "eslint src --fix"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scm-manager/ui-bundler": "^0.0.12",
|
||||
"create-index": "^2.3.0",
|
||||
"enzyme": "^3.5.0",
|
||||
"enzyme-adapter-react-16": "^1.3.1",
|
||||
"flow-bin": "^0.79.1",
|
||||
"flow-typed": "^2.5.1",
|
||||
"jest": "^23.5.0",
|
||||
"raf": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"classnames": "^2.2.6",
|
||||
"moment": "^2.22.2",
|
||||
"react": "^16.4.2",
|
||||
"react-dom": "^16.4.2",
|
||||
"react-i18next": "^7.11.0",
|
||||
"react-jss": "^8.6.1",
|
||||
"react-router-dom": "^4.3.1"
|
||||
},
|
||||
"browserify": {
|
||||
"transform": [
|
||||
[
|
||||
"babelify",
|
||||
{
|
||||
"plugins": ["@babel/plugin-proposal-class-properties"],
|
||||
"presets": ["@babel/preset-env", "@babel/preset-flow", "@babel/preset-react"]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
47
scm-ui-components/pom.xml
Normal file
47
scm-ui-components/pom.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>sonia.scm</groupId>
|
||||
<artifactId>scm</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<groupId>sonia.scm</groupId>
|
||||
<artifactId>scm-ui-components</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<name>scm-ui-components</name>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>com.github.sdorra</groupId>
|
||||
<artifactId>buildfrontend-maven-plugin</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<configuration>
|
||||
<node>
|
||||
<version>8.11.4</version>
|
||||
</node>
|
||||
<pkgManager>
|
||||
<type>YARN</type>
|
||||
<version>1.9.4</version>
|
||||
</pkgManager>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>link</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>link</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
32
scm-ui-components/src/DateFromNow.js
Normal file
32
scm-ui-components/src/DateFromNow.js
Normal file
@@ -0,0 +1,32 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import moment from "moment";
|
||||
import { translate } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
date?: string,
|
||||
|
||||
// context props
|
||||
i18n: any
|
||||
};
|
||||
|
||||
class DateFromNow extends React.Component<Props> {
|
||||
static format(locale: string, date?: string) {
|
||||
let fromNow = "";
|
||||
if (date) {
|
||||
fromNow = moment(date)
|
||||
.locale(locale)
|
||||
.fromNow();
|
||||
}
|
||||
return fromNow;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { i18n, date } = this.props;
|
||||
|
||||
const fromNow = DateFromNow.format(i18n.language, date);
|
||||
return <span>{fromNow}</span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate()(DateFromNow);
|
||||
25
scm-ui-components/src/ErrorNotification.js
Normal file
25
scm-ui-components/src/ErrorNotification.js
Normal file
@@ -0,0 +1,25 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Notification from "./Notification";
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
error?: Error
|
||||
};
|
||||
|
||||
class ErrorNotification extends React.Component<Props> {
|
||||
render() {
|
||||
const { t, error } = this.props;
|
||||
if (error) {
|
||||
return (
|
||||
<Notification type="danger">
|
||||
<strong>{t("error-notification.prefix")}:</strong> {error.message}
|
||||
</Notification>
|
||||
);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(ErrorNotification);
|
||||
27
scm-ui-components/src/ErrorPage.js
Normal file
27
scm-ui-components/src/ErrorPage.js
Normal file
@@ -0,0 +1,27 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import ErrorNotification from "./ErrorNotification";
|
||||
|
||||
type Props = {
|
||||
error: Error,
|
||||
title: string,
|
||||
subtitle: string
|
||||
};
|
||||
|
||||
class ErrorPage extends React.Component<Props> {
|
||||
render() {
|
||||
const { title, subtitle, error } = this.props;
|
||||
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="box column is-4 is-offset-4 container">
|
||||
<h1 className="title">{title}</h1>
|
||||
<p className="subtitle">{subtitle}</p>
|
||||
<ErrorNotification error={error} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorPage;
|
||||
18
scm-ui-components/src/Image.js
Normal file
18
scm-ui-components/src/Image.js
Normal file
@@ -0,0 +1,18 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { withContextPath } from "./urls";
|
||||
|
||||
type Props = {
|
||||
src: string,
|
||||
alt: string,
|
||||
className?: any
|
||||
};
|
||||
|
||||
class Image extends React.Component<Props> {
|
||||
render() {
|
||||
const { src, alt, className } = this.props;
|
||||
return <img className={className} src={withContextPath(src)} alt={alt} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default Image;
|
||||
51
scm-ui-components/src/Loading.js
Normal file
51
scm-ui-components/src/Loading.js
Normal file
@@ -0,0 +1,51 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import injectSheet from "react-jss";
|
||||
import Image from "./Image";
|
||||
|
||||
const styles = {
|
||||
wrapper: {
|
||||
position: "relative"
|
||||
},
|
||||
loading: {
|
||||
width: "128px",
|
||||
height: "128px",
|
||||
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
|
||||
margin: "64px 0 0 -64px"
|
||||
},
|
||||
image: {
|
||||
width: "128px",
|
||||
height: "128px"
|
||||
}
|
||||
};
|
||||
|
||||
type Props = {
|
||||
t: string => string,
|
||||
message?: string,
|
||||
classes: any
|
||||
};
|
||||
|
||||
class Loading extends React.Component<Props> {
|
||||
render() {
|
||||
const { message, t, classes } = this.props;
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<div className={classes.loading}>
|
||||
<Image
|
||||
className={classes.image}
|
||||
src="/images/loading.svg"
|
||||
alt={t("loading.alt")}
|
||||
/>
|
||||
<p className="has-text-centered">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(translate("commons")(Loading));
|
||||
17
scm-ui-components/src/Logo.js
Normal file
17
scm-ui-components/src/Logo.js
Normal file
@@ -0,0 +1,17 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import Image from "./Image";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Logo extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return <Image src="/images/logo.png" alt={t("logo.alt")} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(Logo);
|
||||
18
scm-ui-components/src/MailLink.js
Normal file
18
scm-ui-components/src/MailLink.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
address?: string
|
||||
};
|
||||
|
||||
class MailLink extends React.Component<Props> {
|
||||
render() {
|
||||
const { address } = this.props;
|
||||
if (!address) {
|
||||
return null;
|
||||
}
|
||||
return <a href={"mailto: " + address}>{address}</a>;
|
||||
}
|
||||
}
|
||||
|
||||
export default MailLink;
|
||||
37
scm-ui-components/src/Notification.js
Normal file
37
scm-ui-components/src/Notification.js
Normal file
@@ -0,0 +1,37 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type NotificationType = "primary" | "info" | "success" | "warning" | "danger";
|
||||
|
||||
type Props = {
|
||||
type: NotificationType,
|
||||
onClose?: () => void,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Notification extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "info"
|
||||
};
|
||||
|
||||
renderCloseButton() {
|
||||
const { onClose } = this.props;
|
||||
if (onClose) {
|
||||
return <button className="delete" onClick={onClose} />;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
render() {
|
||||
const { type, children } = this.props;
|
||||
return (
|
||||
<div className={classNames("notification", "is-" + type)}>
|
||||
{this.renderCloseButton()}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Notification;
|
||||
124
scm-ui-components/src/Paginator.js
Normal file
124
scm-ui-components/src/Paginator.js
Normal file
@@ -0,0 +1,124 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import type { PagedCollection } from "./types";
|
||||
import { Button } from "./buttons";
|
||||
|
||||
type Props = {
|
||||
collection: PagedCollection,
|
||||
onPageChange?: string => void,
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class Paginator extends React.Component<Props> {
|
||||
isLinkUnavailable(linkType: string) {
|
||||
return !this.props.collection || !this.props.collection._links[linkType];
|
||||
}
|
||||
|
||||
createAction = (linkType: string) => () => {
|
||||
const { collection, onPageChange } = this.props;
|
||||
if (onPageChange) {
|
||||
const link = collection._links[linkType].href;
|
||||
onPageChange(link);
|
||||
}
|
||||
};
|
||||
|
||||
renderFirstButton() {
|
||||
return this.renderPageButton(1, "first");
|
||||
}
|
||||
|
||||
renderPreviousButton() {
|
||||
const { t } = this.props;
|
||||
return this.renderButton(
|
||||
"pagination-previous",
|
||||
t("paginator.previous"),
|
||||
"prev"
|
||||
);
|
||||
}
|
||||
|
||||
renderNextButton() {
|
||||
const { t } = this.props;
|
||||
return this.renderButton("pagination-next", t("paginator.next"), "next");
|
||||
}
|
||||
|
||||
renderLastButton() {
|
||||
const { collection } = this.props;
|
||||
return this.renderPageButton(collection.pageTotal, "last");
|
||||
}
|
||||
|
||||
renderPageButton(page: number, linkType: string) {
|
||||
return this.renderButton("pagination-link", page.toString(), linkType);
|
||||
}
|
||||
|
||||
renderButton(className: string, label: string, linkType: string) {
|
||||
return (
|
||||
<Button
|
||||
className={className}
|
||||
label={label}
|
||||
disabled={this.isLinkUnavailable(linkType)}
|
||||
action={this.createAction(linkType)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
seperator() {
|
||||
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.seperator());
|
||||
}
|
||||
if (page > 2) {
|
||||
links.push(this.renderPageButton(page - 1, "prev"));
|
||||
}
|
||||
|
||||
links.push(this.currentPage(page));
|
||||
|
||||
if (page + 1 < pageTotal) {
|
||||
links.push(this.renderPageButton(page + 1, "next"));
|
||||
}
|
||||
if (page + 2 < pageTotal)
|
||||
//if there exists pages between next and last
|
||||
links.push(this.seperator());
|
||||
if (page < pageTotal) {
|
||||
links.push(this.renderLastButton());
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<nav className="pagination is-centered" aria-label="pagination">
|
||||
{this.renderPreviousButton()}
|
||||
{this.renderNextButton()}
|
||||
<ul className="pagination-list">
|
||||
{this.pageLinks().map((link, index) => {
|
||||
return <li key={index}>{link}</li>;
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(Paginator);
|
||||
253
scm-ui-components/src/Paginator.test.js
Normal file
253
scm-ui-components/src/Paginator.test.js
Normal file
@@ -0,0 +1,253 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
import { mount, shallow } from "enzyme";
|
||||
import "./tests/enzyme";
|
||||
import "./tests/i18n";
|
||||
|
||||
import Paginator from "./Paginator";
|
||||
|
||||
describe("paginator rendering tests", () => {
|
||||
const dummyLink = {
|
||||
href: "https://dummy"
|
||||
};
|
||||
|
||||
it("should render all buttons but disabled, without links", () => {
|
||||
const collection = {
|
||||
page: 10,
|
||||
pageTotal: 20,
|
||||
_links: {}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
for (let button of buttons) {
|
||||
expect(button.props.disabled).toBeTruthy();
|
||||
}
|
||||
});
|
||||
|
||||
it("should render buttons for first page", () => {
|
||||
const collection = {
|
||||
page: 0,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeTruthy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeTruthy();
|
||||
expect(firstButton.label).toBe(1);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(3).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("2");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(4).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for second page", () => {
|
||||
const collection = {
|
||||
page: 1,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// current button
|
||||
const currentButton = buttons.get(3).props;
|
||||
expect(currentButton.disabled).toBeTruthy();
|
||||
expect(currentButton.label).toBe(2);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(4).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("3");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(5).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for last page", () => {
|
||||
const collection = {
|
||||
page: 147,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(5);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeTruthy();
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(3).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("147");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(4).props;
|
||||
expect(lastButton.disabled).toBeTruthy();
|
||||
expect(lastButton.label).toBe(148);
|
||||
});
|
||||
|
||||
it("should render buttons for penultimate page", () => {
|
||||
const collection = {
|
||||
page: 146,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(6);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// last button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
const currentButton = buttons.get(3).props;
|
||||
expect(currentButton.disabled).toBeFalsy();
|
||||
expect(currentButton.label).toBe("146");
|
||||
|
||||
// current button
|
||||
const nextButton = buttons.get(4).props;
|
||||
expect(nextButton.disabled).toBeTruthy();
|
||||
expect(nextButton.label).toBe(147);
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(5).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should render buttons for a page in the middle", () => {
|
||||
const collection = {
|
||||
page: 41,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: dummyLink,
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
const paginator = shallow(<Paginator collection={collection} />);
|
||||
const buttons = paginator.find("Button");
|
||||
expect(buttons.length).toBe(7);
|
||||
|
||||
// previous button
|
||||
expect(buttons.get(0).props.disabled).toBeFalsy();
|
||||
// next button
|
||||
expect(buttons.get(1).props.disabled).toBeFalsy();
|
||||
|
||||
// first button
|
||||
const firstButton = buttons.get(2).props;
|
||||
expect(firstButton.disabled).toBeFalsy();
|
||||
expect(firstButton.label).toBe("1");
|
||||
|
||||
// previous Button
|
||||
const previousButton = buttons.get(3).props;
|
||||
expect(previousButton.disabled).toBeFalsy();
|
||||
expect(previousButton.label).toBe("41");
|
||||
|
||||
// current button
|
||||
const currentButton = buttons.get(4).props;
|
||||
expect(currentButton.disabled).toBeTruthy();
|
||||
expect(currentButton.label).toBe(42);
|
||||
|
||||
// next button
|
||||
const nextButton = buttons.get(5).props;
|
||||
expect(nextButton.disabled).toBeFalsy();
|
||||
expect(nextButton.label).toBe("43");
|
||||
|
||||
// last button
|
||||
const lastButton = buttons.get(6).props;
|
||||
expect(lastButton.disabled).toBeFalsy();
|
||||
expect(lastButton.label).toBe("148");
|
||||
});
|
||||
|
||||
it("should call the function with the last previous url", () => {
|
||||
const collection = {
|
||||
page: 41,
|
||||
pageTotal: 148,
|
||||
_links: {
|
||||
first: dummyLink,
|
||||
prev: {
|
||||
href: "https://www.scm-manager.org"
|
||||
},
|
||||
next: dummyLink,
|
||||
last: dummyLink
|
||||
}
|
||||
};
|
||||
|
||||
let urlToOpen;
|
||||
const callMe = (url: string) => {
|
||||
urlToOpen = url;
|
||||
};
|
||||
|
||||
const paginator = mount(
|
||||
<Paginator collection={collection} onPageChange={callMe} />
|
||||
);
|
||||
paginator.find("Button.pagination-previous").simulate("click");
|
||||
|
||||
expect(urlToOpen).toBe("https://www.scm-manager.org");
|
||||
});
|
||||
});
|
||||
39
scm-ui-components/src/ProtectedRoute.js
Normal file
39
scm-ui-components/src/ProtectedRoute.js
Normal file
@@ -0,0 +1,39 @@
|
||||
//@flow
|
||||
import React, { Component } from "react";
|
||||
import { Route, Redirect, withRouter } from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
authenticated?: boolean,
|
||||
component: Component<any, any>
|
||||
};
|
||||
|
||||
class ProtectedRoute extends React.Component<Props> {
|
||||
renderRoute = (Component: any, authenticated?: boolean) => {
|
||||
return (routeProps: any) => {
|
||||
if (authenticated) {
|
||||
return <Component {...routeProps} />;
|
||||
} else {
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/login",
|
||||
state: { from: routeProps.location }
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
render() {
|
||||
const { component, authenticated, ...routeProps } = this.props;
|
||||
return (
|
||||
<Route
|
||||
{...routeProps}
|
||||
render={this.renderRoute(component, authenticated)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(ProtectedRoute);
|
||||
11
scm-ui-components/src/buttons/AddButton.js
Normal file
11
scm-ui-components/src/buttons/AddButton.js
Normal file
@@ -0,0 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class AddButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="default" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default AddButton;
|
||||
69
scm-ui-components/src/buttons/Button.js
Normal file
69
scm-ui-components/src/buttons/Button.js
Normal file
@@ -0,0 +1,69 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export type ButtonProps = {
|
||||
label: string,
|
||||
loading?: boolean,
|
||||
disabled?: boolean,
|
||||
action?: (event: Event) => void,
|
||||
link?: string,
|
||||
fullWidth?: boolean,
|
||||
className?: string,
|
||||
classes: any
|
||||
};
|
||||
|
||||
type Props = ButtonProps & {
|
||||
type: string,
|
||||
color: string
|
||||
};
|
||||
|
||||
class Button extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "button",
|
||||
color: "default"
|
||||
};
|
||||
|
||||
renderButton = () => {
|
||||
const {
|
||||
label,
|
||||
loading,
|
||||
disabled,
|
||||
type,
|
||||
color,
|
||||
action,
|
||||
fullWidth,
|
||||
className
|
||||
} = this.props;
|
||||
const loadingClass = loading ? "is-loading" : "";
|
||||
const fullWidthClass = fullWidth ? "is-fullwidth" : "";
|
||||
return (
|
||||
<button
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
onClick={action ? action : (event: Event) => {}}
|
||||
className={classNames(
|
||||
"button",
|
||||
"is-" + color,
|
||||
loadingClass,
|
||||
fullWidthClass,
|
||||
className
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { link } = this.props;
|
||||
if (link) {
|
||||
return <Link to={link}>{this.renderButton()}</Link>;
|
||||
} else {
|
||||
return this.renderButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Button;
|
||||
24
scm-ui-components/src/buttons/CreateButton.js
Normal file
24
scm-ui-components/src/buttons/CreateButton.js
Normal file
@@ -0,0 +1,24 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import injectSheet from "react-jss";
|
||||
import AddButton, { type ButtonProps } from "./Button";
|
||||
import classNames from "classnames";
|
||||
|
||||
const styles = {
|
||||
spacing: {
|
||||
margin: "1em 0 0 1em"
|
||||
}
|
||||
};
|
||||
|
||||
class CreateButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<div className={classNames("is-pulled-right", classes.spacing)}>
|
||||
<AddButton {...this.props} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default injectSheet(styles)(CreateButton);
|
||||
11
scm-ui-components/src/buttons/DeleteButton.js
Normal file
11
scm-ui-components/src/buttons/DeleteButton.js
Normal file
@@ -0,0 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class DeleteButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="warning" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default DeleteButton;
|
||||
11
scm-ui-components/src/buttons/EditButton.js
Normal file
11
scm-ui-components/src/buttons/EditButton.js
Normal file
@@ -0,0 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class EditButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button color="default" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default EditButton;
|
||||
33
scm-ui-components/src/buttons/RemoveEntryOfTableButton.js
Normal file
33
scm-ui-components/src/buttons/RemoveEntryOfTableButton.js
Normal file
@@ -0,0 +1,33 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { DeleteButton } from ".";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
entryname: string,
|
||||
removeEntry: string => void,
|
||||
disabled: boolean,
|
||||
label: string
|
||||
};
|
||||
|
||||
type State = {};
|
||||
|
||||
class RemoveEntryOfTableButton extends React.Component<Props, State> {
|
||||
render() {
|
||||
const { label, entryname, removeEntry, disabled } = this.props;
|
||||
return (
|
||||
<div className={classNames("is-pulled-right")}>
|
||||
<DeleteButton
|
||||
label={label}
|
||||
action={(event: Event) => {
|
||||
event.preventDefault();
|
||||
removeEntry(entryname);
|
||||
}}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default RemoveEntryOfTableButton;
|
||||
11
scm-ui-components/src/buttons/SubmitButton.js
Normal file
11
scm-ui-components/src/buttons/SubmitButton.js
Normal file
@@ -0,0 +1,11 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import Button, { type ButtonProps } from "./Button";
|
||||
|
||||
class SubmitButton extends React.Component<ButtonProps> {
|
||||
render() {
|
||||
return <Button type="submit" color="primary" {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default SubmitButton;
|
||||
10
scm-ui-components/src/buttons/index.js
Normal file
10
scm-ui-components/src/buttons/index.js
Normal file
@@ -0,0 +1,10 @@
|
||||
// @create-index
|
||||
|
||||
export { default as AddButton } from "./AddButton.js";
|
||||
export { default as Button } from "./Button.js";
|
||||
export { default as CreateButton } from "./CreateButton.js";
|
||||
export { default as DeleteButton } from "./DeleteButton.js";
|
||||
export { default as EditButton } from "./EditButton.js";
|
||||
export { default as RemoveEntryOfTableButton } from "./RemoveEntryOfTableButton.js";
|
||||
export { default as SubmitButton } from "./SubmitButton.js";
|
||||
|
||||
68
scm-ui-components/src/forms/AddEntryToTableField.js
Normal file
68
scm-ui-components/src/forms/AddEntryToTableField.js
Normal file
@@ -0,0 +1,68 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
import { AddButton } from "../buttons";
|
||||
import InputField from "./InputField";
|
||||
|
||||
type Props = {
|
||||
addEntry: string => void,
|
||||
disabled: boolean,
|
||||
buttonLabel: string,
|
||||
fieldLabel: string,
|
||||
errorMessage: string
|
||||
};
|
||||
|
||||
type State = {
|
||||
entryToAdd: string
|
||||
};
|
||||
|
||||
class AddEntryToTableField extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
entryToAdd: ""
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
const { disabled, buttonLabel, fieldLabel, errorMessage } = this.props;
|
||||
return (
|
||||
<div className="field">
|
||||
<InputField
|
||||
label={fieldLabel}
|
||||
errorMessage={errorMessage}
|
||||
onChange={this.handleAddEntryChange}
|
||||
validationError={false}
|
||||
value={this.state.entryToAdd}
|
||||
onReturnPressed={this.appendEntry}
|
||||
disabled={disabled}
|
||||
/>
|
||||
<AddButton
|
||||
label={buttonLabel}
|
||||
action={this.addButtonClicked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
addButtonClicked = (event: Event) => {
|
||||
event.preventDefault();
|
||||
this.appendEntry();
|
||||
};
|
||||
|
||||
appendEntry = () => {
|
||||
const { entryToAdd } = this.state;
|
||||
this.props.addEntry(entryToAdd);
|
||||
this.setState({ ...this.state, entryToAdd: "" });
|
||||
};
|
||||
|
||||
handleAddEntryChange = (entryname: string) => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
entryToAdd: entryname
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default AddEntryToTableField;
|
||||
36
scm-ui-components/src/forms/Checkbox.js
Normal file
36
scm-ui-components/src/forms/Checkbox.js
Normal file
@@ -0,0 +1,36 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
checked: boolean,
|
||||
onChange?: boolean => void,
|
||||
disabled?: boolean
|
||||
};
|
||||
class Checkbox extends React.Component<Props> {
|
||||
onCheckboxChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(event.target.checked);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="field">
|
||||
<div className="control">
|
||||
<label className="checkbox" disabled={this.props.disabled}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={this.props.checked}
|
||||
onChange={this.onCheckboxChange}
|
||||
disabled={this.props.disabled}
|
||||
/>
|
||||
{this.props.label}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Checkbox;
|
||||
94
scm-ui-components/src/forms/InputField.js
Normal file
94
scm-ui-components/src/forms/InputField.js
Normal file
@@ -0,0 +1,94 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
placeholder?: string,
|
||||
value?: string,
|
||||
type?: string,
|
||||
autofocus?: boolean,
|
||||
onChange: string => void,
|
||||
onReturnPressed?: () => void,
|
||||
validationError: boolean,
|
||||
errorMessage: string,
|
||||
disabled?: boolean
|
||||
};
|
||||
|
||||
class InputField extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
type: "text",
|
||||
placeholder: ""
|
||||
};
|
||||
|
||||
field: ?HTMLInputElement;
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autofocus && this.field) {
|
||||
this.field.focus();
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLInputElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
|
||||
handleKeyPress = (event: SyntheticKeyboardEvent<HTMLInputElement>) => {
|
||||
const onReturnPressed = this.props.onReturnPressed;
|
||||
if (!onReturnPressed) {
|
||||
return;
|
||||
}
|
||||
if (event.key === "Enter") {
|
||||
event.preventDefault();
|
||||
onReturnPressed();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
type,
|
||||
placeholder,
|
||||
value,
|
||||
validationError,
|
||||
errorMessage,
|
||||
disabled
|
||||
} = this.props;
|
||||
const errorView = validationError ? "is-danger" : "";
|
||||
const helper = validationError ? (
|
||||
<p className="help is-danger">{errorMessage}</p>
|
||||
) : (
|
||||
""
|
||||
);
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className="control">
|
||||
<input
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
className={classNames("input", errorView)}
|
||||
type={type}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
{helper}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default InputField;
|
||||
67
scm-ui-components/src/forms/Select.js
Normal file
67
scm-ui-components/src/forms/Select.js
Normal file
@@ -0,0 +1,67 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
options: SelectItem[],
|
||||
value?: SelectItem,
|
||||
onChange: string => void
|
||||
};
|
||||
|
||||
class Select extends React.Component<Props> {
|
||||
field: ?HTMLSelectElement;
|
||||
|
||||
componentDidMount() {
|
||||
// trigger change after render, if value is null to set it to the first value
|
||||
// of the given options.
|
||||
if (!this.props.value && this.field && this.field.value) {
|
||||
this.props.onChange(this.field.value);
|
||||
}
|
||||
}
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLSelectElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
render() {
|
||||
const { options, value } = this.props;
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className="control select">
|
||||
<select
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
value={value}
|
||||
onChange={this.handleInput}
|
||||
>
|
||||
{options.map(opt => {
|
||||
return (
|
||||
<option value={opt.value} key={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Select;
|
||||
53
scm-ui-components/src/forms/Textarea.js
Normal file
53
scm-ui-components/src/forms/Textarea.js
Normal file
@@ -0,0 +1,53 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
export type SelectItem = {
|
||||
value: string,
|
||||
label: string
|
||||
};
|
||||
|
||||
type Props = {
|
||||
label?: string,
|
||||
placeholder?: SelectItem[],
|
||||
value?: string,
|
||||
onChange: string => void
|
||||
};
|
||||
|
||||
class Textarea extends React.Component<Props> {
|
||||
field: ?HTMLTextAreaElement;
|
||||
|
||||
handleInput = (event: SyntheticInputEvent<HTMLTextAreaElement>) => {
|
||||
this.props.onChange(event.target.value);
|
||||
};
|
||||
|
||||
renderLabel = () => {
|
||||
const label = this.props.label;
|
||||
if (label) {
|
||||
return <label className="label">{label}</label>;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
render() {
|
||||
const { placeholder, value } = this.props;
|
||||
|
||||
return (
|
||||
<div className="field">
|
||||
{this.renderLabel()}
|
||||
<div className="control">
|
||||
<textarea
|
||||
className="textarea"
|
||||
ref={input => {
|
||||
this.field = input;
|
||||
}}
|
||||
placeholder={placeholder}
|
||||
onChange={this.handleInput}
|
||||
value={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Textarea;
|
||||
8
scm-ui-components/src/forms/index.js
Normal file
8
scm-ui-components/src/forms/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// @create-index
|
||||
|
||||
export { default as AddEntryToTableField } from "./AddEntryToTableField.js";
|
||||
export { default as Checkbox } from "./Checkbox.js";
|
||||
export { default as InputField } from "./InputField.js";
|
||||
export { default as Select } from "./Select.js";
|
||||
export { default as Textarea } from "./Textarea.js";
|
||||
|
||||
15
scm-ui-components/src/index.js
Normal file
15
scm-ui-components/src/index.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// @create-index
|
||||
|
||||
export { default as DateFromNow } from "./DateFromNow.js";
|
||||
export { default as ErrorNotification } from "./ErrorNotification.js";
|
||||
export { default as ErrorPage } from "./ErrorPage.js";
|
||||
export { default as Image } from "./Image.js";
|
||||
export { default as Loading } from "./Loading.js";
|
||||
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 ProtectedRoute } from "./ProtectedRoute.js";
|
||||
export { default as urls } from "./urls.js";
|
||||
export { default as validation } from "./validation.js";
|
||||
|
||||
25
scm-ui-components/src/layout/Footer.js
Normal file
25
scm-ui-components/src/layout/Footer.js
Normal file
@@ -0,0 +1,25 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import type { Me } from "../types";
|
||||
|
||||
type Props = {
|
||||
me?: Me
|
||||
};
|
||||
|
||||
class Footer extends React.Component<Props> {
|
||||
render() {
|
||||
const { me } = this.props;
|
||||
if (!me) {
|
||||
return "";
|
||||
}
|
||||
return (
|
||||
<footer className="footer">
|
||||
<div className="container is-centered">
|
||||
<p className="has-text-centered">{me.displayName}</p>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
31
scm-ui-components/src/layout/Header.js
Normal file
31
scm-ui-components/src/layout/Header.js
Normal file
@@ -0,0 +1,31 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import Logo from "./../Logo";
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Header extends React.Component<Props> {
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
return (
|
||||
<section className="hero is-dark is-small">
|
||||
<div className="hero-body">
|
||||
<div className="container">
|
||||
<div className="columns is-vcentered">
|
||||
<div className="column">
|
||||
<Logo />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hero-foot">
|
||||
<div className="container">{children}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Header;
|
||||
44
scm-ui-components/src/layout/Page.js
Normal file
44
scm-ui-components/src/layout/Page.js
Normal file
@@ -0,0 +1,44 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import Loading from "./../Loading";
|
||||
import ErrorNotification from "./../ErrorNotification";
|
||||
import Title from "./Title";
|
||||
import Subtitle from "./Subtitle";
|
||||
|
||||
type Props = {
|
||||
title?: string,
|
||||
subtitle?: string,
|
||||
loading?: boolean,
|
||||
error?: Error,
|
||||
showContentOnError?: boolean,
|
||||
children: React.Node
|
||||
};
|
||||
|
||||
class Page extends React.Component<Props> {
|
||||
render() {
|
||||
const { title, error, subtitle } = this.props;
|
||||
return (
|
||||
<section className="section">
|
||||
<div className="container">
|
||||
<Title title={title} />
|
||||
<Subtitle subtitle={subtitle} />
|
||||
<ErrorNotification error={error} />
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
const { loading, children, showContentOnError, error } = this.props;
|
||||
if (error && !showContentOnError) {
|
||||
return null;
|
||||
}
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
}
|
||||
return children;
|
||||
}
|
||||
}
|
||||
|
||||
export default Page;
|
||||
18
scm-ui-components/src/layout/Subtitle.js
Normal file
18
scm-ui-components/src/layout/Subtitle.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
subtitle?: string
|
||||
};
|
||||
|
||||
class Subtitle extends React.Component<Props> {
|
||||
render() {
|
||||
const { subtitle } = this.props;
|
||||
if (subtitle) {
|
||||
return <h1 className="subtitle">{subtitle}</h1>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Subtitle;
|
||||
18
scm-ui-components/src/layout/Title.js
Normal file
18
scm-ui-components/src/layout/Title.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
title?: string
|
||||
};
|
||||
|
||||
class Title extends React.Component<Props> {
|
||||
render() {
|
||||
const { title } = this.props;
|
||||
if (title) {
|
||||
return <h1 className="title">{title}</h1>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export default Title;
|
||||
8
scm-ui-components/src/layout/index.js
Normal file
8
scm-ui-components/src/layout/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
// @create-index
|
||||
|
||||
export { default as Footer } from "./Footer.js";
|
||||
export { default as Header } from "./Header.js";
|
||||
export { default as Page } from "./Page.js";
|
||||
export { default as Subtitle } from "./Subtitle.js";
|
||||
export { default as Title } from "./Title.js";
|
||||
|
||||
102
scm-ui-components/src/modals/ConfirmAlert.css
Normal file
102
scm-ui-components/src/modals/ConfirmAlert.css
Normal file
@@ -0,0 +1,102 @@
|
||||
/*modified from https://github.com/GA-MO/react-confirm-alert*/
|
||||
.react-confirm-alert-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flex;
|
||||
display: -o-flex;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
-ms-align-items: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
-webkit-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
-moz-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
-o-animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
animation: react-confirm-alert-fadeIn 0.5s 0.2s forwards;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
width: 400px;
|
||||
padding: 30px;
|
||||
text-align: left;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 20px 75px rgba(0, 0, 0, 0.13);
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body > h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.react-confirm-alert-body > h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.react-confirm-alert-button-group {
|
||||
display: -webkit-flex;
|
||||
display: -moz-flex;
|
||||
display: -ms-flex;
|
||||
display: -o-flex;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.react-confirm-alert-button-group > button {
|
||||
outline: none;
|
||||
background: #333;
|
||||
border: none;
|
||||
display: inline-block;
|
||||
padding: 6px 18px;
|
||||
color: #eee;
|
||||
margin-right: 10px;
|
||||
border-radius: 5px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@-webkit-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-o-keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes react-confirm-alert-fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
82
scm-ui-components/src/modals/ConfirmAlert.js
Normal file
82
scm-ui-components/src/modals/ConfirmAlert.js
Normal file
@@ -0,0 +1,82 @@
|
||||
// @flow
|
||||
//modified from https://github.com/GA-MO/react-confirm-alert
|
||||
|
||||
import * as React from "react";
|
||||
import { render, unmountComponentAtNode } from "react-dom";
|
||||
import "./ConfirmAlert.css";
|
||||
|
||||
type Button = {
|
||||
label: string,
|
||||
onClick: () => void | null
|
||||
};
|
||||
|
||||
type Props = {
|
||||
title: string,
|
||||
message: string,
|
||||
buttons: Button[]
|
||||
};
|
||||
|
||||
class ConfirmAlert extends React.Component<Props> {
|
||||
handleClickButton = (button: Button) => {
|
||||
if (button.onClick) {
|
||||
button.onClick();
|
||||
}
|
||||
this.close();
|
||||
};
|
||||
|
||||
close = () => {
|
||||
removeElementReconfirm();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { title, message, buttons } = this.props;
|
||||
|
||||
return (
|
||||
<div className="react-confirm-alert-overlay">
|
||||
<div className="react-confirm-alert">
|
||||
{
|
||||
<div className="react-confirm-alert-body">
|
||||
{title && <h1>{title}</h1>}
|
||||
{message}
|
||||
<div className="react-confirm-alert-button-group">
|
||||
{buttons.map((button, i) => (
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => this.handleClickButton(button)}
|
||||
>
|
||||
{button.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function createElementReconfirm(properties: Props) {
|
||||
const divTarget = document.createElement("div");
|
||||
divTarget.id = "react-confirm-alert";
|
||||
if (document.body) {
|
||||
document.body.appendChild(divTarget);
|
||||
render(<ConfirmAlert {...properties} />, divTarget);
|
||||
}
|
||||
}
|
||||
|
||||
function removeElementReconfirm() {
|
||||
const target = document.getElementById("react-confirm-alert");
|
||||
if (target) {
|
||||
unmountComponentAtNode(target);
|
||||
if (target.parentNode) {
|
||||
target.parentNode.removeChild(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function confirmAlert(properties: Props) {
|
||||
createElementReconfirm(properties);
|
||||
}
|
||||
|
||||
export default ConfirmAlert;
|
||||
4
scm-ui-components/src/modals/index.js
Normal file
4
scm-ui-components/src/modals/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
// @create-index
|
||||
|
||||
export { default as ConfirmAlert } from "./ConfirmAlert.js";
|
||||
|
||||
20
scm-ui-components/src/navigation/NavAction.js
Normal file
20
scm-ui-components/src/navigation/NavAction.js
Normal file
@@ -0,0 +1,20 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
|
||||
type Props = {
|
||||
label: string,
|
||||
action: () => void
|
||||
};
|
||||
|
||||
class NavAction extends React.Component<Props> {
|
||||
render() {
|
||||
const { label, action } = this.props;
|
||||
return (
|
||||
<li>
|
||||
<a onClick={action}>{label}</a>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NavAction;
|
||||
37
scm-ui-components/src/navigation/NavLink.js
Normal file
37
scm-ui-components/src/navigation/NavLink.js
Normal file
@@ -0,0 +1,37 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import { Route, Link } from "react-router-dom";
|
||||
|
||||
// TODO mostly copy of PrimaryNavigationLink
|
||||
|
||||
type Props = {
|
||||
to: string,
|
||||
label: string,
|
||||
activeOnlyWhenExact?: boolean
|
||||
};
|
||||
|
||||
class NavLink extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
activeOnlyWhenExact: true
|
||||
};
|
||||
|
||||
renderLink = (route: any) => {
|
||||
const { to, label } = this.props;
|
||||
return (
|
||||
<li>
|
||||
<Link className={route.match ? "is-active" : ""} to={to}>
|
||||
{label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { to, activeOnlyWhenExact } = this.props;
|
||||
return (
|
||||
<Route path={to} exact={activeOnlyWhenExact} children={this.renderLink} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default NavLink;
|
||||
14
scm-ui-components/src/navigation/Navigation.js
Normal file
14
scm-ui-components/src/navigation/Navigation.js
Normal file
@@ -0,0 +1,14 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Navigation extends React.Component<Props> {
|
||||
render() {
|
||||
return <aside className="menu">{this.props.children}</aside>;
|
||||
}
|
||||
}
|
||||
|
||||
export default Navigation;
|
||||
45
scm-ui-components/src/navigation/PrimaryNavigation.js
Normal file
45
scm-ui-components/src/navigation/PrimaryNavigation.js
Normal file
@@ -0,0 +1,45 @@
|
||||
//@flow
|
||||
import React from "react";
|
||||
import { translate } from "react-i18next";
|
||||
import PrimaryNavigationLink from "./PrimaryNavigationLink";
|
||||
|
||||
type Props = {
|
||||
t: string => string
|
||||
};
|
||||
|
||||
class PrimaryNavigation extends React.Component<Props> {
|
||||
render() {
|
||||
const { t } = this.props;
|
||||
return (
|
||||
<nav className="tabs is-boxed">
|
||||
<ul>
|
||||
<PrimaryNavigationLink
|
||||
to="/repos"
|
||||
match="/(repo|repos)"
|
||||
label={t("primary-navigation.repositories")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/users"
|
||||
match="/(user|users)"
|
||||
label={t("primary-navigation.users")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/groups"
|
||||
match="/(group|groups)"
|
||||
label={t("primary-navigation.groups")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/config"
|
||||
label={t("primary-navigation.config")}
|
||||
/>
|
||||
<PrimaryNavigationLink
|
||||
to="/logout"
|
||||
label={t("primary-navigation.logout")}
|
||||
/>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate("commons")(PrimaryNavigation);
|
||||
35
scm-ui-components/src/navigation/PrimaryNavigationLink.js
Normal file
35
scm-ui-components/src/navigation/PrimaryNavigationLink.js
Normal file
@@ -0,0 +1,35 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
import { Route, Link } from "react-router-dom";
|
||||
|
||||
type Props = {
|
||||
to: string,
|
||||
label: string,
|
||||
match?: string,
|
||||
activeOnlyWhenExact?: boolean
|
||||
};
|
||||
|
||||
class PrimaryNavigationLink extends React.Component<Props> {
|
||||
renderLink = (route: any) => {
|
||||
const { to, label } = this.props;
|
||||
return (
|
||||
<li className={route.match ? "is-active" : ""}>
|
||||
<Link to={to}>{label}</Link>
|
||||
</li>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { to, match, activeOnlyWhenExact } = this.props;
|
||||
const path = match ? match : to;
|
||||
return (
|
||||
<Route
|
||||
path={path}
|
||||
exact={activeOnlyWhenExact}
|
||||
children={this.renderLink}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PrimaryNavigationLink;
|
||||
21
scm-ui-components/src/navigation/Section.js
Normal file
21
scm-ui-components/src/navigation/Section.js
Normal file
@@ -0,0 +1,21 @@
|
||||
//@flow
|
||||
import * as React from "react";
|
||||
|
||||
type Props = {
|
||||
label: string,
|
||||
children?: React.Node
|
||||
};
|
||||
|
||||
class Section extends React.Component<Props> {
|
||||
render() {
|
||||
const { label, children } = this.props;
|
||||
return (
|
||||
<div>
|
||||
<p className="menu-label">{label}</p>
|
||||
<ul className="menu-list">{children}</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Section;
|
||||
9
scm-ui-components/src/navigation/index.js
Normal file
9
scm-ui-components/src/navigation/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
// @create-index
|
||||
|
||||
export { default as NavAction } from "./NavAction.js";
|
||||
export { default as NavLink } from "./NavLink.js";
|
||||
export { default as Navigation } from "./Navigation.js";
|
||||
export { default as PrimaryNavigation } from "./PrimaryNavigation.js";
|
||||
export { default as PrimaryNavigationLink } from "./PrimaryNavigationLink.js";
|
||||
export { default as Section } from "./Section.js";
|
||||
|
||||
12
scm-ui-components/src/tests/enzyme.js
Normal file
12
scm-ui-components/src/tests/enzyme.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import "raf/polyfill";
|
||||
import { configure } from "enzyme";
|
||||
import Adapter from "enzyme-adapter-react-16";
|
||||
|
||||
// Temporary hack to suppress error
|
||||
// https://github.com/facebook/create-react-app/issues/3199#issuecomment-345024029
|
||||
window.requestAnimationFrame = function(callback) {
|
||||
setTimeout(callback, 0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
configure({ adapter: new Adapter() });
|
||||
7
scm-ui-components/src/tests/i18n.js
Normal file
7
scm-ui-components/src/tests/i18n.js
Normal file
@@ -0,0 +1,7 @@
|
||||
jest.mock("react-i18next", () => ({
|
||||
// this mock makes sure any components using the translate HoC receive the t function as a prop
|
||||
translate: () => Component => {
|
||||
Component.defaultProps = { ...Component.defaultProps, t: key => key };
|
||||
return Component;
|
||||
}
|
||||
}));
|
||||
7
scm-ui-components/src/types/Action.js
Normal file
7
scm-ui-components/src/types/Action.js
Normal file
@@ -0,0 +1,7 @@
|
||||
// @flow
|
||||
export type Action = {
|
||||
type: string,
|
||||
payload?: any,
|
||||
itemId?: string | number,
|
||||
resetPending?: boolean
|
||||
};
|
||||
27
scm-ui-components/src/types/Config.js
Normal file
27
scm-ui-components/src/types/Config.js
Normal file
@@ -0,0 +1,27 @@
|
||||
//@flow
|
||||
import type { Links } from "./hal";
|
||||
|
||||
export type Config = {
|
||||
proxyPassword: string | null,
|
||||
proxyPort: number,
|
||||
proxyServer: string,
|
||||
proxyUser: string | null,
|
||||
enableProxy: boolean,
|
||||
realmDescription: string,
|
||||
enableRepositoryArchive: boolean,
|
||||
disableGroupingGrid: boolean,
|
||||
dateFormat: string,
|
||||
anonymousAccessEnabled: boolean,
|
||||
adminGroups: string[],
|
||||
adminUsers: string[],
|
||||
baseUrl: string,
|
||||
forceBaseUrl: boolean,
|
||||
loginAttemptLimit: number,
|
||||
proxyExcludes: string[],
|
||||
skipFailedAuthenticators: boolean,
|
||||
pluginUrl: string,
|
||||
loginAttemptLimitTimeout: number,
|
||||
enabledXsrfProtection: boolean,
|
||||
defaultNamespaceStrategy: string,
|
||||
_links: Links
|
||||
};
|
||||
19
scm-ui-components/src/types/Group.js
Normal file
19
scm-ui-components/src/types/Group.js
Normal file
@@ -0,0 +1,19 @@
|
||||
//@flow
|
||||
import type { Collection, Links } from "./hal";
|
||||
|
||||
export type Member = {
|
||||
name: string,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
export type Group = Collection & {
|
||||
name: string,
|
||||
description: string,
|
||||
type: string,
|
||||
members: string[],
|
||||
_embedded: {
|
||||
members: Member[]
|
||||
},
|
||||
creationDate?: string,
|
||||
lastModified?: string
|
||||
};
|
||||
6
scm-ui-components/src/types/Me.js
Normal file
6
scm-ui-components/src/types/Me.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// @flow
|
||||
|
||||
export type Me = {
|
||||
name: string,
|
||||
displayName: string
|
||||
};
|
||||
24
scm-ui-components/src/types/Repositories.js
Normal file
24
scm-ui-components/src/types/Repositories.js
Normal file
@@ -0,0 +1,24 @@
|
||||
//@flow
|
||||
import type { PagedCollection, Links } from "./hal";
|
||||
|
||||
export type Repository = {
|
||||
namespace: string,
|
||||
name: string,
|
||||
type: string,
|
||||
contact?: string,
|
||||
description?: string,
|
||||
creationDate?: string,
|
||||
lastModified?: string,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
export type RepositoryCollection = PagedCollection & {
|
||||
_embedded: {
|
||||
repositories: Repository[] | string[]
|
||||
}
|
||||
};
|
||||
|
||||
export type RepositoryGroup = {
|
||||
name: string,
|
||||
repositories: Repository[]
|
||||
};
|
||||
14
scm-ui-components/src/types/RepositoryTypes.js
Normal file
14
scm-ui-components/src/types/RepositoryTypes.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// @flow
|
||||
|
||||
import type { Collection } from "./hal";
|
||||
|
||||
export type RepositoryType = {
|
||||
name: string,
|
||||
displayName: string
|
||||
};
|
||||
|
||||
export type RepositoryTypeCollection = Collection & {
|
||||
_embedded: {
|
||||
repositoryTypes: RepositoryType[]
|
||||
}
|
||||
};
|
||||
15
scm-ui-components/src/types/User.js
Normal file
15
scm-ui-components/src/types/User.js
Normal file
@@ -0,0 +1,15 @@
|
||||
//@flow
|
||||
import type { Links } from "./hal";
|
||||
|
||||
export type User = {
|
||||
displayName: string,
|
||||
name: string,
|
||||
mail: string,
|
||||
password: string,
|
||||
admin: boolean,
|
||||
active: boolean,
|
||||
type?: string,
|
||||
creationDate?: string,
|
||||
lastModified?: string,
|
||||
_links: Links
|
||||
};
|
||||
16
scm-ui-components/src/types/hal.js
Normal file
16
scm-ui-components/src/types/hal.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// @flow
|
||||
export type Link = {
|
||||
href: string
|
||||
};
|
||||
|
||||
export type Links = { [string]: Link };
|
||||
|
||||
export type Collection = {
|
||||
_embedded: Object,
|
||||
_links: Links
|
||||
};
|
||||
|
||||
export type PagedCollection = Collection & {
|
||||
page: number,
|
||||
pageTotal: number
|
||||
};
|
||||
12
scm-ui-components/src/types/index.js
Normal file
12
scm-ui-components/src/types/index.js
Normal file
@@ -0,0 +1,12 @@
|
||||
// @flow
|
||||
export type { Action } from "./Action";
|
||||
export type { Link, Links, Collection, PagedCollection } from "./hal";
|
||||
|
||||
export type { Me } from "./Me";
|
||||
export type { User } from "./User";
|
||||
export type { Group, Member } from "./Group";
|
||||
|
||||
export type { Repository, RepositoryCollection, RepositoryGroup } from "./Repositories";
|
||||
export type { RepositoryType, RepositoryTypeCollection } from "./RepositoryTypes";
|
||||
|
||||
export type { Config } from "./Config";
|
||||
6
scm-ui-components/src/urls.js
Normal file
6
scm-ui-components/src/urls.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// @flow
|
||||
export const contextPath = window.ctxPath || "";
|
||||
|
||||
export function withContextPath(path: string) {
|
||||
return contextPath + path;
|
||||
}
|
||||
16
scm-ui-components/src/validation.js
Normal file
16
scm-ui-components/src/validation.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// @flow
|
||||
const nameRegex = /^([A-z0-9.\-_@]|[^ ]([A-z0-9.\-_@ ]*[A-z0-9.\-_@]|[^\s])?)$/;
|
||||
|
||||
export const isNameValid = (name: string) => {
|
||||
return nameRegex.test(name);
|
||||
};
|
||||
|
||||
const mailRegex = /^[A-z0-9][\w.-]*@[A-z0-9][\w\-.]*\.[A-z0-9][A-z0-9-]+$/;
|
||||
|
||||
export const isMailValid = (mail: string) => {
|
||||
return mailRegex.test(mail);
|
||||
};
|
||||
|
||||
export const isNumberValid = (number: string) => {
|
||||
return !isNaN(number);
|
||||
};
|
||||
102
scm-ui-components/src/validation.test.js
Normal file
102
scm-ui-components/src/validation.test.js
Normal file
@@ -0,0 +1,102 @@
|
||||
// @flow
|
||||
import * as validator from "./validation";
|
||||
|
||||
describe("test name validation", () => {
|
||||
it("should return false", () => {
|
||||
// invalid names taken from ValidationUtilTest.java
|
||||
const invalidNames = [
|
||||
" test 123",
|
||||
" test 123 ",
|
||||
"test 123 ",
|
||||
"test/123",
|
||||
"test%123",
|
||||
"test:123",
|
||||
"t ",
|
||||
" t",
|
||||
" t ",
|
||||
"",
|
||||
|
||||
" invalid_name",
|
||||
"another%one",
|
||||
"!!!",
|
||||
"!_!"
|
||||
];
|
||||
for (let name of invalidNames) {
|
||||
expect(validator.isNameValid(name)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return true", () => {
|
||||
// valid names taken from ValidationUtilTest.java
|
||||
const validNames = [
|
||||
"test",
|
||||
"test.git",
|
||||
"Test123.git",
|
||||
"Test123-git",
|
||||
"Test_user-123.git",
|
||||
"test@scm-manager.de",
|
||||
"test 123",
|
||||
"tt",
|
||||
"t",
|
||||
|
||||
"valid_name",
|
||||
"another1",
|
||||
"stillValid",
|
||||
"this.one_as-well",
|
||||
"and@this"
|
||||
];
|
||||
for (let name of validNames) {
|
||||
expect(validator.isNameValid(name)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("test mail validation", () => {
|
||||
it("should return false", () => {
|
||||
// invalid taken from ValidationUtilTest.java
|
||||
const invalid = [
|
||||
"ostfalia.de",
|
||||
"@ostfalia.de",
|
||||
"s.sdorra@",
|
||||
"s.sdorra@ostfalia",
|
||||
"s.sdorra@@ostfalia.de",
|
||||
"s.sdorra@ ostfalia.de",
|
||||
"s.sdorra @ostfalia.de"
|
||||
];
|
||||
for (let mail of invalid) {
|
||||
expect(validator.isMailValid(mail)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("should return true", () => {
|
||||
// valid taken from ValidationUtilTest.java
|
||||
const valid = [
|
||||
"s.sdorra@ostfalia.de",
|
||||
"sdorra@ostfalia.de",
|
||||
"s.sdorra@hbk-bs.de",
|
||||
"s.sdorra@gmail.com",
|
||||
"s.sdorra@t.co",
|
||||
"s.sdorra@ucla.college",
|
||||
"s.sdorra@example.xn--p1ai",
|
||||
"s.sdorra@scm.solutions"
|
||||
];
|
||||
for (let mail of valid) {
|
||||
expect(validator.isMailValid(mail)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("test number validation", () => {
|
||||
it("should return false", () => {
|
||||
const invalid = ["1a", "35gu", "dj6", "45,5", "test"];
|
||||
for (let number of invalid) {
|
||||
expect(validator.isNumberValid(number)).toBe(false);
|
||||
}
|
||||
});
|
||||
it("should return true", () => {
|
||||
const valid = ["1", "35", "2", "235", "34.4"];
|
||||
for (let number of valid) {
|
||||
expect(validator.isNumberValid(number)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
8163
scm-ui-components/yarn.lock
Normal file
8163
scm-ui-components/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,8 +31,8 @@
|
||||
"webfonts": "copyfiles -f node_modules/@fortawesome/fontawesome-free/webfonts/* target/styles/webfonts",
|
||||
"build-css": "node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/styles",
|
||||
"watch-css": "npm run build-css && node-sass-chokidar --include-path ./styles --include-path ./node_modules styles/ -o target/styles --watch --recursive",
|
||||
"start-js": "ui-bundler serve",
|
||||
"start": "npm-run-all -p webfonts watch-css build-vendor start-js",
|
||||
"start-js": "ui-bundler serve --vendor vendor.bundle.js",
|
||||
"start": "npm-run-all -p webfonts watch-css start-js",
|
||||
"build-js": "ui-bundler bundle target/scm-ui.bundle.js",
|
||||
"build-vendor": "ui-bundler vendor target/vendor.bundle.js",
|
||||
"build": "npm-run-all -s webfonts build-css build-vendor build-js",
|
||||
@@ -42,7 +42,7 @@
|
||||
"pre-commit": "jest && flow && eslint src"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@scm-manager/ui-bundler": "^0.0.9",
|
||||
"@scm-manager/ui-bundler": "^0.0.12",
|
||||
"copyfiles": "^2.0.0",
|
||||
"enzyme": "^3.3.0",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
|
||||
@@ -31,18 +31,28 @@
|
||||
<plugin>
|
||||
<groupId>com.github.sdorra</groupId>
|
||||
<artifactId>buildfrontend-maven-plugin</artifactId>
|
||||
<version>2.0.1</version>
|
||||
<version>2.1.0</version>
|
||||
<configuration>
|
||||
<node>
|
||||
<version>8.11.3</version>
|
||||
<version>8.11.4</version>
|
||||
</node>
|
||||
<pkgManager>
|
||||
<type>YARN</type>
|
||||
<version>1.7.0</version>
|
||||
<version>1.9.4</version>
|
||||
</pkgManager>
|
||||
<script>run</script>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>link-components</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>install-link</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<pkg>@scm-manager/ui-components</pkg>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>install</id>
|
||||
<phase>process-resources</phase>
|
||||
|
||||
@@ -732,9 +732,9 @@
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/eslint-config/-/eslint-config-0.0.2.tgz#94cc8c3fb4f51f870b235893dc134fc6c423ae85"
|
||||
|
||||
"@scm-manager/ui-bundler@^0.0.9":
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.9.tgz#b61bdccaf6cf0ff3f4856098f95b462223c626c8"
|
||||
"@scm-manager/ui-bundler@^0.0.10":
|
||||
version "0.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@scm-manager/ui-bundler/-/ui-bundler-0.0.10.tgz#94c90ed1c1830352e274e048158843436ce31352"
|
||||
dependencies:
|
||||
"@babel/core" "^7.0.0"
|
||||
"@babel/plugin-proposal-class-properties" "^7.0.0"
|
||||
|
||||
Reference in New Issue
Block a user