fix form validation after refactoring

This commit is contained in:
Eduard Heimbuch
2020-12-01 11:35:49 +01:00
parent af9f6ab629
commit 6a93a198e8
12 changed files with 134 additions and 66 deletions

View File

@@ -32,6 +32,7 @@ type Props = {
repository: RepositoryUrlImport;
onChange: (repository: RepositoryUrlImport) => void;
setValid: (valid: boolean) => void;
disabled?: boolean;
};
const Column = styled.div`
@@ -42,7 +43,7 @@ const Columns = styled.div`
padding: 0.75rem 0 0;
`;
const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid, disabled }) => {
const [t] = useTranslation("repos");
const [urlValidationError, setUrlValidationError] = useState(false);
@@ -72,6 +73,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
helpText={t("help.importUrlHelpText")}
validationError={urlValidationError}
errorMessage={t("validation.url-invalid")}
disabled={disabled}
/>
</Column>
<Column className="column is-half">
@@ -80,6 +82,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
onChange={username => onChange({ ...repository, username })}
value={repository.username}
helpText={t("help.usernameHelpText")}
disabled={disabled}
/>
</Column>
<Column className="column is-half">
@@ -89,6 +92,7 @@ const ImportFromUrlForm: FC<Props> = ({ repository, onChange, setValid }) => {
value={repository.password}
type="password"
helpText={t("help.passwordHelpText")}
disabled={disabled}
/>
</Column>
</Columns>

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import React, { FC, useState } from "react";
import React, { FC, FormEvent, useState } from "react";
import NamespaceAndNameFields from "./NamespaceAndNameFields";
import { Repository, RepositoryUrlImport } from "@scm-manager/ui-types";
import ImportFromUrlForm from "./ImportFromUrlForm";
@@ -62,7 +62,9 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
setImportPending(loading);
};
const submit = () => {
const submit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const currentPath = history.location.pathname;
handleImportLoading(true);
apiClient
.post(url, repo, "application/vnd.scmm-repository+json;v=2")
@@ -72,7 +74,11 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
return apiClient.get(location);
})
.then(response => response.json())
.then(repo => history.push(`/repo/${repo.namespace}/${repo.name}/code/sources`))
.then(repo => {
if (history.location.pathname === currentPath) {
history.push(`/repo/${repo.namespace}/${repo.name}/code/sources`);
}
})
.catch(error => {
setError(error);
handleImportLoading(false);
@@ -81,22 +87,24 @@ const ImportRepositoryFromUrl: FC<Props> = ({ url, setImportPending }) => {
return (
<form onSubmit={submit}>
<ErrorNotification error={error} />
<ImportFromUrlForm
repository={repo}
onChange={setRepo}
setValid={(importUrl: boolean) => setValid({ ...valid, importUrl })}
disabled={loading}
/>
<ErrorNotification error={error} />
<hr />
<NamespaceAndNameFields
repository={repo}
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
setValid={(namespaceAndName: boolean) => setValid({ ...valid, namespaceAndName })}
disabled={loading}
/>
<RepositoryInformationForm
repository={repo}
onChange={setRepo as React.Dispatch<React.SetStateAction<Repository>>}
disabled={false}
disabled={loading}
setValid={(contact: boolean) => setValid({ ...valid, contact })}
/>
<Level

View File

@@ -30,18 +30,21 @@ type Props = {
repositoryTypes: RepositoryType[];
repositoryType?: RepositoryType;
setRepositoryType: (repositoryType: RepositoryType) => void;
disabled?: boolean;
};
const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType, setRepositoryType }) => {
const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType, setRepositoryType, disabled }) => {
const [t] = useTranslation("repos");
const createSelectOptions = () => {
const options = repositoryTypes.filter(repoType => !!repoType._links.import).map(repositoryType => {
return {
label: repositoryType.displayName,
value: repositoryType.name
};
});
const options = repositoryTypes
.filter(repoType => !!repoType._links.import)
.map(repositoryType => {
return {
label: repositoryType.displayName,
value: repositoryType.name
};
});
options.unshift({ label: "", value: "" });
return options;
};
@@ -52,13 +55,14 @@ const ImportRepositoryTypeSelect: FC<Props> = ({ repositoryTypes, repositoryType
};
return (
<Select
label={t("repository.type")}
onChange={onChangeType}
value={repositoryType ? repositoryType.name : ""}
options={createSelectOptions()}
helpText={t("help.typeHelpText")}
/>
<Select
label={t("repository.type")}
onChange={onChangeType}
value={repositoryType ? repositoryType.name : ""}
options={createSelectOptions()}
helpText={t("help.typeHelpText")}
disabled={disabled}
/>
);
};

View File

@@ -22,17 +22,18 @@
* SOFTWARE.
*/
import React, { FC } from "react";
import { RepositoryType, Link } from "@scm-manager/ui-types";
import { Radio } from "@scm-manager/ui-components";
import { Link, RepositoryType } from "@scm-manager/ui-types";
import { LabelWithHelpIcon, Radio } from "@scm-manager/ui-components";
import { useTranslation } from "react-i18next";
type Props = {
repositoryType: RepositoryType;
importType: string;
setImportType: (type: string) => void;
disabled?: boolean;
};
const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType }) => {
const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType, disabled }) => {
const [t] = useTranslation("repos");
const changeImportType = (checked: boolean, name?: string) => {
@@ -43,6 +44,7 @@ const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType
return (
<>
<LabelWithHelpIcon label={t("import.importTypes.label")} key="import.importTypes.label" />
{(repositoryType._links.import as Link[]).map((type, index) => (
<Radio
name={type.name}
@@ -52,6 +54,7 @@ const ImportTypeSelect: FC<Props> = ({ repositoryType, importType, setImportType
helpText={t(`import.importTypes.${type.name}.helpText`)}
onChange={changeImportType}
key={index}
disabled={disabled}
/>
))}
</>

View File

@@ -36,45 +36,45 @@ type Props = {
onChange: (repository: Repository) => void;
namespaceStrategy: string;
setValid: (valid: boolean) => void;
disabled?: boolean;
};
const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStrategy, setValid }) => {
const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStrategy, setValid, disabled }) => {
const [t] = useTranslation("repos");
const [namespaceValidationError, setNamespaceValidationError] = useState(false);
const [nameValidationError, setNameValidationError] = useState(false);
useEffect(() => {
//TODO fix validation
if (repository.name) {
const valid = validator.isNameValid(repository.name);
setNameValidationError(!valid);
onFieldChange();
const nameValid = validator.isNameValid(repository.name);
setNameValidationError(!nameValid);
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
if (repository.namespace) {
const namespaceValid = validator.isNamespaceValid(repository.namespace);
setValid(!(!namespaceValid || !nameValid));
} else {
setValid(false);
}
} else {
setValid(nameValid);
}
} else {
setValid(false);
}
}, [repository.name]);
}, [repository.name, repository.namespace]);
const handleNamespaceChange = (namespace: string) => {
const valid = validator.isNamespaceValid(namespace);
setNamespaceValidationError(!valid);
onFieldChange(valid);
onChange({ ...repository, namespace });
};
const handleNameChange = (name: string) => {
const valid = validator.isNameValid(name);
setNameValidationError(!valid);
onFieldChange();
onChange({ ...repository, name });
};
//TODO fix validation
const onFieldChange = (namespaceValid?: boolean) => {
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY && !validator.isNamespaceValid(repository.namespace)) {
setValid(false);
} else {
setValid(!nameValidationError && !namespaceValidationError);
}
};
const renderNamespaceField = () => {
const props = {
label: t("repository.namespace"),
@@ -82,7 +82,8 @@ const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStra
value: repository ? repository.namespace : "",
onChange: handleNamespaceChange,
errorMessage: t("validation.namespace-invalid"),
validationError: namespaceValidationError
validationError: namespaceValidationError,
disabled: disabled
};
if (namespaceStrategy === CUSTOM_NAMESPACE_STRATEGY) {
@@ -102,6 +103,7 @@ const NamespaceAndNameFields: FC<Props> = ({ repository, onChange, namespaceStra
validationError={nameValidationError}
errorMessage={t("validation.name-invalid")}
helpText={t("help.nameHelpText")}
disabled={disabled}
/>
</>
);

View File

@@ -78,7 +78,6 @@ const RepositoryForm: FC<Props> = ({
});
const [initRepository, setInitRepository] = useState(false);
const [contextEntries, setContextEntries] = useState({});
//TODO fix validation
const [valid, setValid] = useState({ namespaceAndName: false, contact: true });
const [t] = useTranslation("repos");
@@ -194,7 +193,7 @@ const RepositoryForm: FC<Props> = ({
<RepositoryInformationForm
repository={repo}
onChange={setRepo}
disabled={!createRepository}
disabled={!(isModifiable() || createRepository)}
setValid={contact => setValid({ ...valid, contact })}
/>
{submitButton()}

View File

@@ -25,12 +25,16 @@
import React, { FC } from "react";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import { Button, ButtonAddons, Level } from "@scm-manager/ui-components";
import { Button, ButtonAddons, Icon, Level } from "@scm-manager/ui-components";
type Props = {
creationMode: "CREATE" | "IMPORT";
};
const MarginIcon = styled(Icon)`
padding-right: 0.5rem;
`;
const SmallButton = styled(Button)`
border-radius: 4px;
font-size: 1rem;
@@ -38,9 +42,14 @@ const SmallButton = styled(Button)`
`;
const TopLevel = styled(Level)`
margin-top: -2.75rem;
margin-bottom: 2.75rem !important; //TODO Try to remove important
margin-top: 1.5rem;
margin-bottom: -1.5rem !important;
height: 0;
position: absolute;
right: 0;
@media (max-width: 785px) {
margin-top: 4.5rem;
}
`;
const RepositoryFormSwitcher: FC<Props> = ({ creationMode }) => {
@@ -59,17 +68,20 @@ const RepositoryFormSwitcher: FC<Props> = ({ creationMode }) => {
right={
<ButtonAddons>
<SmallButton
label={t("repositoryForm.createButton")}
icon="fa fa-plus"
color={isCreateMode() ? "link is-selected" : undefined}
link={isImportMode() ? "/repos/create" : undefined}
/>
>
<MarginIcon name="fa fa-plus" color={isCreateMode() ? "white" : "default"} />{" "}
<p className="is-hidden-mobile is-hidden-tablet-only">{t("repositoryForm.createButton")}</p>
</SmallButton>
<SmallButton
label={t("repositoryForm.importButton")}
icon="fa fa-file-upload"
color={isImportMode() ? "link is-selected" : undefined}
link={isCreateMode() ? "/repos/import" : undefined}
/>
className="has-text-left-desktop"
>
<MarginIcon name="fa fa-file-upload" color={isImportMode() ? "white" : "default"} />
<p className="is-hidden-mobile is-hidden-tablet-only">{t("repositoryForm.importButton")}</p>
</SmallButton>
</ButtonAddons>
}
/>