mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-12 00:15:44 +01:00
Optimize config form and binder
This commit is contained in:
6
gradle/changelog/fixes.yaml
Normal file
6
gradle/changelog/fixes.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
- type: fixed
|
||||
description: ConfigurationBinder Navlink does not only match exact routes
|
||||
- type: changed
|
||||
description: Move form list entry button to left side
|
||||
- type: changed
|
||||
description: Show empty message for form list table without entries
|
||||
@@ -55,7 +55,7 @@ class ConfigurationBinder {
|
||||
|
||||
route(path: string, Component: any) {
|
||||
return (
|
||||
<Route path={urls.escapeUrlForRoute(path)} exact>
|
||||
<Route path={urls.escapeUrlForRoute(path)}>
|
||||
{Component}
|
||||
</Route>
|
||||
);
|
||||
@@ -70,7 +70,7 @@ class ConfigurationBinder {
|
||||
|
||||
// create NavigationLink with translated label
|
||||
const ConfigNavLink = withTranslation(this.i18nNamespace)(({ t }) => {
|
||||
return this.navLink("/admin/settings" + to, labelI18nKey, t);
|
||||
return this.navLink("/admin/settings" + to, labelI18nKey, t, { activeOnlyWhenExact: false });
|
||||
});
|
||||
|
||||
// bind navigation link to extension point
|
||||
@@ -151,7 +151,7 @@ class ConfigurationBinder {
|
||||
|
||||
// create NavigationLink with translated label
|
||||
const RepoNavLink = withTranslation(this.i18nNamespace)(({ t, url }: RepositoryNavProps) => {
|
||||
return this.navLink(url + "/settings" + to, labelI18nKey, t);
|
||||
return this.navLink(url + "/settings" + to, labelI18nKey, t, { activeOnlyWhenExact: false });
|
||||
});
|
||||
|
||||
// bind navigation link to extension point
|
||||
|
||||
@@ -88,6 +88,9 @@ function AddListEntryForm<FormType extends Record<string, unknown>, DefaultValue
|
||||
const submitButtonLabel = translateWithExtraPrefix("add", {
|
||||
defaultValue: defaultTranslate("list.add.label", { entity: translateWithExtraPrefix("entity") }),
|
||||
});
|
||||
const titleLabel = translateWithExtraPrefix("title", {
|
||||
defaultValue: defaultTranslate("list.title.label", { entity: translateWithExtraPrefix("entity") }),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (isSubmitSuccessful) {
|
||||
@@ -102,9 +105,10 @@ function AddListEntryForm<FormType extends Record<string, unknown>, DefaultValue
|
||||
return (
|
||||
<ScmFormContextProvider {...form} t={translateWithExtraPrefix} formId={nameWithPrefix}>
|
||||
<ScmFormPathContextProvider path="">
|
||||
<h3 className="subtitle is-5">{titleLabel}</h3>
|
||||
<form id={nameWithPrefix} onSubmit={form.handleSubmit(onSubmit)} noValidate></form>
|
||||
{typeof children === "function" ? children(form) : children}
|
||||
<div className="level-right">
|
||||
<div className="level-left">
|
||||
<Button
|
||||
form={nameWithPrefix}
|
||||
type="submit"
|
||||
|
||||
@@ -36,6 +36,8 @@ import AddListEntryForm from "./AddListEntryForm";
|
||||
import { ScmNestedFormPathContextProvider } from "./FormPathContext";
|
||||
|
||||
export { default as ConfigurationForm } from "./ConfigurationForm";
|
||||
export { default as SelectField } from "./select/SelectField";
|
||||
export { default as Select } from "./select/Select";
|
||||
export * from "./resourceHooks";
|
||||
|
||||
export const Form = Object.assign(FormCmp, {
|
||||
|
||||
@@ -33,15 +33,19 @@ type Props = {
|
||||
testId?: string;
|
||||
} & InputHTMLAttributes<HTMLSelectElement>;
|
||||
|
||||
/**
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const Select = React.forwardRef<HTMLSelectElement, Props>(
|
||||
({ variant, children, className, options, testId, ...props }, ref) => (
|
||||
<div className={classNames("select", { "is-multiple": props.multiple }, createVariantClass(variant), className)}>
|
||||
<select ref={ref} {...props} {...createAttributesForTesting(testId)} className={className}>
|
||||
{options
|
||||
? options.map((option) => (
|
||||
<option {...option} key={option.value as Key}>
|
||||
{option.label}
|
||||
{option.children}
|
||||
? options.map((opt) => (
|
||||
<option {...opt} key={opt.value as Key}>
|
||||
{opt.label}
|
||||
{opt.children}
|
||||
</option>
|
||||
))
|
||||
: children}
|
||||
|
||||
@@ -39,6 +39,8 @@ type Props = {
|
||||
|
||||
/**
|
||||
* @see https://bulma.io/documentation/form/select/
|
||||
* @beta
|
||||
* @since 2.44.0
|
||||
*/
|
||||
const SelectField = React.forwardRef<HTMLSelectElement, Props>(
|
||||
({ label, helpText, error, className, id, ...props }, ref) => {
|
||||
|
||||
@@ -31,6 +31,7 @@ import { prefixWithoutIndices } from "../helpers";
|
||||
import classNames from "classnames";
|
||||
import { useScmFormListContext } from "../ScmFormListContext";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Notification } from "@scm-manager/ui-components";
|
||||
|
||||
type RenderProps<T extends Record<string, unknown>, PATH extends Path<T>> = {
|
||||
value: PathValue<T, PATH>;
|
||||
@@ -59,12 +60,9 @@ function ControlledTable<T extends Record<string, unknown>, PATH extends Path<T>
|
||||
const prefixedNameWithoutIndices = prefixWithoutIndices(nameWithPrefix);
|
||||
const { fields, remove } = useScmFormListContext();
|
||||
const deleteLabel = t(`${prefixedNameWithoutIndices}.delete`) || defaultTranslate("delete.label");
|
||||
const emptyTableLabel = t(`${prefixedNameWithoutIndices}.empty`) || defaultTranslate("empty.label");
|
||||
const actionHeaderLabel = t(`${prefixedNameWithoutIndices}.action.label`) || defaultTranslate("headers.action.label");
|
||||
|
||||
if (!fields.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<table className={classNames("table content is-hoverable", className)}>
|
||||
<thead>
|
||||
@@ -76,6 +74,7 @@ function ControlledTable<T extends Record<string, unknown>, PATH extends Path<T>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{fields.length === 0 ? <tr><td colSpan={1000}><Notification type="info">{emptyTableLabel}</Notification></td></tr> : null}
|
||||
{fields.map((value, index) => (
|
||||
<ScmFormPathContextProvider key={value.id} path={`${nameWithPrefix}.${index}`}>
|
||||
<tr>
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
},
|
||||
"delete": {
|
||||
"label": "Löschen"
|
||||
},
|
||||
"empty": {
|
||||
"label": "Keine Einträge"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@@ -20,6 +23,9 @@
|
||||
},
|
||||
"delete": {
|
||||
"label": "{{entity}} löschen"
|
||||
},
|
||||
"title": {
|
||||
"label": "Neuen {{entity}} hinzufügen"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
},
|
||||
"delete": {
|
||||
"label": "Delete"
|
||||
},
|
||||
"empty": {
|
||||
"label": "No entries"
|
||||
}
|
||||
},
|
||||
"list": {
|
||||
@@ -20,6 +23,9 @@
|
||||
},
|
||||
"delete": {
|
||||
"label": "Delete {{entity}}"
|
||||
},
|
||||
"title": {
|
||||
"label": "Add new {{entity}}"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user