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