-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
1,341 additions
and
0 deletions.
There are no files selected for viewing
41 changes: 41 additions & 0 deletions
41
src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import React, { ReactNode } from 'react'; | ||
import { EuiForm } from '@elastic/eui'; | ||
|
||
import { FormProvider } from '../form_context'; | ||
import { FormHook } from '../types'; | ||
|
||
interface Props { | ||
form: FormHook<any>; | ||
FormWrapper?: (props: any) => JSX.Element; | ||
children: ReactNode | ReactNode[]; | ||
className: string; | ||
} | ||
|
||
const DefaultFormWrapper = (props: any) => { | ||
return <EuiForm {...props} />; | ||
}; | ||
|
||
export const Form = ({ form, FormWrapper = DefaultFormWrapper, ...rest }: Props) => ( | ||
<FormProvider form={form}> | ||
<FormWrapper {...rest} /> | ||
</FormProvider> | ||
); |
61 changes: 61 additions & 0 deletions
61
src/plugins/es_ui_shared/static/forms/hook_form_lib/components/form_data_provider.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import { useState, useEffect, useRef } from 'react'; | ||
|
||
import { FormData } from '../types'; | ||
import { Subscription } from '../lib'; | ||
import { useFormContext } from '../form_context'; | ||
|
||
interface Props { | ||
children: (formData: FormData) => JSX.Element | null; | ||
pathsToWatch?: string | string[]; | ||
} | ||
|
||
export const FormDataProvider = ({ children, pathsToWatch }: Props) => { | ||
const [formData, setFormData] = useState<FormData>({}); | ||
const previousState = useRef<FormData>({}); | ||
const subscription = useRef<Subscription | null>(null); | ||
const form = useFormContext(); | ||
|
||
useEffect(() => { | ||
subscription.current = form.__formData$.current.subscribe(data => { | ||
// To avoid re-rendering the children for updates on the form data | ||
// that we are **not** interested in, we can specify one or multiple path(s) | ||
// to watch. | ||
if (pathsToWatch) { | ||
const valuesToWatchArray = Array.isArray(pathsToWatch) | ||
? (pathsToWatch as string[]) | ||
: ([pathsToWatch] as string[]); | ||
if (valuesToWatchArray.some(value => previousState.current[value] !== data[value])) { | ||
previousState.current = data; | ||
setFormData(data); | ||
} | ||
} else { | ||
setFormData(data); | ||
} | ||
}); | ||
|
||
return () => { | ||
subscription.current!.unsubscribe(); | ||
}; | ||
}, [pathsToWatch]); | ||
|
||
return children(formData); | ||
}; |
23 changes: 23 additions & 0 deletions
23
src/plugins/es_ui_shared/static/forms/hook_form_lib/components/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
export * from './form'; | ||
export * from './use_field'; | ||
export * from './use_array'; | ||
export * from './form_data_provider'; |
103 changes: 103 additions & 0 deletions
103
src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_array.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import { useState, useRef } from 'react'; | ||
|
||
import { useFormContext } from '../form_context'; | ||
|
||
interface Props { | ||
path: string; | ||
initialNumberOfItems?: number; | ||
children: (args: { | ||
items: ArrayItem[]; | ||
addItem: () => void; | ||
removeItem: (id: number) => void; | ||
}) => JSX.Element; | ||
} | ||
|
||
export interface ArrayItem { | ||
id: number; | ||
path: string; | ||
isNew: boolean; | ||
} | ||
|
||
/** | ||
* Use UseArray to dynamically add fields to your form. | ||
* | ||
* example: | ||
* If your form data looks like this: | ||
* | ||
* { | ||
* users: [] | ||
* } | ||
* | ||
* and you want to be able to add user objects ({ name: 'john', lastName. 'snow' }) inside | ||
* the "users" array, you would use UseArray to render rows of user objects with 2 fields in each of them ("name" and "lastName") | ||
* | ||
* Look at the README.md for some examples. | ||
*/ | ||
export const UseArray = ({ path, initialNumberOfItems, children }: Props) => { | ||
const form = useFormContext(); | ||
const defaultValues = form.getFieldDefaultValue(path) as any[]; | ||
const uniqueId = useRef(0); | ||
|
||
const getInitialItemsFromValues = (values: any[]): ArrayItem[] => | ||
values.map((_, index) => ({ | ||
id: uniqueId.current++, | ||
path: `${path}[${index}]`, | ||
isNew: false, | ||
})); | ||
|
||
const getNewItemAtIndex = (index: number): ArrayItem => ({ | ||
id: uniqueId.current++, | ||
path: `${path}[${index}]`, | ||
isNew: true, | ||
}); | ||
|
||
const initialState = defaultValues | ||
? getInitialItemsFromValues(defaultValues) | ||
: new Array(initialNumberOfItems).fill('').map((_, i) => getNewItemAtIndex(i)); | ||
|
||
const [items, setItems] = useState<ArrayItem[]>(initialState); | ||
|
||
const updatePaths = (_rows: ArrayItem[]) => | ||
_rows.map( | ||
(row, index) => | ||
({ | ||
...row, | ||
path: `${path}[${index}]`, | ||
} as ArrayItem) | ||
); | ||
|
||
const addItem = () => { | ||
setItems(previousItems => { | ||
const itemIndex = previousItems.length; | ||
return [...previousItems, getNewItemAtIndex(itemIndex)]; | ||
}); | ||
}; | ||
|
||
const removeItem = (id: number) => { | ||
setItems(previousItems => { | ||
const updatedItems = previousItems.filter(item => item.id !== id); | ||
return updatePaths(updatedItems); | ||
}); | ||
}; | ||
|
||
return children({ items, addItem, removeItem }); | ||
}; |
93 changes: 93 additions & 0 deletions
93
src/plugins/es_ui_shared/static/forms/hook_form_lib/components/use_field.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
import React, { useEffect, FunctionComponent } from 'react'; | ||
|
||
import { FieldHook, FieldConfig } from '../types'; | ||
import { useField } from '../hooks'; | ||
import { useFormContext } from '../form_context'; | ||
|
||
interface Props { | ||
path: string; | ||
config?: FieldConfig<any>; | ||
defaultValue?: unknown; | ||
component?: FunctionComponent<any> | 'input'; | ||
componentProps?: Record<string, any>; | ||
children?: (field: FieldHook) => JSX.Element; | ||
} | ||
|
||
export const UseField = ({ | ||
path, | ||
config, | ||
defaultValue, | ||
component = 'input', | ||
componentProps = {}, | ||
children, | ||
}: Props) => { | ||
const form = useFormContext(); | ||
|
||
if (typeof defaultValue === 'undefined') { | ||
defaultValue = form.getFieldDefaultValue(path); | ||
} | ||
|
||
if (!config) { | ||
config = form.__readFieldConfigFromSchema(path); | ||
} | ||
|
||
// Don't modify the config object | ||
const configCopy = | ||
typeof defaultValue !== 'undefined' ? { ...config, defaultValue } : { ...config }; | ||
|
||
if (!configCopy.path) { | ||
configCopy.path = path; | ||
} else { | ||
if (configCopy.path !== path) { | ||
throw new Error( | ||
`Field path mismatch. Got "${path}" but field config has "${configCopy.path}".` | ||
); | ||
} | ||
} | ||
|
||
const field = useField(form, path, configCopy); | ||
|
||
// Remove field from form when it is unmounted or if its path changes | ||
useEffect(() => { | ||
return () => { | ||
form.__removeField(path); | ||
}; | ||
}, [path]); | ||
|
||
// Children prevails over anything else provided. | ||
if (children) { | ||
return children!(field); | ||
} | ||
|
||
if (component === 'input') { | ||
return ( | ||
<input | ||
type={field.type} | ||
onChange={field.onChange} | ||
value={field.value as string} | ||
{...componentProps} | ||
/> | ||
); | ||
} | ||
|
||
return component({ field, ...componentProps }); | ||
}; |
36 changes: 36 additions & 0 deletions
36
src/plugins/es_ui_shared/static/forms/hook_form_lib/constants.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
/* | ||
* Licensed to Elasticsearch B.V. under one or more contributor | ||
* license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright | ||
* ownership. Elasticsearch B.V. licenses this file to you under | ||
* the Apache License, Version 2.0 (the "License"); you may | ||
* not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, | ||
* software distributed under the License is distributed on an | ||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
* KIND, either express or implied. See the License for the | ||
* specific language governing permissions and limitations | ||
* under the License. | ||
*/ | ||
|
||
// Field types | ||
export const FIELD_TYPES = { | ||
TEXT: 'text', | ||
NUMBER: 'number', | ||
TOGGLE: 'toggle', | ||
CHECKBOX: 'checkbox', | ||
COMBO_BOX: 'comboBox', | ||
SELECT: 'select', | ||
MULTI_SELECT: 'multiSelect', | ||
}; | ||
|
||
// Validation types | ||
export const VALIDATION_TYPES = { | ||
FIELD: 'field', // Default validation error (on the field value) | ||
ASYNC: 'async', // Returned from asynchronous validations | ||
ARRAY_ITEM: 'arrayItem', // If the field value is an Array, this error would be returned if an _item_ of the array is invalid | ||
}; |
Oops, something went wrong.