Optimize config form and binder

This commit is contained in:
Eduard Heimbuch
2023-04-19 11:52:22 +02:00
parent 40c4e1672c
commit a185cc7d16
9 changed files with 41 additions and 12 deletions

View 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

View File

@@ -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

View File

@@ -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"

View File

@@ -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, {

View File

@@ -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}

View File

@@ -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) => {

View File

@@ -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>

View File

@@ -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"
}
}
},

View File

@@ -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}}"
}
}
},