mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-14 09:25:43 +01:00
Fix validation in "Add Entry" components (#1625)
This commit is contained in:
2
gradle/changelog/addentrytotablefield_validation.yaml
Normal file
2
gradle/changelog/addentrytotablefield_validation.yaml
Normal 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))
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user