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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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