1
0
mirror of https://github.com/OpenNebula/one.git synced 2024-12-24 21:34:01 +03:00

F #3951: Add autocomplete component (#221)

This commit is contained in:
Sergio Betanzos 2020-09-17 15:40:09 +02:00 committed by GitHub
parent 123e08cfd6
commit 9935e9916c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 24 deletions

View File

@ -19,6 +19,7 @@ const DialogForm = memo(
const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs')); const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs'));
const { handleSubmit, ...methods } = useForm({ const { handleSubmit, ...methods } = useForm({
mode: 'onChange',
reValidateMode: 'onSubmit', reValidateMode: 'onSubmit',
defaultValues: values, defaultValues: values,
resolver: yupResolver(resolver) resolver: yupResolver(resolver)

View File

@ -0,0 +1,72 @@
import React from 'react';
import PropTypes from 'prop-types';
import { TextField } from '@material-ui/core';
import { Autocomplete } from '@material-ui/lab';
import { Controller } from 'react-hook-form';
import ErrorHelper from 'client/components/FormControl/ErrorHelper';
const AutocompleteController = ({
control,
cy,
name,
label,
values,
error
}) => (
<Controller
render={({ value: val, onBlur, onChange }) => {
const selected = values.find(({ value }) => value === val) ?? null;
return (
<Autocomplete
fullWidth
onBlur={onBlur}
onChange={(_, newValue) => onChange(newValue.value)}
options={values}
value={selected}
getOptionLabel={option => option.text}
getOptionSelected={option => option.value === val}
renderInput={({ inputProps, ...inputParams }) => (
<TextField
label={label}
inputProps={{ ...inputProps, 'data-cy': cy }}
error={Boolean(error)}
helperText={
Boolean(error) && <ErrorHelper label={error?.message} />
}
FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
{...inputParams}
/>
)}
/>
);
}}
name={name}
control={control}
/>
);
AutocompleteController.propTypes = {
control: PropTypes.object,
cy: PropTypes.string,
name: PropTypes.string.isRequired,
label: PropTypes.string,
values: PropTypes.arrayOf(PropTypes.object).isRequired,
error: PropTypes.oneOfType([
PropTypes.bool,
PropTypes.objectOf(PropTypes.any)
])
};
AutocompleteController.defaultProps = {
control: {},
cy: 'cy',
name: '',
label: '',
values: [],
error: false
};
export default AutocompleteController;

View File

@ -19,7 +19,6 @@ const SelectController = memo(
error={Boolean(error)} error={Boolean(error)}
helperText={Boolean(error) && <ErrorHelper label={error?.message} />} helperText={Boolean(error) && <ErrorHelper label={error?.message} />}
FormHelperTextProps={{ 'data-cy': `${cy}-error` }} FormHelperTextProps={{ 'data-cy': `${cy}-error` }}
style={{ marginTop: 12 }}
> >
{Array.isArray(values) && {Array.isArray(values) &&
values?.map(({ text, value }) => ( values?.map(({ text, value }) => (

View File

@ -2,17 +2,19 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Grid } from '@material-ui/core'; import { Grid } from '@material-ui/core';
import { useFormContext } from 'react-hook-form'; import { useFormContext, useWatch } from 'react-hook-form';
import { TYPE_INPUT } from 'client/constants'; import { TYPE_INPUT } from 'client/constants';
import TextController from 'client/components/FormControl/TextController'; import TextController from 'client/components/FormControl/TextController';
import SelectController from 'client/components/FormControl/SelectController'; import SelectController from 'client/components/FormControl/SelectController';
import CheckboxController from 'client/components/FormControl/CheckboxController'; import CheckboxController from 'client/components/FormControl/CheckboxController';
import AutocompleteController from 'client/components/FormControl/AutocompleteController';
const InputController = { const InputController = {
[TYPE_INPUT.TEXT]: TextController, [TYPE_INPUT.TEXT]: TextController,
[TYPE_INPUT.SELECT]: SelectController, [TYPE_INPUT.SELECT]: SelectController,
[TYPE_INPUT.CHECKBOX]: CheckboxController [TYPE_INPUT.CHECKBOX]: CheckboxController,
[TYPE_INPUT.AUTOCOMPLETE]: AutocompleteController
}; };
const FormWithSchema = ({ id, cy, fields }) => { const FormWithSchema = ({ id, cy, fields }) => {
@ -20,11 +22,14 @@ const FormWithSchema = ({ id, cy, fields }) => {
return ( return (
<Grid container spacing={1}> <Grid container spacing={1}>
{fields?.map(({ name, type, label, values }) => { {fields?.map(({ name, type, label, values, dependOf }) => {
const dataCy = `${cy}-${name}`; const dataCy = `${cy}-${name}`;
const inputName = id ? `${id}.${name}` : name; const inputName = id ? `${id}.${name}` : name;
const formError = id ? errors[id] : errors; const formError = id ? errors[id] : errors;
const inputError = formError ? formError[name] : false; const inputError = formError ? formError[name] : false;
const dependValue = dependOf
? useWatch({ control, name: id ? `${id}.${dependOf}` : dependOf })
: null;
return ( return (
<Grid key={`${cy}-${name}`} item xs={12} md={6}> <Grid key={`${cy}-${name}`} item xs={12} md={6}>
@ -34,7 +39,7 @@ const FormWithSchema = ({ id, cy, fields }) => {
cy: dataCy, cy: dataCy,
name: inputName, name: inputName,
label, label,
values, values: dependOf ? values(dependValue) : values,
error: inputError error: inputError
})} })}
</Grid> </Grid>

View File

@ -71,11 +71,11 @@ Search.propTypes = {
}; };
Search.defaultProps = { Search.defaultProps = {
fullList: [], list: [],
listOptions: {}, listOptions: {},
renderResult: item => item, renderResult: item => item,
startAdornment: undefined, startAdornment: undefined,
searchBoxProps: {} searchBoxProps: undefined
}; };
export default Search; export default Search;

View File

@ -33,6 +33,7 @@ module.exports = {
TYPE_INPUT: { TYPE_INPUT: {
TEXT: 'text', TEXT: 'text',
SELECT: 'select', SELECT: 'select',
CHECKBOX: 'checkbox' CHECKBOX: 'checkbox',
AUTOCOMPLETE: 'autocomplete'
} }
}; };

View File

@ -23,7 +23,7 @@ export const FORM_FIELDS = [
.min(5) .min(5)
.trim() .trim()
.required('Name field is required') .required('Name field is required')
.default('One_service') .default('One_application')
}, },
{ {
name: 'description', name: 'description',

View File

@ -19,7 +19,7 @@ const Clusters = () => {
resolver: STEP_FORM_SCHEMA, resolver: STEP_FORM_SCHEMA,
onlyOneSelect: true, onlyOneSelect: true,
preRender: getClusters, preRender: getClusters,
list: clusters, list: clusters?.sort((a, b) => a.ID - b.ID),
ItemComponent: ClusterCard ItemComponent: ClusterCard
}), }),
[getClusters, clusters] [getClusters, clusters]

View File

@ -1,15 +1,7 @@
import * as yup from 'yup'; import * as yup from 'yup';
import { TYPE_INPUT } from 'client/constants'; import { TYPE_INPUT } from 'client/constants';
import { getValidationFromFields } from 'client/utils/helpers'; import { getValidationFromFields } from 'client/utils/helpers';
import useOpennebula from 'client/hooks/useOpennebula';
const DEFAULT_NETWORK = {
mandatory: true,
name: 'Public_dev',
description: 'Public network in development mode',
type: 'id',
id: '0',
extra: 'size=5'
};
export const SELECT = { export const SELECT = {
template: 'template', template: 'template',
@ -66,7 +58,27 @@ export const FORM_FIELDS = [
{ {
name: 'id', name: 'id',
label: `Select a network`, label: `Select a network`,
type: TYPE_INPUT.TEXT, type: TYPE_INPUT.AUTOCOMPLETE,
dependOf: 'type',
values: dependValue => {
const { vNetworks, vNetworksTemplates } = useOpennebula();
const type = TYPES_NETWORKS.find(({ value }) => value === dependValue);
switch (type?.select) {
case SELECT.network:
return vNetworks.map(({ ID, NAME }) => ({
text: NAME,
value: ID
}));
case SELECT.template:
return vNetworksTemplates.map(({ ID, NAME }) => ({
text: NAME,
value: ID
}));
default:
return [];
}
},
validation: yup validation: yup
.string() .string()
.when('type', { .when('type', {
@ -84,7 +96,7 @@ export const FORM_FIELDS = [
.required('Network template is required field') .required('Network template is required field')
}) })
.required() .required()
.default('') .default(null)
}, },
{ {
name: 'extra', name: 'extra',
@ -105,6 +117,4 @@ export const NETWORK_FORM_SCHEMA = yup.object(
export const STEP_FORM_SCHEMA = yup export const STEP_FORM_SCHEMA = yup
.array() .array()
.of(NETWORK_FORM_SCHEMA) .of(NETWORK_FORM_SCHEMA)
.min(2) .default([]);
.required()
.default([DEFAULT_NETWORK, DEFAULT_NETWORK]);