mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-06 12:18:34 +02:00
Building forms documentation with react-hook-form (#1704)
Building forms documentation with react-hook-form
This commit is contained in:
@@ -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 <http://localhost:8081/scm>.
|
||||
You can also start a dev server using `./gradlew run`. SCM-Manager is served at <http://localhost:8081/scm>.
|
||||
|
||||
145
docs/en/development/building-forms.md
Normal file
145
docs/en/development/building-forms.md
Normal file
@@ -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<Name>();
|
||||
const [stored, setStored] = useState<Person>();
|
||||
|
||||
const onSubmit = (person: Person) => {
|
||||
setStored(person);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<InputField label="First Name" autofocus={true} {...register("firstName")} />
|
||||
<InputField
|
||||
label="Last Name"
|
||||
{...register("lastName", { required: true })}
|
||||
validationError={!!errors.lastName}
|
||||
errorMessage={"Last name is required"}
|
||||
/>
|
||||
<Level className="pt-2" right={<SubmitButton>Submit</SubmitButton>} />
|
||||
</form>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### 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<Props> = ({ link }) => {
|
||||
// formProps spread syntax returns prop for name, onBlur, onChange and ref and additionally attaches them to fields
|
||||
const { initialConfiguration, update, isReadOnly, ...formProps } = useConfigLink<GlobalConfigurationDto>(link);
|
||||
const { formState, handleSubmit, register, reset, control } = useForm<GlobalConfigurationDto>({
|
||||
// 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 (
|
||||
<ConfigurationForm isValid={formState.isValid} isReadOnly={isReadOnly} onSubmit={handleSubmit(update)} {...formProps}>
|
||||
<Title title={t("settings.title")} />
|
||||
<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)
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Testing guide
|
||||
title: Testing Guide
|
||||
subtitle: Howto write tests for SCM-Manager
|
||||
displayToc: false
|
||||
---
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
- /development/definition-of-done/
|
||||
- /development/ui-dod/
|
||||
- /development/decision-table/
|
||||
- /development/building-forms/
|
||||
- /development/testing-guide/
|
||||
- /development/integration-tests/
|
||||
|
||||
|
||||
2
gradle/changelog/react_hook_form_doc.yaml
Normal file
2
gradle/changelog/react_hook_form_doc.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- type: added
|
||||
description: Building forms documentation with react-hook-form ([#1704](https://github.com/scm-manager/scm-manager/pull/1704))
|
||||
Reference in New Issue
Block a user