From 9b6fb991e551590d60d6e03f78502ea37ccfe981 Mon Sep 17 00:00:00 2001 From: Florian Scholdei Date: Wed, 16 Jun 2021 17:19:55 +0200 Subject: [PATCH] Building forms documentation with react-hook-form (#1704) Building forms documentation with react-hook-form --- docs/en/development/build-from-source.md | 25 ++-- docs/en/development/building-forms.md | 145 ++++++++++++++++++++++ docs/en/development/testing-guide.md | 2 +- docs/en/navigation.yml | 1 + gradle/changelog/react_hook_form_doc.yaml | 2 + 5 files changed, 159 insertions(+), 16 deletions(-) create mode 100644 docs/en/development/building-forms.md create mode 100644 gradle/changelog/react_hook_form_doc.yaml diff --git a/docs/en/development/build-from-source.md b/docs/en/development/build-from-source.md index dd770f0323..33f88886b4 100644 --- a/docs/en/development/build-from-source.md +++ b/docs/en/development/build-from-source.md @@ -1,30 +1,25 @@ --- -title: Building SCM-Manager from source +title: Building SCM-Manager from Source --- ### Software Requirements - JDK 1.8 or higher ([download](https://openjdk.java.net/install/)) -- Maven 3 or higher ([download](http://maven.apache.org/)) -- Mercurial ([download](https://www.mercurial-scm.org/)) +- Maven 3 or higher ([download](https://maven.apache.org/)) +- Git ([download](https://git-scm.com/)) -### Build SCM-Manager 2.x from source +### Build SCM-Manager 2.x from Source ```bash -hg clone https://github.com/scm-manager/scm-manager.git +git clone https://github.com/scm-manager/scm-manager.git cd scm-manager git checkout develop -mvn clean install +./gradlew build ``` -**Note**: if you use the \"package\" phase rather than the \"install\" phase, -the standalone version may include an old version of the WAR file in the distribution bundle, -rather than the version you just built. +After gradle finished, the war bundle is located at +**scm-webapp/build/scm-webapp.war** and the standalone version is +located at **scm-server/build/scm-server-app**. -After mvn finished, the war bundle is located at -**scm-webapp/target/scm-webapp.war** and the standalone version is -located at **scm-server/target/scm-server-app**. - -You can also start a dev server using `mvn jetty:run-war -f -scm-webapp`. SCM-Manager is served at . +You can also start a dev server using `./gradlew run`. SCM-Manager is served at . diff --git a/docs/en/development/building-forms.md b/docs/en/development/building-forms.md new file mode 100644 index 0000000000..46ffd2e71b --- /dev/null +++ b/docs/en/development/building-forms.md @@ -0,0 +1,145 @@ +--- +title: Building Forms +subtitle: Howto build forms for SCM-Manager +displayToc: false +--- + +Below we would like to explain how to write [React Hook Form](https://react-hook-form.com/) forms in an easy and fast way, +why it makes sense to switch and what needs to be considered. + +### Legacy Process + +Previously, we passed our self-written form component into the Configuration component's render function. +In the form we defined a prop for each entry, plus an onChange handler that takes the value and writes it to a state. +Additionally, we added validation logic when a field changes. + +Especially in [old areas](https://github.com/scm-manager/scm-ldap-plugin/blob/develop/src/main/js/LdapConfigurationForm.tsx#L65), which were still built with class components, you should be very careful. + +A lot of boilerplate code was needed, errors were frequent, and typings were generally flawed. + +### Standard Process + +React Hook Form will bring the `useForm` hook to validate your form with minimal re-render. +This contains a generic parameter which summarizes the possible input fields. + +The useForm hook returns an object with several properties: + +- `register` allows you to register an input or select element and apply validation rules to React Hook Form. +- `formState` contains information about the form state. This can also specify `isValid`. +- `handleSubmit` is called when you press the submit button and will receive the form data if form validation is successful. +- `reset` reset either the entire form state or part of the form state. + +```tsx +import React, { FC, useEffect } from "react"; +// import hook from react-hook-form library +import { useForm } from "react-hook-form"; + +const ReactHookForm: FC = () => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + const [stored, setStored] = useState(); + + const onSubmit = (person: Person) => { + setStored(person); + }; + + return ( +
+ + + Submit} /> + + ); +}; +``` + +### Building Configuration Forms + +`UseConfigLink` from `@scm-manager/ui-api` gets links via prop from binder and loads initial config asynchronously, +also specifies as reading part whether readOnly (no update link was set) and as writing part an update method. +As well as formProps for isLoading, isUpdating etc for ConfigurationForm. + +```tsx +import React, { FC, useEffect } from "react"; +import { useForm } from "react-hook-form"; + +const GlobalConfig: FC = ({ link }) => { + // formProps spread syntax returns prop for name, onBlur, onChange and ref and additionally attaches them to fields + const { initialConfiguration, update, isReadOnly, ...formProps } = useConfigLink(link); + const { formState, handleSubmit, register, reset, control } = useForm({ + // mode onChange should be specified so that validation takes place immediately! + mode: "onChange", + }); + + // ... +}; +``` + +ConfigurationForm only takes care of the display of the component. All the logic now lives in the hook. + +Registering your own `onChange`-handler is not necessary anymore. +`onSubmit` `handleSubmit`-function passes own submit function, which is called with filled form data type. + +In the `register`-function you can specify additional options for validation. +For example, _required, min, max, pattern_. + +```tsx +return ( + + + <Checkbox + label={t("fastForwardOnly.label")} + helpText={t("fastForwardOnly.helpText")} + disabled={isReadOnly} + {...register("fastForwardOnly", { shouldUnregister: true })} + /> + <InputField + label={t("branchesAndTagsPatterns.label")} + helpText={t("branchesAndTagsPatterns.helpText")} + disabled={isReadOnly} + {...register("branchesAndTagsPatterns")} + /> + <GpgVerificationControl control={control} isReadonly={isReadOnly} /> + </ConfigurationForm> +); +``` + +#### Note when using `formState` + +Be sure to use as proxy to get objects out (not formState.isValid!), because you won't notice the render cycle otherwise. + +#### Set to initial values + +In synchronous loading, a form can be set to an initial value using `defaultValue`. +In the asynchronous case, values for each field can be set separately by using `defaultValue={stored.fastForwardOnly}` or an entire form using `reset`. + +```tsx +useEffect(() => { + if (initialConfiguration) { + reset(initialConfiguration); + } +}, [initialConfiguration]); +``` + +### Note when Creating new Components + +- If possible, pass all props. +- React Hook Form needs the following values for event to be recognized: name, onChange, onBlur, ref (reference to input element). +- `FormFieldTypes` is not a base, but helps for backwards compatibility with old function types. When writing a new component omit old onChange! +- Since some components have other elements built around an input field, there is also the `forwardRef`. It creates a reference that can be passed to an inner element. +- Nested forms are a bit more complex to build and might need a wrapper. +- Validation rules are all based on the HTML standard and also allow for custom validation methods. +- Fields marked as `disabled` in SCM-Manager won't be included on submission. If you want to prevent interaction but need to submit the value of a form element, `readOnly` is the better choice. + +Some implementations: + +- [Git Global Configuration](https://github.com/scm-manager/scm-manager/blob/develop/scm-plugins/scm-git-plugin/src/main/js/GitGlobalConfiguration.tsx#L43) +- [Repository Mirror Plugin Config](https://github.com/scm-manager/scm-repository-mirror-plugin/blob/develop/src/main/js/config/GlobalConfig.tsx#L37) diff --git a/docs/en/development/testing-guide.md b/docs/en/development/testing-guide.md index 34650b9659..0f98c9e8ff 100644 --- a/docs/en/development/testing-guide.md +++ b/docs/en/development/testing-guide.md @@ -1,5 +1,5 @@ --- -title: Testing guide +title: Testing Guide subtitle: Howto write tests for SCM-Manager displayToc: false --- diff --git a/docs/en/navigation.yml b/docs/en/navigation.yml index 7d5f0b49f2..0b2dded1e8 100644 --- a/docs/en/navigation.yml +++ b/docs/en/navigation.yml @@ -33,6 +33,7 @@ - /development/definition-of-done/ - /development/ui-dod/ - /development/decision-table/ + - /development/building-forms/ - /development/testing-guide/ - /development/integration-tests/ diff --git a/gradle/changelog/react_hook_form_doc.yaml b/gradle/changelog/react_hook_form_doc.yaml new file mode 100644 index 0000000000..82ec2f3204 --- /dev/null +++ b/gradle/changelog/react_hook_form_doc.yaml @@ -0,0 +1,2 @@ +- type: added + description: Building forms documentation with react-hook-form ([#1704](https://github.com/scm-manager/scm-manager/pull/1704))