diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.mdx b/packages/grafana-ui/src/components/Forms/FieldArray.mdx new file mode 100644 index 00000000000..465b68acc4f --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/FieldArray.mdx @@ -0,0 +1,40 @@ +import { Meta, Props } from '@storybook/addon-docs/blocks'; +import { FieldArray } from './FieldArray'; + + + +# FieldArray + +`FieldArray` provides a way to render a list of dynamic inputs. It exposes the functionality of `useFieldArray` in [react-hook-form](https://react-hook-form.com/advanced-usage/#FieldArrays). `FieldArray` must be wrapped at some level by a `
` element. + +### Usage + +```jsx +import { Form, FieldArray } from '@grafana/ui'; + + + ({control, register}) => ( + + {({ fields, append }) => ( +
+ {fields.map((field, index) => ( +
+ + +
+ ))} + +
+ )} +
+ ) +
; +``` + +### FieldArray API + +The `FieldArray` component exposes its API via render prop. Properties exposed are: `fields`, `append`, `prepend`, `remove`, `swap`, `move`, `insert` + +### Props + + diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx b/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx new file mode 100644 index 00000000000..fd4bccd6820 --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/FieldArray.story.tsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { withStoryContainer } from '../../utils/storybook/withStoryContainer'; +import { Form, Input, Button, HorizontalGroup } from '@grafana/ui'; +import { FieldArray } from './FieldArray'; +import mdx from './FieldArray.mdx'; + +export default { + title: 'Forms/FieldArray', + component: FieldArray, + decorators: [withStoryContainer, withCenteredStory], + parameters: { + docs: { + page: mdx, + }, + }, +}; + +export const simple = () => { + const defaultValues = { + people: [{ firstName: 'Janis', lastName: 'Joplin' }], + }; + return ( +
console.log(values)} defaultValues={defaultValues}> + {({ control, register }) => ( +
+ + {({ fields, append }) => ( + <> +
+ {fields.map((field, index) => ( + + + + + ))} +
+ + + )} +
+ +
+ )} +
+ ); +}; diff --git a/packages/grafana-ui/src/components/Forms/FieldArray.tsx b/packages/grafana-ui/src/components/Forms/FieldArray.tsx new file mode 100644 index 00000000000..d9f07bb5e1c --- /dev/null +++ b/packages/grafana-ui/src/components/Forms/FieldArray.tsx @@ -0,0 +1,15 @@ +import { FC } from 'react'; +import { useFieldArray, UseFieldArrayProps } from 'react-hook-form'; +import { FieldArrayApi } from '../../types'; + +export interface FieldArrayProps extends UseFieldArrayProps { + children: (api: FieldArrayApi) => JSX.Element; +} + +export const FieldArray: FC = ({ name, control, children }) => { + const { fields, append, prepend, remove, swap, move, insert } = useFieldArray({ + control, + name, + }); + return children({ fields, append, prepend, remove, swap, move, insert }); +}; diff --git a/packages/grafana-ui/src/types/forms.ts b/packages/grafana-ui/src/types/forms.ts index 9442095440f..8088399e2f5 100644 --- a/packages/grafana-ui/src/types/forms.ts +++ b/packages/grafana-ui/src/types/forms.ts @@ -1,7 +1,19 @@ -import { FormContextValues } from 'react-hook-form'; +import { FormContextValues, FieldValues, ArrayField } from 'react-hook-form'; export { OnSubmit as FormsOnSubmit, FieldErrors as FormFieldErrors } from 'react-hook-form'; export type FormAPI = Pick< FormContextValues, 'register' | 'errors' | 'control' | 'formState' | 'getValues' | 'watch' >; + +type FieldArrayValue = Partial | Array>; + +export interface FieldArrayApi { + fields: Array>>; + append: (value: FieldArrayValue) => void; + prepend: (value: FieldArrayValue) => void; + remove: (index?: number | number[]) => void; + swap: (indexA: number, indexB: number) => void; + move: (from: number, to: number) => void; + insert: (index: number, value: FieldArrayValue) => void; +}