mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-09 06:55:47 +01:00
Bootstrapped validation for groups
This commit is contained in:
@@ -5,14 +5,17 @@ import InputField from "../../components/forms/InputField";
|
|||||||
import { SubmitButton } from "../../components/buttons";
|
import { SubmitButton } from "../../components/buttons";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import type { Group } from "../types/Group";
|
import type { Group } from "../types/Group";
|
||||||
|
import * as validator from "./groupValidation"
|
||||||
|
|
||||||
export interface Props {
|
type Props = {
|
||||||
t: string => string;
|
t: string => string,
|
||||||
submitForm: Group => void;
|
submitForm: Group => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
type State = {
|
||||||
group: Group;
|
group: Group,
|
||||||
|
nameValidationError: boolean,
|
||||||
|
descriptionValidationError: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class GroupForm extends React.Component<Props, State> {
|
class GroupForm extends React.Component<Props, State> {
|
||||||
@@ -28,21 +31,27 @@ class GroupForm extends React.Component<Props, State> {
|
|||||||
_links: {},
|
_links: {},
|
||||||
members: [],
|
members: [],
|
||||||
type: "",
|
type: "",
|
||||||
}
|
},
|
||||||
|
nameValidationError: false,
|
||||||
|
descriptionValidationError: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubmit = (event: Event) => {
|
onSubmit = (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.submitForm(this.state.group);
|
this.props.submitForm(this.state.group);
|
||||||
};
|
};
|
||||||
|
|
||||||
isValid = () => {
|
isValid = () => {
|
||||||
return true;
|
const group = this.state.group;
|
||||||
|
return !(this.state.nameValidationError || this.state.descriptionValidationError || group.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
submit = (event: Event) => {
|
submit = (event: Event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.props.submitForm(this.state.group)
|
if (this.isValid) {
|
||||||
|
this.props.submitForm(this.state.group)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -51,15 +60,15 @@ class GroupForm extends React.Component<Props, State> {
|
|||||||
<form onSubmit={this.onSubmit}>
|
<form onSubmit={this.onSubmit}>
|
||||||
<InputField
|
<InputField
|
||||||
label={t("group.name")}
|
label={t("group.name")}
|
||||||
errorMessage=""
|
errorMessage="group name invalid"
|
||||||
onChange={this.handleGroupNameChange}
|
onChange={this.handleGroupNameChange}
|
||||||
validationError={false}
|
validationError={this.state.nameValidationError}
|
||||||
/>
|
/>
|
||||||
<InputField
|
<InputField
|
||||||
label={t("group.description")}
|
label={t("group.description")}
|
||||||
errorMessage=""
|
errorMessage=""
|
||||||
onChange={this.handleDescriptionChange}
|
onChange={this.handleDescriptionChange}
|
||||||
validationError={false}
|
validationError={this.state.descriptionValidationError}
|
||||||
/>
|
/>
|
||||||
<SubmitButton label={t("group-form.submit")} />
|
<SubmitButton label={t("group-form.submit")} />
|
||||||
</form>
|
</form>
|
||||||
@@ -68,19 +77,14 @@ class GroupForm extends React.Component<Props, State> {
|
|||||||
|
|
||||||
handleGroupNameChange = (name: string) => {
|
handleGroupNameChange = (name: string) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
group: {
|
nameValidationError: !validator.isNameValid(name),
|
||||||
...this.state.group,
|
group: {...this.state.group, name}
|
||||||
name
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
handleDescriptionChange = (description: string) => {
|
handleDescriptionChange = (description: string) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
group: {
|
group: {...this.state.group, description }
|
||||||
...this.state.group,
|
|
||||||
description
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
10
scm-ui/src/groups/components/groupValidation.js
Normal file
10
scm-ui/src/groups/components/groupValidation.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// @flow
|
||||||
|
|
||||||
|
//TODO: How should a group be validated
|
||||||
|
//TODO: Tests!
|
||||||
|
|
||||||
|
const nameRegex = /^([A-z0-9.\-_@]|[^ ]([A-z0-9.\-_@ ]*[A-z0-9.\-_@]|[^\s])?)$/;
|
||||||
|
|
||||||
|
export const isNameValid = (name: string) => {
|
||||||
|
return nameRegex.test(name);
|
||||||
|
};
|
||||||
@@ -3,17 +3,19 @@ import React from "react";
|
|||||||
|
|
||||||
import Page from "../../components/layout/Page";
|
import Page from "../../components/layout/Page";
|
||||||
import { translate } from "react-i18next";
|
import { translate } from "react-i18next";
|
||||||
import GroupForm from "./GroupForm";
|
import GroupForm from "../components/GroupForm";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { createGroup } from "../modules/groups";
|
import { createGroup } from "../modules/groups";
|
||||||
import type { Group } from "../types/Group";
|
import type { Group } from "../types/Group";
|
||||||
|
import type { History } from "history";
|
||||||
|
|
||||||
export interface Props {
|
type Props = {
|
||||||
t: string => string;
|
t: string => string,
|
||||||
createGroup: Group => void;
|
createGroup: (group: Group, callback?: () => void) => void,
|
||||||
|
history: History
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface State {}
|
type State = {}
|
||||||
|
|
||||||
class AddGroup extends React.Component<Props, State> {
|
class AddGroup extends React.Component<Props, State> {
|
||||||
render() {
|
render() {
|
||||||
@@ -27,14 +29,18 @@ class AddGroup extends React.Component<Props, State> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
groupCreated = () => {
|
||||||
|
console.log("pushing history")
|
||||||
|
this.props.history.push("/groups")
|
||||||
|
}
|
||||||
createGroup = (group: Group) => {
|
createGroup = (group: Group) => {
|
||||||
this.props.createGroup(group);
|
this.props.createGroup(group, this.groupCreated)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
return {
|
return {
|
||||||
createGroup: (group: Group) => dispatch(createGroup(group))
|
createGroup: (group: Group, callback?: () => void) => dispatch(createGroup(group, callback))
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,12 +133,16 @@ export function fetchGroupFailure(name: string, error: Error): Action {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//create group
|
//create group
|
||||||
export function createGroup(group: Group) {
|
export function createGroup(group: Group, callback?: () => void) {
|
||||||
return function(dispatch: Dispatch) {
|
return function(dispatch: Dispatch) {
|
||||||
dispatch(createGroupPending());
|
dispatch(createGroupPending());
|
||||||
return apiClient
|
return apiClient
|
||||||
.postWithContentType(GROUPS_URL, group, CONTENT_TYPE_GROUP)
|
.postWithContentType(GROUPS_URL, group, CONTENT_TYPE_GROUP)
|
||||||
.then(() => dispatch(createGroupSuccess()))
|
.then(() => {
|
||||||
|
dispatch(createGroupSuccess())
|
||||||
|
if (callback) {
|
||||||
|
callback();
|
||||||
|
}})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
dispatch(
|
dispatch(
|
||||||
createGroupFailure(
|
createGroupFailure(
|
||||||
@@ -168,6 +172,11 @@ export function createGroupFailure(error: Error) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createGroupReset() {
|
||||||
|
return {
|
||||||
|
type: CREATE_GROUP_RESET
|
||||||
|
}
|
||||||
|
}
|
||||||
//delete group
|
//delete group
|
||||||
|
|
||||||
export function deleteGroup(group: Group, callback?: () => void) {
|
export function deleteGroup(group: Group, callback?: () => void) {
|
||||||
|
|||||||
@@ -205,6 +205,25 @@ describe("groups fetch()", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should call the callback after creating group", () => {
|
||||||
|
fetchMock.postOnce(GROUPS_URL, {
|
||||||
|
status: 201
|
||||||
|
});
|
||||||
|
let called = false;
|
||||||
|
|
||||||
|
const callMe = () => {
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
const store = mockStore({});
|
||||||
|
return store.dispatch(createGroup(humanGroup, callMe)).then(() => {
|
||||||
|
const actions = store.getActions();
|
||||||
|
expect(actions[0].type).toEqual(CREATE_GROUP_PENDING);
|
||||||
|
expect(actions[1].type).toEqual(CREATE_GROUP_SUCCESS);
|
||||||
|
expect(called).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
it("should fail creating group on HTTP 500", () => {
|
it("should fail creating group on HTTP 500", () => {
|
||||||
fetchMock.postOnce(GROUPS_URL, {
|
fetchMock.postOnce(GROUPS_URL, {
|
||||||
status: 500
|
status: 500
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//@flow
|
//@flow
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import UserForm from "./../components/UserForm";
|
import UserForm from "../components/UserForm";
|
||||||
import type { User } from "../types/User";
|
import type { User } from "../types/User";
|
||||||
import type { History } from "history";
|
import type { History } from "history";
|
||||||
import {
|
import {
|
||||||
|
|||||||
Reference in New Issue
Block a user