Fix validation in "Add Entry" components (#1625)

This commit is contained in:
Florian Scholdei
2021-04-21 14:36:52 +02:00
committed by GitHub
parent b6018280ed
commit 05ef203038
4 changed files with 115 additions and 146 deletions

View File

@@ -0,0 +1,2 @@
- type: fixed
description: Validation in "Add Entry" components for configuration table ([#1625](https://github.com/scm-manager/scm-manager/pull/1625))

View File

@@ -29,6 +29,7 @@ import { createAttributesForTesting } from "../devBuild";
export type ButtonProps = { export type ButtonProps = {
label?: string; label?: string;
title?: string;
loading?: boolean; loading?: boolean;
disabled?: boolean; disabled?: boolean;
action?: (event: MouseEvent) => void; action?: (event: MouseEvent) => void;
@@ -43,7 +44,6 @@ export type ButtonProps = {
type Props = ButtonProps & type Props = ButtonProps &
RouteComponentProps & { RouteComponentProps & {
title?: string;
type?: "button" | "submit" | "reset"; type?: "button" | "submit" | "reset";
color?: string; color?: string;
}; };

View File

@@ -21,24 +21,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
import React, { MouseEvent } from "react"; import React, { FC, useState, MouseEvent } from "react";
import styled from "styled-components"; import styled from "styled-components";
import Level from "../layout/Level"; import Level from "../layout/Level";
import InputField from "./InputField";
import AddButton from "../buttons/AddButton"; import AddButton from "../buttons/AddButton";
import InputField from "./InputField";
type Props = { type Props = {
addEntry: (p: string) => void; addEntry: (p: string) => void;
disabled: boolean; disabled?: boolean;
buttonLabel: string; buttonLabel: string;
fieldLabel: string; fieldLabel: string;
errorMessage: string;
helpText?: string; helpText?: string;
validateEntry?: (p: string) => boolean; validateEntry?: (p: string) => boolean;
}; errorMessage: string;
type State = {
entryToAdd: string;
}; };
const StyledLevel = styled(Level)` const StyledLevel = styled(Level)`
@@ -46,46 +42,62 @@ const StyledLevel = styled(Level)`
margin-bottom: 1rem !important; // same margin as field margin-bottom: 1rem !important; // same margin as field
`; `;
const StyledInputField = styled(InputField)` const FullWidthInputField = styled(InputField)`
width: 100%; width: 100%;
margin-right: 1.5rem; margin-right: 1.5rem;
`; `;
const StyledField = styled.div.attrs(props => ({ const StyledField = styled.div.attrs(() => ({
className: "field" className: "field"
}))` }))`
align-self: flex-end; align-self: flex-end;
`; `;
class AddEntryToTableField extends React.Component<Props, State> { const AddEntryToTableField: FC<Props> = ({
constructor(props: Props) { addEntry,
super(props); disabled,
this.state = { buttonLabel,
entryToAdd: "" fieldLabel,
}; helpText,
} validateEntry,
errorMessage
}) => {
const [entryToAdd, setEntryToAdd] = useState("");
isValid = () => { const handleAddEntryChange = (entryName: string) => {
const { validateEntry } = this.props; setEntryToAdd(entryName);
if (!this.state.entryToAdd || this.state.entryToAdd === "" || !validateEntry) { };
const addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
appendEntry();
};
const appendEntry = () => {
if (!disabled && entryToAdd !== "" && isValid()) {
addEntry(entryToAdd);
setEntryToAdd("");
}
};
const isValid = () => {
if (entryToAdd === "" || !validateEntry) {
return true; return true;
} else { } else {
return validateEntry(this.state.entryToAdd); return validateEntry(entryToAdd);
} }
}; };
render() {
const { disabled, buttonLabel, fieldLabel, errorMessage, helpText } = this.props;
return ( return (
<StyledLevel <StyledLevel
children={ children={
<StyledInputField <FullWidthInputField
label={fieldLabel} label={fieldLabel}
errorMessage={errorMessage} errorMessage={errorMessage}
onChange={this.handleAddEntryChange} onChange={handleAddEntryChange}
validationError={!this.isValid()} validationError={!isValid()}
value={this.state.entryToAdd} value={entryToAdd}
onReturnPressed={this.appendEntry} onReturnPressed={appendEntry}
disabled={disabled} disabled={disabled}
helpText={helpText} helpText={helpText}
/> />
@@ -94,35 +106,13 @@ class AddEntryToTableField extends React.Component<Props, State> {
<StyledField> <StyledField>
<AddButton <AddButton
label={buttonLabel} label={buttonLabel}
action={this.addButtonClicked} action={addButtonClicked}
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()} disabled={disabled || entryToAdd === "" || !isValid()}
/> />
</StyledField> </StyledField>
} }
/> />
); );
} };
addButtonClicked = (event: MouseEvent) => {
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; export default AddEntryToTableField;

View File

@@ -21,16 +21,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
import React, { MouseEvent } from "react"; import React, { FC, useState, MouseEvent } from "react";
import styled from "styled-components"; import styled from "styled-components";
import { SelectValue } from "@scm-manager/ui-types"; import { SelectValue } from "@scm-manager/ui-types";
import Level from "../layout/Level"; import Level from "../layout/Level";
import Autocomplete from "../Autocomplete";
import AddButton from "../buttons/AddButton"; import AddButton from "../buttons/AddButton";
import Autocomplete from "../Autocomplete";
type Props = { type Props = {
addEntry: (p: SelectValue) => void; addEntry: (p: SelectValue) => void;
disabled: boolean; disabled?: boolean;
buttonLabel: string; buttonLabel: string;
fieldLabel: string; fieldLabel: string;
helpText?: string; helpText?: string;
@@ -40,24 +40,13 @@ type Props = {
noOptionsMessage?: string; noOptionsMessage?: string;
}; };
type State = { const FullWidthAutocomplete = styled(Autocomplete)`
selectedValue?: SelectValue;
};
const StyledAutocomplete = styled(Autocomplete)`
width: 100%; width: 100%;
margin-right: 1.5rem; margin-right: 1.5rem;
`; `;
class AutocompleteAddEntryToTableField extends React.Component<Props, State> { const AutocompleteAddEntryToTableField: FC<Props> = ({
constructor(props: Props) { addEntry,
super(props);
this.state = {
selectedValue: undefined
};
}
render() {
const {
disabled, disabled,
buttonLabel, buttonLabel,
fieldLabel, fieldLabel,
@@ -66,16 +55,33 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
placeholder, placeholder,
loadingMessage, loadingMessage,
noOptionsMessage noOptionsMessage
} = this.props; }) => {
const [selectedValue, setSelectedValue] = useState<SelectValue | undefined>(undefined);
const handleAddEntryChange = (selection: SelectValue) => {
setSelectedValue(selection);
};
const addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
appendEntry();
};
const appendEntry = () => {
if (disabled || !selectedValue) {
return;
}
addEntry(selectedValue);
setSelectedValue(undefined);
};
const { selectedValue } = this.state;
return ( return (
<Level <Level
children={ children={
<StyledAutocomplete <FullWidthAutocomplete
label={fieldLabel} label={fieldLabel}
loadSuggestions={loadSuggestions} loadSuggestions={loadSuggestions}
valueSelected={this.handleAddEntryChange} valueSelected={handleAddEntryChange}
helpText={helpText} helpText={helpText}
value={selectedValue} value={selectedValue}
placeholder={placeholder} placeholder={placeholder}
@@ -86,40 +92,11 @@ class AutocompleteAddEntryToTableField extends React.Component<Props, State> {
} }
right={ right={
<div className="field"> <div className="field">
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} /> <AddButton label={buttonLabel} action={addButtonClicked} disabled={disabled} />
</div> </div>
} }
/> />
); );
} };
addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
this.appendEntry();
};
appendEntry = () => {
const { selectedValue } = this.state;
if (!selectedValue) {
return;
}
this.setState(
// @ts-ignore
{
...this.state,
// @ts-ignore null is needed to clear the selection; undefined does not work
selectedValue: null
},
() => this.props.addEntry(selectedValue)
);
};
handleAddEntryChange = (selection: SelectValue) => {
this.setState({
...this.state,
selectedValue: selection
});
};
}
export default AutocompleteAddEntryToTableField; export default AutocompleteAddEntryToTableField;