mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-03 12:05:52 +01:00
Restructure global configuration forms for each repository type
Squash commits of branch feature/repository-type-configuration: - Refactor hg configuration form - Add support of description text for checkbox and input fields - Refactor git configuration form - Refactor svn configuration form - Add aria-describedby for checkbox and input fields - Change hgBinary can also be null - Fix naming of test - Fix spelling - Change logic of successfull notification to only be shown if the config rest api returns without an error Reviewed-by: Philipp Ahrendt <philipp.ahrendt@cloudogu.com>, Till-André Diegeler <till-andre.diegeler@cloudogu.com> Reviewed-by: Philipp Ahrendt <philipp.ahrendt@cloudogu.com>
This commit is contained in:
10
gradle/changelog/repository-type-config-forms.yaml
Normal file
10
gradle/changelog/repository-type-config-forms.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
- type: added
|
||||||
|
description: Support of description text for input fields and checkboxes
|
||||||
|
- type: added
|
||||||
|
description: aria-describedby for input fields and checkboxes
|
||||||
|
- type: changed
|
||||||
|
description: Disabling git repositories is now the first setting within the configuration form
|
||||||
|
- type: changed
|
||||||
|
description: Disabling hg repositories is now the first setting within the configuration form
|
||||||
|
- type: changed
|
||||||
|
description: Disabling svn repositories is now the first setting within the configuration form
|
||||||
@@ -14,13 +14,11 @@
|
|||||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
* along with this program. If not, see https://www.gnu.org/licenses/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { FC, useEffect } from "react";
|
import React, { FC } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useConfigLink } from "@scm-manager/ui-api";
|
import { ConfigurationForm, Form, Title, useDocumentTitle } from "@scm-manager/ui-core";
|
||||||
import { ConfigurationForm, InputField, Checkbox, validation } from "@scm-manager/ui-components";
|
|
||||||
import { Title, useDocumentTitle } from "@scm-manager/ui-core";
|
|
||||||
import { HalRepresentation } from "@scm-manager/ui-types";
|
import { HalRepresentation } from "@scm-manager/ui-types";
|
||||||
|
import { validation } from "@scm-manager/ui-components";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: string;
|
link: string;
|
||||||
@@ -40,62 +38,41 @@ const GitGlobalConfiguration: FC<Props> = ({ link }) => {
|
|||||||
const [t] = useTranslation("plugins");
|
const [t] = useTranslation("plugins");
|
||||||
useDocumentTitle(t("scm-git-plugin.config.title"));
|
useDocumentTitle(t("scm-git-plugin.config.title"));
|
||||||
|
|
||||||
const { initialConfiguration, isReadOnly, update, ...formProps } = useConfigLink<Configuration>(link);
|
const validateLfsWriteAuthorization = (value: string) => {
|
||||||
const { formState, handleSubmit, register, reset } = useForm<Configuration>({ mode: "onChange" });
|
const authorizationTime = parseInt(value);
|
||||||
|
return Number.isInteger(authorizationTime) && authorizationTime > 0;
|
||||||
useEffect(() => {
|
|
||||||
if (initialConfiguration) {
|
|
||||||
reset(initialConfiguration);
|
|
||||||
}
|
|
||||||
}, [initialConfiguration, reset]);
|
|
||||||
|
|
||||||
const isValidDefaultBranch = (value: string) => {
|
|
||||||
return validation.isBranchValid(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConfigurationForm
|
<ConfigurationForm<Configuration> link={link} translationPath={["plugins", "scm-git-plugin.config"]}>
|
||||||
isValid={formState.isValid}
|
{({ watch }) => (
|
||||||
isReadOnly={isReadOnly}
|
<>
|
||||||
onSubmit={handleSubmit(update)}
|
|
||||||
{...formProps}
|
|
||||||
>
|
|
||||||
<Title>{t("scm-git-plugin.config.title")}</Title>
|
<Title>{t("scm-git-plugin.config.title")}</Title>
|
||||||
<InputField
|
<Form.Row>
|
||||||
label={t("scm-git-plugin.config.gcExpression")}
|
<Form.Checkbox name="disabled" readOnly={!watch("allowDisable")} />
|
||||||
helpText={t("scm-git-plugin.config.gcExpressionHelpText")}
|
</Form.Row>
|
||||||
disabled={isReadOnly}
|
{!watch("disabled") ? (
|
||||||
{...register("gcExpression")}
|
<>
|
||||||
/>
|
<Form.Row>
|
||||||
<Checkbox
|
<Form.Input name="gcExpression" />
|
||||||
label={t("scm-git-plugin.config.nonFastForwardDisallowed")}
|
</Form.Row>
|
||||||
helpText={t("scm-git-plugin.config.nonFastForwardDisallowedHelpText")}
|
<Form.Row>
|
||||||
disabled={isReadOnly}
|
<Form.Checkbox name="nonFastForwardDisallowed" />
|
||||||
{...register("nonFastForwardDisallowed")}
|
</Form.Row>
|
||||||
/>
|
<Form.Row>
|
||||||
<InputField
|
<Form.Input name="defaultBranch" rules={{ required: true, validate: validation.isBranchValid }} />
|
||||||
label={t("scm-git-plugin.config.defaultBranch")}
|
</Form.Row>
|
||||||
helpText={t("scm-git-plugin.config.defaultBranchHelpText")}
|
<Form.Row>
|
||||||
disabled={isReadOnly}
|
<Form.Input
|
||||||
validationError={!!formState.errors.defaultBranch}
|
name="lfsWriteAuthorizationExpirationInMinutes"
|
||||||
errorMessage={t("scm-git-plugin.config.defaultBranchValidationError")}
|
|
||||||
{...register("defaultBranch", { validate: isValidDefaultBranch })}
|
|
||||||
/>
|
|
||||||
<InputField
|
|
||||||
type="number"
|
type="number"
|
||||||
label={t("scm-git-plugin.config.lfsWriteAuthorizationExpirationInMinutes")}
|
rules={{ required: true, validate: validateLfsWriteAuthorization }}
|
||||||
helpText={t("scm-git-plugin.config.lfsWriteAuthorizationExpirationInMinutesHelpText")}
|
|
||||||
disabled={isReadOnly}
|
|
||||||
validationError={!!formState.errors.lfsWriteAuthorizationExpirationInMinutes}
|
|
||||||
errorMessage={t("scm-git-plugin.config.lfsWriteAuthorizationExpirationInMinutesValidationError")}
|
|
||||||
{...register("lfsWriteAuthorizationExpirationInMinutes", { min: 1, required: true })}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label={t("scm-git-plugin.config.disabled")}
|
|
||||||
helpText={t("scm-git-plugin.config.disabledHelpText")}
|
|
||||||
disabled={isReadOnly || !initialConfiguration?.allowDisable}
|
|
||||||
{...register("disabled")}
|
|
||||||
/>
|
/>
|
||||||
|
</Form.Row>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ConfigurationForm>
|
</ConfigurationForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,18 +21,24 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Git",
|
"link": "Git",
|
||||||
"title": "Git Konfiguration",
|
"title": "Git Konfiguration",
|
||||||
"gcExpression": "GC Cron Ausdruck",
|
"gcExpression": {
|
||||||
"gcExpressionHelpText": "Benutze Quartz Cron Ausdrücke (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK), um git GC regelmäßig auszuführen.",
|
"label": "GC Cron Ausdruck",
|
||||||
"nonFastForwardDisallowed": "Deaktiviere \"Non Fast-Forward\"",
|
"descriptionText": "Benutze Quartz Cron Ausdrücke (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK), um git GC regelmäßig auszuführen."
|
||||||
"nonFastForwardDisallowedHelpText": "Git Pushes ablehnen, die nicht \"fast-forward\" sind, wie \"--force\".",
|
},
|
||||||
"defaultBranch": "Default Branch",
|
"nonFastForwardDisallowed": {
|
||||||
"defaultBranchHelpText": "Dieser Name wird bei der Initialisierung neuer Git Repositories genutzt. Er hat keine weiteren Auswirkungen (insbesondere hat er keinen Einfluss auf den Branchnamen bei leeren Repositories).",
|
"label": "Deaktiviere \"Non Fast-Forward\"",
|
||||||
"defaultBranchValidationError": "Dies ist kein valider Branchname",
|
"descriptionText": "Git Pushes ablehnen, die nicht \"fast-forward\" sind, wie \"--force\"."
|
||||||
"lfsWriteAuthorizationExpirationInMinutes": "Ablaufzeit für LFS Autorisierung",
|
},
|
||||||
"lfsWriteAuthorizationExpirationInMinutesHelpText": "Ablaufzeit für den Autorisierungstoken in Minuten, der für LFS Speicheranfragen ausgestellt wird. Wenn der SCM-Manager hinter einem Reverse-Proxy mit Zwischenspeicherung (z. B. Nginx) betrieben wird, sollte dieser Wert auf die Zeit gesetzt werden, die ein LFS-Upload maximal benötigen kann.",
|
"defaultBranch": {
|
||||||
"lfsWriteAuthorizationExpirationInMinutesValidationError": "Has to be at least 1 minute",
|
"label": "Default Branch für neu initialisierte Repositorys"
|
||||||
"disabled": "Deaktiviert",
|
},
|
||||||
"disabledHelpText": "Aktiviere oder deaktiviere das Git Plugin. Nur erlaubt, wenn keine Git Repositories existieren.",
|
"lfsWriteAuthorizationExpirationInMinutes": {
|
||||||
|
"label": "Ablaufzeit für LFS Autorisierung",
|
||||||
|
"descriptionText": "Ablaufzeit für den Autorisierungstoken in Minuten, der für LFS Speicheranfragen ausgestellt wird. Wenn der SCM-Manager hinter einem Reverse-Proxy mit Zwischenspeicherung (z. B. Nginx) betrieben wird, sollte dieser Wert auf die Zeit gesetzt werden, die ein LFS-Upload maximal benötigen kann."
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"label": "Deaktiviere das Git Plugin. Nur erlaubt, wenn keine Git Repositories existieren."
|
||||||
|
},
|
||||||
"submit": "Speichern"
|
"submit": "Speichern"
|
||||||
},
|
},
|
||||||
"repoConfig": {
|
"repoConfig": {
|
||||||
|
|||||||
@@ -21,18 +21,24 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Git",
|
"link": "Git",
|
||||||
"title": "Git Configuration",
|
"title": "Git Configuration",
|
||||||
"gcExpression": "GC Cron Expression",
|
"gcExpression": {
|
||||||
"gcExpressionHelpText": "Use Quartz Cron Expressions (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK) to run git gc in intervals.",
|
"label": "GC Cron Expression",
|
||||||
"nonFastForwardDisallowed": "Disallow Non Fast-Forward",
|
"descriptionText": "Use Quartz Cron Expressions (SECOND MINUTE HOUR DAYOFMONTH MONTH DAYOFWEEK) to run git gc in intervals."
|
||||||
"nonFastForwardDisallowedHelpText": "Reject git pushes which are non fast-forward such as --force.",
|
},
|
||||||
"defaultBranch": "Default Branch",
|
"nonFastForwardDisallowed": {
|
||||||
"defaultBranchHelpText": "This name will be used for the initialization of new git repositories. It has no effect otherwise (especially this cannot change the initial branch name for empty repositories).",
|
"label": "Disallow Non Fast-Forward",
|
||||||
"defaultBranchValidationError": "This is not a valid branch name",
|
"descriptionText": "Reject git pushes which are non fast-forward such as --force."
|
||||||
"lfsWriteAuthorizationExpirationInMinutes": "LFS authorization expiration",
|
},
|
||||||
"lfsWriteAuthorizationExpirationInMinutesHelpText": "Expiration time of the authorization token generated for LFS put requests in minutes. If SCM-Manager is run behind a reverse proxy that buffers http requests (eg. Nginx), this should set up to the time, an LFS upload may take at maximum.",
|
"defaultBranch": {
|
||||||
"lfsWriteAuthorizationExpirationInMinutesValidationError": "Has to be at least 1 minute",
|
"label": "Default branch for newly initialized repositories"
|
||||||
"disabled": "Disabled",
|
},
|
||||||
"disabledHelpText": "Enable or disable the Git plugin. Only allowed if no Git Repositories exist.",
|
"lfsWriteAuthorizationExpirationInMinutes": {
|
||||||
|
"label": "LFS authorization expiration",
|
||||||
|
"descriptionText": "Expiration time of the authorization token generated for LFS put requests in minutes. If SCM-Manager is run behind a reverse proxy that buffers http requests (eg. Nginx), this should set up to the time, an LFS upload may take at maximum."
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"label": "Disable the Git plugin. Only allowed if no Git Repositories exist."
|
||||||
|
},
|
||||||
"submit": "Submit"
|
"submit": "Submit"
|
||||||
},
|
},
|
||||||
"repoConfig": {
|
"repoConfig": {
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
"typecheck": "tsc"
|
"typecheck": "tsc"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scm-manager/ui-plugins": "3.7.2-SNAPSHOT"
|
"@scm-manager/ui-plugins": "3.7.2-SNAPSHOT",
|
||||||
|
"react-query": "^3.25.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@scm-manager/babel-preset": "^2.13.1",
|
"@scm-manager/babel-preset": "^2.13.1",
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ public class HgConfigResource {
|
|||||||
HgGlobalConfig config = dtoToConfigMapper.map(configDto);
|
HgGlobalConfig config = dtoToConfigMapper.map(configDto);
|
||||||
ConfigurationPermissions.write(config).check();
|
ConfigurationPermissions.write(config).check();
|
||||||
|
|
||||||
if (config.getHgBinary() != null) {
|
if (!config.isDisabled() && config.getHgBinary() != null) {
|
||||||
HgVerifier.HgVerifyStatus verifyStatus = new HgVerifier().verify(config.getHgBinary());
|
HgVerifier.HgVerifyStatus verifyStatus = new HgVerifier().verify(config.getHgBinary());
|
||||||
doThrow()
|
doThrow()
|
||||||
.violation(verifyStatus.getDescription())
|
.violation(verifyStatus.getDescription())
|
||||||
|
|||||||
@@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 - present Cloudogu GmbH
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify it under
|
|
||||||
* the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
* Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { FC, useEffect, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Link, Links } from "@scm-manager/ui-types";
|
|
||||||
import { apiClient, Button, Checkbox, InputField } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
type Configuration = {
|
|
||||||
disabled: boolean;
|
|
||||||
allowDisable: boolean;
|
|
||||||
hgBinary: string;
|
|
||||||
encoding: string;
|
|
||||||
showRevisionInId: boolean;
|
|
||||||
enableHttpPostArgs: boolean;
|
|
||||||
_links: Links;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
initialConfiguration: Configuration;
|
|
||||||
readOnly: boolean;
|
|
||||||
onConfigurationChange: (p1: Configuration, p2: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const HgConfigurationForm: FC<Props> = ({ initialConfiguration, onConfigurationChange, readOnly }) => {
|
|
||||||
const [validationErrors, setValidationErrors] = useState<string[]>([]);
|
|
||||||
const [configuration, setConfiguration] = useState(initialConfiguration);
|
|
||||||
const [t] = useTranslation("plugins");
|
|
||||||
|
|
||||||
useEffect(() => setConfiguration(initialConfiguration), [initialConfiguration]);
|
|
||||||
useEffect(() => onConfigurationChange(configuration, updateValidationStatus()), [configuration]);
|
|
||||||
|
|
||||||
const updateValidationStatus = () => {
|
|
||||||
const errors = [];
|
|
||||||
if (!configuration.hgBinary) {
|
|
||||||
errors.push("hgBinary");
|
|
||||||
}
|
|
||||||
if (!configuration.encoding) {
|
|
||||||
errors.push("encoding");
|
|
||||||
}
|
|
||||||
|
|
||||||
setValidationErrors(errors);
|
|
||||||
return errors.length === 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const hasValidationError = (name: string) => {
|
|
||||||
return validationErrors.indexOf(name) >= 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const triggerAutoConfigure = () => {
|
|
||||||
apiClient
|
|
||||||
.put(
|
|
||||||
(initialConfiguration._links.autoConfiguration as Link).href,
|
|
||||||
{ ...initialConfiguration, hgBinary: configuration.hgBinary },
|
|
||||||
"application/vnd.scmm-hgConfig+json;v=2"
|
|
||||||
)
|
|
||||||
.then(() =>
|
|
||||||
apiClient
|
|
||||||
.get((initialConfiguration._links.self as Link).href)
|
|
||||||
.then(r => r.json())
|
|
||||||
.then((config: Configuration) => setConfiguration({ ...configuration, hgBinary: config.hgBinary }))
|
|
||||||
)
|
|
||||||
.then(() => onConfigurationChange(configuration, updateValidationStatus()));
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="is-flex is-flex-direction-column">
|
|
||||||
<InputField
|
|
||||||
name="hgBinary"
|
|
||||||
label={t("scm-hg-plugin.config.hgBinary")}
|
|
||||||
helpText={t("scm-hg-plugin.config.hgBinaryHelpText")}
|
|
||||||
value={configuration.hgBinary}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, hgBinary: value })}
|
|
||||||
validationError={hasValidationError("hgBinary")}
|
|
||||||
errorMessage={t("scm-hg-plugin.config.required")}
|
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
<InputField
|
|
||||||
name="encoding"
|
|
||||||
label={t("scm-hg-plugin.config.encoding")}
|
|
||||||
helpText={t("scm-hg-plugin.config.encodingHelpText")}
|
|
||||||
value={configuration.encoding}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, encoding: value })}
|
|
||||||
validationError={hasValidationError("encoding")}
|
|
||||||
errorMessage={t("scm-hg-plugin.config.required")}
|
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
name="showRevisionInId"
|
|
||||||
label={t("scm-hg-plugin.config.showRevisionInId")}
|
|
||||||
helpText={t("scm-hg-plugin.config.showRevisionInIdHelpText")}
|
|
||||||
checked={configuration.showRevisionInId}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, showRevisionInId: value })}
|
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
name="enableHttpPostArgs"
|
|
||||||
label={t("scm-hg-plugin.config.enableHttpPostArgs")}
|
|
||||||
helpText={t("scm-hg-plugin.config.enableHttpPostArgsHelpText")}
|
|
||||||
checked={configuration.enableHttpPostArgs}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, enableHttpPostArgs: value })}
|
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
name="disabled"
|
|
||||||
label={t("scm-hg-plugin.config.disabled")}
|
|
||||||
helpText={t("scm-hg-plugin.config.disabledHelpText")}
|
|
||||||
checked={configuration.disabled}
|
|
||||||
onChange={value => {
|
|
||||||
setConfiguration({ ...configuration, disabled: value });
|
|
||||||
}}
|
|
||||||
disabled={readOnly || !configuration.allowDisable}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="is-align-self-flex-end"
|
|
||||||
disabled={!initialConfiguration?._links?.autoConfiguration}
|
|
||||||
action={() => triggerAutoConfigure()}
|
|
||||||
>
|
|
||||||
{t("scm-hg-plugin.config.autoConfigure")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default HgConfigurationForm;
|
|
||||||
@@ -16,23 +16,55 @@
|
|||||||
|
|
||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Configuration } from "@scm-manager/ui-components";
|
import { Button, ConfigurationForm, Form, Title, useDocumentTitle } from "@scm-manager/ui-core";
|
||||||
import { Title, useDocumentTitle } from "@scm-manager/ui-core";
|
import { SmallLoadingSpinner, validation } from "@scm-manager/ui-components";
|
||||||
import HgConfigurationForm from "./HgConfigurationForm";
|
import { HgGlobalConfigurationDto, useHgAutoConfiguration } from "./hooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: string;
|
link: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const HgGlobalConfiguration: FC<Props> = ({ link }) => {
|
const HgGlobalConfiguration: FC<Props> = ({ link }) => {
|
||||||
|
const { mutate: triggerAutoConfiguration, isLoading: isAutoConfigLoading } = useHgAutoConfiguration(link);
|
||||||
const [t] = useTranslation("plugins");
|
const [t] = useTranslation("plugins");
|
||||||
useDocumentTitle(t("scm-hg-plugin.config.title"));
|
useDocumentTitle(t("scm-hg-plugin.config.title"));
|
||||||
|
|
||||||
|
const isHgBinaryValid = (hgBinaryPath: string | undefined | null) => {
|
||||||
|
return !hgBinaryPath || validation.isPathValid(hgBinaryPath);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<ConfigurationForm<HgGlobalConfigurationDto> link={link} translationPath={["plugins", "scm-hg-plugin.config"]}>
|
||||||
|
{({ watch, getValues }) => (
|
||||||
|
<>
|
||||||
<Title>{t("scm-hg-plugin.config.title")}</Title>
|
<Title>{t("scm-hg-plugin.config.title")}</Title>
|
||||||
<Configuration link={link} render={(props: any) => <HgConfigurationForm {...props} />} />
|
<Form.Row>
|
||||||
</div>
|
<Form.Checkbox name="disabled" readOnly={!watch("allowDisable")} />
|
||||||
|
</Form.Row>
|
||||||
|
{!watch("disabled") ? (
|
||||||
|
<>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Input name="hgBinary" rules={{ validate: isHgBinaryValid }} />
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Input name="encoding" />
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Checkbox name="showRevisionInId" />
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Checkbox name="enableHttpPostArgs" />
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row className="is-justify-content-flex-end">
|
||||||
|
<Button className="mr-2" onClick={() => triggerAutoConfiguration(getValues())}>
|
||||||
|
{isAutoConfigLoading ? <SmallLoadingSpinner /> : t("scm-hg-plugin.config.autoConfigure")}
|
||||||
|
</Button>
|
||||||
|
</Form.Row>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ConfigurationForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ const HgRepositoryConfigurationForm: FC<Props> = ({ repository }) => {
|
|||||||
{updated ? <Notification type="info">{t("scm-hg-plugin.config.success")}</Notification> : null}
|
{updated ? <Notification type="info">{t("scm-hg-plugin.config.success")}</Notification> : null}
|
||||||
<InputField
|
<InputField
|
||||||
name="encoding"
|
name="encoding"
|
||||||
label={t("scm-hg-plugin.config.encoding")}
|
label={t("scm-hg-plugin.config.encoding.label")}
|
||||||
helpText={t("scm-hg-plugin.config.encodingHelpText")}
|
|
||||||
value={configuration.encoding || ""}
|
value={configuration.encoding || ""}
|
||||||
onChange={encodingChanged}
|
onChange={encodingChanged}
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
|
|||||||
@@ -17,11 +17,41 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { apiClient } from "@scm-manager/ui-components";
|
import { apiClient } from "@scm-manager/ui-components";
|
||||||
import { HalRepresentation, Link, Repository } from "@scm-manager/ui-types";
|
import { HalRepresentation, Link, Repository } from "@scm-manager/ui-types";
|
||||||
|
import { useMutation, useQueryClient } from "react-query";
|
||||||
|
|
||||||
export type HgRepositoryConfiguration = HalRepresentation & {
|
export type HgRepositoryConfiguration = HalRepresentation & {
|
||||||
encoding?: string;
|
encoding?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type HgGlobalConfigurationDto = HalRepresentation & {
|
||||||
|
disabled: boolean;
|
||||||
|
allowDisable: boolean;
|
||||||
|
encoding: string;
|
||||||
|
hgBinary?: string | null;
|
||||||
|
showRevisionInId: boolean;
|
||||||
|
enableHttpPostArgs: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useHgAutoConfiguration = (configLink: string) => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
const { mutate, isLoading } = useMutation<unknown, Error, HgGlobalConfigurationDto>(
|
||||||
|
(config) => apiClient.put(requiredLink(config, "autoConfiguration"), config),
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
return queryClient.invalidateQueries(["configLink", configLink]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return { mutate, isLoading };
|
||||||
|
};
|
||||||
|
|
||||||
|
const requiredLink = (halObject: HalRepresentation, linkName: string): string => {
|
||||||
|
if (!halObject._links[linkName]) {
|
||||||
|
throw new Error("Could not find link: " + linkName);
|
||||||
|
}
|
||||||
|
return (halObject._links[linkName] as Link).href;
|
||||||
|
};
|
||||||
|
|
||||||
export const useHgRepositoryConfiguration = (repository: Repository) => {
|
export const useHgRepositoryConfiguration = (repository: Repository) => {
|
||||||
const [isLoading, setLoading] = useState(false);
|
const [isLoading, setLoading] = useState(false);
|
||||||
const [data, setData] = useState<HgRepositoryConfiguration | null>(null);
|
const [data, setData] = useState<HgRepositoryConfiguration | null>(null);
|
||||||
|
|||||||
@@ -11,17 +11,22 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Mercurial",
|
"link": "Mercurial",
|
||||||
"title": "Mercurial Konfiguration",
|
"title": "Mercurial Konfiguration",
|
||||||
"hgBinary": "HG Binary",
|
"disabled": {
|
||||||
"hgBinaryHelpText": "Pfad des Mercurial Binary",
|
"label": "Deaktiviert das Mercurial Plugin. Nur erlaubt, wenn keine Mercurial Repositories existieren."
|
||||||
"encoding": "Encoding",
|
},
|
||||||
"encodingHelpText": "Repository Encoding",
|
"hgBinary": {
|
||||||
"showRevisionInId": "Revision anzeigen",
|
"label": "Pfad des Mercurial Binary"
|
||||||
"showRevisionInIdHelpText": "Die Revision als Teil der Node ID anzeigen",
|
},
|
||||||
"enableHttpPostArgs": "HttpPostArgs Protocol aktivieren",
|
"encoding": {
|
||||||
"enableHttpPostArgsHelpText": "Aktiviert das experimentelle HttpPostArgs Protokoll von Mercurial. Das HttpPostArgs Protokoll verwendet den Post Request Body anstatt des HTTP Headers um Meta Informationen zu versenden. Dieses Vorgehen reduziert die Header Größe der Mercurial Requests. HttpPostArgs wird seit Mercurial 3.8 unterstützt.",
|
"label": "Repository Encoding"
|
||||||
"disabled": "Deaktiviert",
|
},
|
||||||
"disabledHelpText": "Aktiviert oder deaktiviert das Mercurial Plugin. Nur erlaubt, wenn keine Mercurial Repositories existieren.",
|
"showRevisionInId": {
|
||||||
"required": "Dieser Konfigurationswert wird benötigt",
|
"label": "Die Revision als Teil der Node ID anzeigen"
|
||||||
|
},
|
||||||
|
"enableHttpPostArgs": {
|
||||||
|
"label": "HttpPostArgs Protocol aktivieren",
|
||||||
|
"descriptionText": "Aktiviert das experimentelle HttpPostArgs Protokoll von Mercurial. Das HttpPostArgs Protokoll verwendet den Post Request Body anstatt des HTTP Headers um Meta Informationen zu versenden. Dieses Vorgehen reduziert die Header Größe der Mercurial Requests. HttpPostArgs wird seit Mercurial 3.8 unterstützt."
|
||||||
|
},
|
||||||
"submit": "Speichern",
|
"submit": "Speichern",
|
||||||
"success": "Einstellungen wurden erfolgreich geändert",
|
"success": "Einstellungen wurden erfolgreich geändert",
|
||||||
"autoConfigure": "Mercurial automatisch konfigurieren"
|
"autoConfigure": "Mercurial automatisch konfigurieren"
|
||||||
|
|||||||
@@ -11,17 +11,22 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Mercurial",
|
"link": "Mercurial",
|
||||||
"title": "Mercurial Configuration",
|
"title": "Mercurial Configuration",
|
||||||
"hgBinary": "HG Binary",
|
"disabled": {
|
||||||
"hgBinaryHelpText": "Location of Mercurial binary",
|
"label": "Disable the Mercurial plugin. Only allowed if no Mercurial repositories exist."
|
||||||
"encoding": "Encoding",
|
},
|
||||||
"encodingHelpText": "Repository Encoding",
|
"hgBinary": {
|
||||||
"showRevisionInId": "Show Revision",
|
"label": "Path to the mercurial binary"
|
||||||
"showRevisionInIdHelpText": "Show revision as part of the node id",
|
},
|
||||||
"enableHttpPostArgs": "Enable HttpPostArgs Protocol",
|
"encoding": {
|
||||||
"enableHttpPostArgsHelpText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8.",
|
"label": "Repository encoding"
|
||||||
"disabled": "Disabled",
|
},
|
||||||
"disabledHelpText": "Enable or disable the Mercurial plugin. Only allowed if no Mercurial repositories exist.",
|
"showRevisionInId": {
|
||||||
"required": "This configuration value is required",
|
"label": "Show revision as part of the node id"
|
||||||
|
},
|
||||||
|
"enableHttpPostArgs": {
|
||||||
|
"label": "Enable HttpPostArgs Protocol",
|
||||||
|
"descriptionText": "Enables the experimental HttpPostArgs Protocol of mercurial. The HttpPostArgs Protocol uses the body of post requests to send the meta information instead of http headers. This helps to reduce the header size of mercurial requests. HttpPostArgs is supported since mercurial 3.8."
|
||||||
|
},
|
||||||
"submit": "Submit",
|
"submit": "Submit",
|
||||||
"success": "Configuration changed successfully",
|
"success": "Configuration changed successfully",
|
||||||
"autoConfigure": "Configure Mercurial Automatically"
|
"autoConfigure": "Configure Mercurial Automatically"
|
||||||
|
|||||||
@@ -161,6 +161,13 @@ public class HgConfigResourceTest {
|
|||||||
assertEquals(400, response.getStatus());
|
assertEquals(400, response.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SubjectAware(username = "writeOnly")
|
||||||
|
public void shouldUpdateInvalidBinaryIfConfigIsDisabled() throws URISyntaxException {
|
||||||
|
MockHttpResponse response = put("{\"disabled\": true, \"hgBinary\":\"3.2.1\"}");
|
||||||
|
assertEquals(HttpServletResponse.SC_NO_CONTENT, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SubjectAware(username = "readOnly")
|
@SubjectAware(username = "readOnly")
|
||||||
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException {
|
public void shouldNotUpdateConfigWhenNotAuthorized() throws URISyntaxException, UnsupportedEncodingException {
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2020 - present Cloudogu GmbH
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify it under
|
|
||||||
* the terms of the GNU Affero General Public License as published by the Free
|
|
||||||
* Software Foundation, version 3.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
||||||
* details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see https://www.gnu.org/licenses/.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, { FC, useEffect, useState } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { Links } from "@scm-manager/ui-types";
|
|
||||||
import { Checkbox, Select } from "@scm-manager/ui-components";
|
|
||||||
|
|
||||||
type Configuration = {
|
|
||||||
disabled: boolean;
|
|
||||||
allowDisable: boolean;
|
|
||||||
compatibility: string;
|
|
||||||
enabledGZip: boolean;
|
|
||||||
_links: Links;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
initialConfiguration: Configuration;
|
|
||||||
readOnly: boolean;
|
|
||||||
onConfigurationChange: (p1: Configuration, p2: boolean) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
const SvnConfigurationForm: FC<Props> = ({ initialConfiguration, readOnly, onConfigurationChange }) => {
|
|
||||||
const [t] = useTranslation("plugins");
|
|
||||||
const [configuration, setConfiguration] = useState(initialConfiguration);
|
|
||||||
|
|
||||||
useEffect(() => setConfiguration(initialConfiguration), [initialConfiguration]);
|
|
||||||
useEffect(() => onConfigurationChange(configuration, true), [configuration]);
|
|
||||||
|
|
||||||
const options = ["NONE", "PRE14", "PRE15", "PRE16", "PRE17", "WITH17"].map((option: string) => ({
|
|
||||||
value: option,
|
|
||||||
label: t("scm-svn-plugin.config.compatibility-values." + option.toLowerCase())
|
|
||||||
}));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Select
|
|
||||||
name="compatibility"
|
|
||||||
label={t("scm-svn-plugin.config.compatibility")}
|
|
||||||
helpText={t("scm-svn-plugin.config.compatibilityHelpText")}
|
|
||||||
value={configuration.compatibility}
|
|
||||||
options={options}
|
|
||||||
onChange={option => setConfiguration({ ...configuration, compatibility: option })}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
name="enabledGZip"
|
|
||||||
label={t("scm-svn-plugin.config.enabledGZip")}
|
|
||||||
helpText={t("scm-svn-plugin.config.enabledGZipHelpText")}
|
|
||||||
checked={configuration.enabledGZip}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, enabledGZip: value })}
|
|
||||||
disabled={readOnly}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
name="disabled"
|
|
||||||
label={t("scm-svn-plugin.config.disabled")}
|
|
||||||
helpText={t("scm-svn-plugin.config.disabledHelpText")}
|
|
||||||
checked={configuration.disabled}
|
|
||||||
onChange={value => setConfiguration({ ...configuration, disabled: value })}
|
|
||||||
disabled={readOnly || !configuration.allowDisable}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default SvnConfigurationForm;
|
|
||||||
@@ -17,22 +17,51 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Configuration } from "@scm-manager/ui-components";
|
import { Configuration } from "@scm-manager/ui-components";
|
||||||
import { Title, useDocumentTitle } from "@scm-manager/ui-core";
|
import { ConfigurationForm, Title, useDocumentTitle, Form } from "@scm-manager/ui-core";
|
||||||
import SvnConfigurationForm from "./SvnConfigurationForm";
|
import { HalRepresentation } from "@scm-manager/ui-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
link: string;
|
link: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Configuration = {
|
||||||
|
disabled: boolean;
|
||||||
|
allowDisable: boolean;
|
||||||
|
compatibility: string;
|
||||||
|
enabledGZip: boolean;
|
||||||
|
} & HalRepresentation;
|
||||||
|
|
||||||
const SvnGlobalConfiguration: FC<Props> = ({ link }) => {
|
const SvnGlobalConfiguration: FC<Props> = ({ link }) => {
|
||||||
const [t] = useTranslation("plugins");
|
const [t] = useTranslation("plugins");
|
||||||
useDocumentTitle(t("scm-svn-plugin.config.title"));
|
useDocumentTitle(t("scm-svn-plugin.config.title"));
|
||||||
|
|
||||||
|
const compatibilityOptions = ["NONE", "PRE14", "PRE15", "PRE16", "PRE17", "WITH17"].map((option: string) => (
|
||||||
|
<option value={option} key={`compatibility-${option}`}>
|
||||||
|
{t("scm-svn-plugin.config.compatibility-values." + option.toLowerCase())}
|
||||||
|
</option>
|
||||||
|
));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<ConfigurationForm<Configuration> link={link} translationPath={["plugins", "scm-svn-plugin.config"]}>
|
||||||
|
{({ watch }) => (
|
||||||
|
<>
|
||||||
<Title>{t("scm-svn-plugin.config.title")}</Title>
|
<Title>{t("scm-svn-plugin.config.title")}</Title>
|
||||||
<Configuration link={link} render={(props: any) => <SvnConfigurationForm {...props} />} />
|
<Form.Row>
|
||||||
</div>
|
<Form.Checkbox name="disabled" readOnly={!watch("allowDisable")} />
|
||||||
|
</Form.Row>
|
||||||
|
{!watch("disabled") ? (
|
||||||
|
<>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Select name="compatibility">{compatibilityOptions}</Form.Select>
|
||||||
|
</Form.Row>
|
||||||
|
<Form.Row>
|
||||||
|
<Form.Checkbox name="enabledGZip" />
|
||||||
|
</Form.Row>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</ConfigurationForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Subversion",
|
"link": "Subversion",
|
||||||
"title": "Subversion Konfiguration",
|
"title": "Subversion Konfiguration",
|
||||||
"compatibility": "Version Kompatibilität",
|
"compatibility": {
|
||||||
"compatibilityHelpText": "Gibt an, mit welcher Subversion Version die Repositories kompatibel sind.",
|
"label": "Versionskompatibilität für jedes Subversion Repository"
|
||||||
|
},
|
||||||
"compatibility-values": {
|
"compatibility-values": {
|
||||||
"none": "Keine Kompatibilität",
|
"none": "Keine Kompatibilität",
|
||||||
"pre14": "Vor 1.4 kompatibel",
|
"pre14": "Vor 1.4 kompatibel",
|
||||||
@@ -16,11 +17,12 @@
|
|||||||
"pre17": "Vor 1.7 kompatibel",
|
"pre17": "Vor 1.7 kompatibel",
|
||||||
"with17": "Mit 1.7 kompatibel"
|
"with17": "Mit 1.7 kompatibel"
|
||||||
},
|
},
|
||||||
"enabledGZip": "GZip Compression aktivieren",
|
"enabledGZip": {
|
||||||
"enabledGZipHelpText": "Aktiviert GZip Kompression für SVN Responses",
|
"label": "Aktiviert GZip Kompression für Subversion Responses"
|
||||||
"disabled": "Deaktiviert",
|
},
|
||||||
"disabledHelpText": "Aktiviert oder deaktiviert das SVN Plugin. Nur erlaubt, wenn keine Subversion Repositories existieren.",
|
"disabled": {
|
||||||
"required": "Dieser Konfigurationswert wird benötigt"
|
"label": "Deaktiviert das SVN Plugin. Nur erlaubt, wenn keine Subversion Repositories existieren."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"link": "Subversion",
|
"link": "Subversion",
|
||||||
"title": "Subversion Configuration",
|
"title": "Subversion Configuration",
|
||||||
"compatibility": "Version Compatibility",
|
"compatibility": {
|
||||||
"compatibilityHelpText": "Specifies with which Subversion version repositories are compatible.",
|
"label": "Version compatibility for each Subversion repository"
|
||||||
|
},
|
||||||
"compatibility-values": {
|
"compatibility-values": {
|
||||||
"none": "No compatibility",
|
"none": "No compatibility",
|
||||||
"pre14": "Pre 1.4 Compatible",
|
"pre14": "Pre 1.4 Compatible",
|
||||||
@@ -16,11 +17,12 @@
|
|||||||
"pre17": "Pre 1.7 Compatible",
|
"pre17": "Pre 1.7 Compatible",
|
||||||
"with17": "With 1.7 Compatible"
|
"with17": "With 1.7 Compatible"
|
||||||
},
|
},
|
||||||
"enabledGZip": "Enable GZip Compression",
|
"enabledGZip": {
|
||||||
"enabledGZipHelpText": "Enable GZip compression for SVN responses.",
|
"label": "Enable GZip Compression for Subversion responses"
|
||||||
"disabled": "Disabled",
|
},
|
||||||
"disabledHelpText": "Enable or disable the SVN plugin. Only allowed if no Subversion repositories exist.",
|
"disabled": {
|
||||||
"required": "This configuration value is required"
|
"label": "Disable the SVN plugin. Only allowed if no Subversion repositories exist."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
|
|||||||
@@ -135,10 +135,10 @@ function Form<FormType extends Record<string, unknown>, DefaultValues extends Fo
|
|||||||
|
|
||||||
// See https://react-hook-form.com/api/useform/reset/
|
// See https://react-hook-form.com/api/useform/reset/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isSubmitSuccessful) {
|
if (isSubmitSuccessful && !error) {
|
||||||
setShowSuccessNotification(true);
|
setShowSuccessNotification(true);
|
||||||
}
|
}
|
||||||
}, [isSubmitSuccessful]);
|
}, [isSubmitSuccessful, error]);
|
||||||
|
|
||||||
useEffect(() => reset(defaultValues as never), [defaultValues, reset]);
|
useEffect(() => reset(defaultValues as never), [defaultValues, reset]);
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const StyledLabel = styled.label`
|
|||||||
type InputFieldProps = {
|
type InputFieldProps = {
|
||||||
label: string;
|
label: string;
|
||||||
helpText?: string;
|
helpText?: string;
|
||||||
|
descriptionText?: string;
|
||||||
testId?: string;
|
testId?: string;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
} & Omit<InputHTMLAttributes<HTMLInputElement>, "type">;
|
} & Omit<InputHTMLAttributes<HTMLInputElement>, "type">;
|
||||||
@@ -45,6 +46,7 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
{
|
{
|
||||||
readOnly,
|
readOnly,
|
||||||
label,
|
label,
|
||||||
|
descriptionText,
|
||||||
className,
|
className,
|
||||||
labelClassName,
|
labelClassName,
|
||||||
value,
|
value,
|
||||||
@@ -57,7 +59,11 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
...props
|
...props
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => (
|
) => {
|
||||||
|
const descriptionId = descriptionText ? `checkbox-description-${name}` : undefined;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{descriptionText ? <p id={descriptionId}>{descriptionText}</p> : null}
|
||||||
<StyledLabel
|
<StyledLabel
|
||||||
className={classNames("checkbox is-align-items-center", labelClassName)}
|
className={classNames("checkbox is-align-items-center", labelClassName)}
|
||||||
// @ts-ignore bulma uses the disabled attribute on labels, although it is not part of the html spec
|
// @ts-ignore bulma uses the disabled attribute on labels, although it is not part of the html spec
|
||||||
@@ -72,6 +78,7 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
defaultChecked={defaultChecked}
|
defaultChecked={defaultChecked}
|
||||||
|
aria-describedby={descriptionId}
|
||||||
readOnly
|
readOnly
|
||||||
/>
|
/>
|
||||||
<StyledInput
|
<StyledInput
|
||||||
@@ -82,6 +89,7 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
defaultChecked={defaultChecked}
|
defaultChecked={defaultChecked}
|
||||||
|
aria-describedby={descriptionId}
|
||||||
{...props}
|
{...props}
|
||||||
{...createAttributesForTesting(testId)}
|
{...createAttributesForTesting(testId)}
|
||||||
disabled
|
disabled
|
||||||
@@ -97,6 +105,7 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
defaultChecked={defaultChecked}
|
defaultChecked={defaultChecked}
|
||||||
|
aria-describedby={descriptionId}
|
||||||
{...props}
|
{...props}
|
||||||
{...createAttributesForTesting(testId)}
|
{...createAttributesForTesting(testId)}
|
||||||
/>
|
/>
|
||||||
@@ -105,6 +114,8 @@ const Checkbox = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
{label}
|
{label}
|
||||||
{helpText ? <Help className="ml-1" text={helpText} /> : null}
|
{helpText ? <Help className="ml-1" text={helpText} /> : null}
|
||||||
</StyledLabel>
|
</StyledLabel>
|
||||||
)
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
export default Checkbox;
|
export default Checkbox;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
helpText,
|
helpText,
|
||||||
|
descriptionText,
|
||||||
rules,
|
rules,
|
||||||
testId,
|
testId,
|
||||||
defaultChecked,
|
defaultChecked,
|
||||||
@@ -48,6 +49,8 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
const prefixedNameWithoutIndices = prefixWithoutIndices(nameWithPrefix);
|
const prefixedNameWithoutIndices = prefixWithoutIndices(nameWithPrefix);
|
||||||
const labelTranslation = label || t(`${prefixedNameWithoutIndices}.label`) || "";
|
const labelTranslation = label || t(`${prefixedNameWithoutIndices}.label`) || "";
|
||||||
const helpTextTranslation = helpText || t(`${prefixedNameWithoutIndices}.helpText`);
|
const helpTextTranslation = helpText || t(`${prefixedNameWithoutIndices}.helpText`);
|
||||||
|
const descriptionTextTranslation = descriptionText || t(`${prefixedNameWithoutIndices}.descriptionText`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -64,6 +67,7 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
{...field}
|
{...field}
|
||||||
label={labelTranslation}
|
label={labelTranslation}
|
||||||
helpText={helpTextTranslation}
|
helpText={helpTextTranslation}
|
||||||
|
descriptionText={descriptionTextTranslation}
|
||||||
testId={testId ?? `checkbox-${nameWithPrefix}`}
|
testId={testId ?? `checkbox-${nameWithPrefix}`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
helpText,
|
helpText,
|
||||||
|
descriptionText,
|
||||||
rules,
|
rules,
|
||||||
testId,
|
testId,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
@@ -48,6 +49,8 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
const prefixedNameWithoutIndices = prefixWithoutIndices(nameWithPrefix);
|
const prefixedNameWithoutIndices = prefixWithoutIndices(nameWithPrefix);
|
||||||
const labelTranslation = label || t(`${prefixedNameWithoutIndices}.label`) || "";
|
const labelTranslation = label || t(`${prefixedNameWithoutIndices}.label`) || "";
|
||||||
const helpTextTranslation = helpText || t(`${prefixedNameWithoutIndices}.helpText`);
|
const helpTextTranslation = helpText || t(`${prefixedNameWithoutIndices}.helpText`);
|
||||||
|
const descriptionTextTranslation = descriptionText || t(`${prefixedNameWithoutIndices}.descriptionText`);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@@ -64,6 +67,7 @@ function ControlledInputField<T extends Record<string, unknown>>({
|
|||||||
form={formId}
|
form={formId}
|
||||||
label={labelTranslation}
|
label={labelTranslation}
|
||||||
helpText={helpTextTranslation}
|
helpText={helpTextTranslation}
|
||||||
|
descriptionText={descriptionTextTranslation}
|
||||||
error={
|
error={
|
||||||
fieldState.error
|
fieldState.error
|
||||||
? fieldState.error.message || t(`${prefixedNameWithoutIndices}.error.${fieldState.error.type}`)
|
? fieldState.error.message || t(`${prefixedNameWithoutIndices}.error.${fieldState.error.type}`)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { useAriaId } from "../../helpers";
|
|||||||
type InputFieldProps = {
|
type InputFieldProps = {
|
||||||
label: string;
|
label: string;
|
||||||
helpText?: string;
|
helpText?: string;
|
||||||
|
descriptionText?: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
} & React.ComponentProps<typeof Input>;
|
} & React.ComponentProps<typeof Input>;
|
||||||
|
|
||||||
@@ -33,8 +34,9 @@ type InputFieldProps = {
|
|||||||
* @see https://bulma.io/documentation/form/input/
|
* @see https://bulma.io/documentation/form/input/
|
||||||
*/
|
*/
|
||||||
const InputField = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
const InputField = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
||||||
({ label, helpText, error, className, id, ...props }, ref) => {
|
({ name, label, helpText, descriptionText, error, className, id, ...props }, ref) => {
|
||||||
const inputId = useAriaId(id ?? props.testId);
|
const inputId = useAriaId(id ?? props.testId);
|
||||||
|
const descriptionId = descriptionText ? `input-description-${name}` : undefined;
|
||||||
const variant = error ? "danger" : undefined;
|
const variant = error ? "danger" : undefined;
|
||||||
return (
|
return (
|
||||||
<Field className={className}>
|
<Field className={className}>
|
||||||
@@ -42,8 +44,13 @@ const InputField = React.forwardRef<HTMLInputElement, InputFieldProps>(
|
|||||||
{label}
|
{label}
|
||||||
{helpText ? <Help className="ml-1" text={helpText} /> : null}
|
{helpText ? <Help className="ml-1" text={helpText} /> : null}
|
||||||
</Label>
|
</Label>
|
||||||
|
{descriptionText ? (
|
||||||
|
<p className="mb-2" id={descriptionId}>
|
||||||
|
{descriptionText}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
<Control>
|
<Control>
|
||||||
<Input variant={variant} ref={ref} id={inputId} {...props}></Input>
|
<Input variant={variant} ref={ref} id={inputId} aria-describedby={descriptionId} {...props}></Input>
|
||||||
</Control>
|
</Control>
|
||||||
{error ? <FieldMessage variant={variant}>{error}</FieldMessage> : null}
|
{error ? <FieldMessage variant={variant}>{error}</FieldMessage> : null}
|
||||||
</Field>
|
</Field>
|
||||||
|
|||||||
@@ -16351,7 +16351,7 @@ react-popper@^1.3.7:
|
|||||||
typed-styles "^0.0.7"
|
typed-styles "^0.0.7"
|
||||||
warning "^4.0.2"
|
warning "^4.0.2"
|
||||||
|
|
||||||
react-query@^3.39.2:
|
react-query@^3.25.1, react-query@^3.39.2:
|
||||||
version "3.39.3"
|
version "3.39.3"
|
||||||
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35"
|
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35"
|
||||||
integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
|
integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
|
||||||
|
|||||||
Reference in New Issue
Block a user