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,83 +42,77 @@ 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) { };
return true;
} else { const addButtonClicked = (event: MouseEvent) => {
return validateEntry(this.state.entryToAdd); event.preventDefault();
appendEntry();
};
const appendEntry = () => {
if (!disabled && entryToAdd !== "" && isValid()) {
addEntry(entryToAdd);
setEntryToAdd("");
} }
}; };
render() { const isValid = () => {
const { disabled, buttonLabel, fieldLabel, errorMessage, helpText } = this.props; if (entryToAdd === "" || !validateEntry) {
return ( return true;
<StyledLevel } else {
children={ return validateEntry(entryToAdd);
<StyledInputField }
label={fieldLabel} };
errorMessage={errorMessage}
onChange={this.handleAddEntryChange} return (
validationError={!this.isValid()} <StyledLevel
value={this.state.entryToAdd} children={
onReturnPressed={this.appendEntry} <FullWidthInputField
disabled={disabled} label={fieldLabel}
helpText={helpText} errorMessage={errorMessage}
onChange={handleAddEntryChange}
validationError={!isValid()}
value={entryToAdd}
onReturnPressed={appendEntry}
disabled={disabled}
helpText={helpText}
/>
}
right={
<StyledField>
<AddButton
label={buttonLabel}
action={addButtonClicked}
disabled={disabled || entryToAdd === "" || !isValid()}
/> />
} </StyledField>
right={ }
<StyledField> />
<AddButton );
label={buttonLabel} };
action={this.addButtonClicked}
disabled={disabled || this.state.entryToAdd === "" || !this.isValid()}
/>
</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,86 +40,63 @@ 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); disabled,
this.state = { buttonLabel,
selectedValue: undefined fieldLabel,
}; helpText,
} loadSuggestions,
render() { placeholder,
const { loadingMessage,
disabled, noOptionsMessage
buttonLabel, }) => {
fieldLabel, const [selectedValue, setSelectedValue] = useState<SelectValue | undefined>(undefined);
helpText,
loadSuggestions,
placeholder,
loadingMessage,
noOptionsMessage
} = this.props;
const { selectedValue } = this.state; const handleAddEntryChange = (selection: SelectValue) => {
return ( setSelectedValue(selection);
<Level
children={
<StyledAutocomplete
label={fieldLabel}
loadSuggestions={loadSuggestions}
valueSelected={this.handleAddEntryChange}
helpText={helpText}
value={selectedValue}
placeholder={placeholder}
loadingMessage={loadingMessage}
noOptionsMessage={noOptionsMessage}
creatable={true}
/>
}
right={
<div className="field">
<AddButton label={buttonLabel} action={this.addButtonClicked} disabled={disabled} />
</div>
}
/>
);
}
addButtonClicked = (event: MouseEvent) => {
event.preventDefault();
this.appendEntry();
}; };
appendEntry = () => { const addButtonClicked = (event: MouseEvent) => {
const { selectedValue } = this.state; event.preventDefault();
if (!selectedValue) { appendEntry();
};
const appendEntry = () => {
if (disabled || !selectedValue) {
return; return;
} }
this.setState( addEntry(selectedValue);
// @ts-ignore setSelectedValue(undefined);
{
...this.state,
// @ts-ignore null is needed to clear the selection; undefined does not work
selectedValue: null
},
() => this.props.addEntry(selectedValue)
);
}; };
handleAddEntryChange = (selection: SelectValue) => { return (
this.setState({ <Level
...this.state, children={
selectedValue: selection <FullWidthAutocomplete
}); label={fieldLabel}
}; loadSuggestions={loadSuggestions}
} valueSelected={handleAddEntryChange}
helpText={helpText}
value={selectedValue}
placeholder={placeholder}
loadingMessage={loadingMessage}
noOptionsMessage={noOptionsMessage}
creatable={true}
/>
}
right={
<div className="field">
<AddButton label={buttonLabel} action={addButtonClicked} disabled={disabled} />
</div>
}
/>
);
};
export default AutocompleteAddEntryToTableField; export default AutocompleteAddEntryToTableField;