Skip to content

Commit b796209

Browse files
committed
split capability multi-select field to separate single-select fields in service create
1 parent 2a2d5af commit b796209

File tree

2 files changed

+116
-75
lines changed

2 files changed

+116
-75
lines changed

app/javascript/components/storage-service-form/index.jsx

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
import React, { useState, useEffect } from 'react';
22
import PropTypes from 'prop-types';
33
import MiqFormRenderer from '@@ddf';
4-
import { Loading } from 'carbon-components-react';
54
import createSchema from './storage-service-form.schema';
65
import miqRedirectBack from '../../helpers/miq-redirect-back';
7-
import EditingContext from '../physical-storage-form/editing-context';
86
import mapper from '../../forms/mappers/componentMapper';
97
import enhancedSelect from '../../helpers/enhanced-select';
108

119
const StorageServiceForm = ({ recordId, storageManagerId }) => {
12-
const [state, setState] = useState({});
13-
const { isLoading, initialValues } = state;
10+
const [{ fields, initialValues, isLoading }, setState] = useState({ fields: [], isLoading: !!recordId || !!storageManagerId });
1411
const submitLabel = !!recordId ? __('Save') : __('Add');
1512

16-
const loadSchema = (appendState = {}) => () => {
13+
const loadSchema = (appendState = {}) => ({ data: { form_schema: { fields } } }) => {
1714
setState((state) => ({
1815
...state,
1916
...appendState,
17+
fields,
18+
}));
19+
};
20+
21+
const emptySchema = (appendState = {}) => {
22+
const fields = [];
23+
setState((state) => ({
24+
...state,
25+
...appendState,
26+
fields,
2027
}));
2128
};
2229

2330
useEffect(() => {
2431
if (recordId) {
2532
API.get(`/api/storage_services/${recordId}`).then((initialValues) => {
26-
API.options(`/api/storage_services?ems_id=${initialValues.ems_id}`)
27-
.then(loadSchema({ initialValues: { ...initialValues, edit: 'yes' }, isLoading: false }));
33+
API.options(`/api/storage_services/${recordId}?ems_id=${initialValues.ems_id}`).then(loadSchema({ initialValues, isLoading: false }));
2834
});
2935
}
3036
if (storageManagerId) {
@@ -33,18 +39,24 @@ const StorageServiceForm = ({ recordId, storageManagerId }) => {
3339
}
3440
}, [recordId, storageManagerId]);
3541

42+
const redirectUrl = storageManagerId ? `/ems_storage/${storageManagerId}?display=storage_services#/` : '/storage_service/show_list';
43+
3644
const onSubmit = ({ edit: _edit, ...values }) => {
37-
miqSparkleOn();
38-
const request = recordId ? API.patch(`/api/storage_services/${recordId}`, values) : API.post('/api/storage_services', values);
39-
request.then(() => {
40-
const message = sprintf(
41-
recordId
42-
? __('Modification of Storage Service "%s" has been successfully queued.')
43-
: __('Add of Storage Service "%s" has been successfully queued.'),
44-
values.name,
45-
);
46-
miqRedirectBack(message, undefined, '/storage_service/show_list');
47-
}).catch(miqSparkleOff);
45+
if (values.ems_id !== '-1') {
46+
miqSparkleOn();
47+
48+
const request = recordId ? API.patch(`/api/storage_services/${recordId}`, values) : API.post('/api/storage_services', values);
49+
request.then(() => {
50+
const message = sprintf(
51+
recordId
52+
? __('Modification of Storage Service "%s" has been successfully queued.')
53+
: __('Add of Storage Service "%s" has been successfully queued.'),
54+
values.name,
55+
);
56+
57+
miqRedirectBack(message, undefined, redirectUrl);
58+
}).catch(miqSparkleOff);
59+
}
4860
};
4961

5062
const onCancel = () => {
@@ -54,33 +66,40 @@ const StorageServiceForm = ({ recordId, storageManagerId }) => {
5466
: __('Add of new Storage Service was cancelled by the user.'),
5567
initialValues && initialValues.name,
5668
);
57-
miqRedirectBack(message, 'warning', '/storage_service/show_list');
69+
miqRedirectBack(message, 'warning', redirectUrl);
5870
};
5971

60-
if (isLoading) return <Loading className="export-spinner" withOverlay={false} small />;
72+
const validation = (values) => {
73+
const errors = {};
74+
if (values.ems_id === '-1') {
75+
errors.ems_id = __('Required');
76+
}
77+
return errors;
78+
};
6179

6280
const componentMapper = {
6381
...mapper,
6482
'enhanced-select': enhancedSelect,
6583
};
6684

67-
return (
68-
<div>
69-
{ !isLoading && (
70-
<EditingContext.Provider value={{ storageManagerId, setState }}>
71-
<MiqFormRenderer
72-
schema={createSchema(!!recordId, !!storageManagerId, initialValues, state, setState)}
73-
componentMapper={componentMapper}
74-
initialValues={initialValues}
75-
canReset={!!recordId}
76-
onSubmit={onSubmit}
77-
onReset={() => add_flash(__('All changes have been reset'), 'warn')}
78-
onCancel={onCancel}
79-
buttonsLabels={{ submitLabel }}
80-
/>
81-
</EditingContext.Provider>
82-
) }
83-
</div>
85+
const schema = createSchema(fields, !!recordId, !!storageManagerId, loadSchema, emptySchema);
86+
const requiredCapabilities = schema.fields.find((field) => field.name === 'required_capabilities');
87+
if (requiredCapabilities) {
88+
delete requiredCapabilities.resolveProps;
89+
}
90+
91+
return !isLoading && (
92+
<MiqFormRenderer
93+
schema={schema}
94+
componentMapper={componentMapper}
95+
initialValues={initialValues}
96+
canReset={!!recordId}
97+
validate={validation}
98+
onSubmit={onSubmit}
99+
onReset={() => add_flash(__('All changes have been reset'), 'warn')}
100+
onCancel={onCancel}
101+
buttonsLabels={{ submitLabel }}
102+
/>
84103
);
85104
};
86105

app/javascript/components/storage-service-form/storage-service-form.schema.js

Lines changed: 60 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,71 @@
11
import { componentTypes, validatorTypes } from '@@ddf';
2+
import { parseCondition } from '@data-driven-forms/react-form-renderer';
23
import validateName from '../../helpers/storage_manager/validate-names';
3-
import { loadProviderCapabilities } from '../../helpers/storage_manager/load-provider-capabilities';
44
import filterResourcesByCapabilities from '../../helpers/storage_manager/filter-resources-by-capabilities';
55

6-
const loadProviders = () =>
7-
API.get(
8-
'/api/providers?expand=resources&attributes=id,name,supports_block_storage'
9-
+ '&filter[]=supports_block_storage=true&filter[]=supports_add_storage=true',
10-
).then(({ resources }) =>
11-
resources.map(({ id, name }) => ({ value: id, label: name })));
6+
const changeValue = (value, loadSchema, emptySchema) => {
7+
if (value === '-1') {
8+
emptySchema();
9+
} else {
10+
miqSparkleOn();
11+
API.options(`/api/storage_services?ems_id=${value}`).then(loadSchema()).then(miqSparkleOff);
12+
}
13+
};
14+
15+
const storageManagers = (supports) =>
16+
API.get(`/api/providers?expand=resources&attributes=id,name,${supports}&filter[]=${supports}=true`)
17+
.then(({ resources }) => {
18+
const storageManagersOptions = resources.map(({ id, name }) => ({ label: name, value: id }));
19+
storageManagersOptions.unshift({ label: `<${__('Choose')}>`, value: '-1' });
20+
return storageManagersOptions;
21+
});
1222

1323
const getProviderCapabilities = async(providerId) => API.get(`/api/providers/${providerId}?attributes=capabilities`)
1424
.then((result) => result.capabilities);
1525

16-
const createSchema = (edit, ems, initialValues, state, setState) => {
26+
const createSchema = (fields, edit, ems, loadSchema, emptySchema) => {
27+
const idx = fields.findIndex((field) => field.name === 'required_capabilities');
28+
const supports = edit ? 'supports_storage_service' : 'supports_storage_service_create';
1729
let providerCapabilities;
18-
let emsId = state.ems_id;
19-
if (initialValues && initialValues.ems_id) {
20-
emsId = initialValues.ems_id;
21-
}
2230

2331
return ({
2432
fields: [
2533
{
2634
component: componentTypes.SELECT,
2735
name: 'ems_id',
28-
key: `${emsId}`,
2936
id: 'ems_id',
3037
label: __('Storage Manager'),
31-
placeholder: __('<Choose>'),
32-
onChange: (value) => {
33-
emsId = value;
34-
return setState({ ...state, ems_id: value });
35-
},
36-
loadOptions: loadProviders,
38+
onChange: (value) => changeValue(value, loadSchema, emptySchema),
39+
loadOptions: () => storageManagers(supports),
3740
isDisabled: edit || ems,
3841
isRequired: true,
39-
includeEmpty: true,
4042
validate: [{ type: validatorTypes.REQUIRED }],
4143
},
4244
{
4345
component: componentTypes.TEXT_FIELD,
4446
name: 'name',
4547
id: 'name',
46-
label: __('Name:'),
48+
label: __('Service Name'),
4749
isRequired: true,
4850
validate: [
49-
{ type: validatorTypes.REQUIRED },
51+
{
52+
type: validatorTypes.REQUIRED,
53+
},
54+
{
55+
type: 'pattern',
56+
pattern: '^[a-zA-Z0-9-_. ]*$',
57+
message: __('The name can contain letters, numbers, spaces, periods, dashes and underscores'),
58+
},
59+
{
60+
type: 'pattern',
61+
pattern: '^[^ ]+( +[^ ]+)*$',
62+
message: __('The name must not begin or end with a space'),
63+
},
64+
{
65+
type: 'pattern',
66+
pattern: '^[a-zA-Z_]',
67+
message: __('The name must begin with a letter or an underscore'),
68+
},
5069
async(value) => validateName('storage_services', value, edit),
5170
],
5271
},
@@ -57,25 +76,22 @@ const createSchema = (edit, ems, initialValues, state, setState) => {
5776
label: __('Description:'),
5877
isRequired: false,
5978
},
60-
{
61-
component: componentTypes.SELECT,
62-
name: 'required_capabilities',
63-
id: 'required_capabilities',
64-
label: __('Required Capabilities'),
65-
placeholder: __('<Choose>'),
66-
loadOptions: () => (emsId ? loadProviderCapabilities(emsId) : Promise.resolve([])),
67-
isDisabled: edit,
68-
isRequired: true,
69-
isMulti: true,
70-
validate: [{ type: validatorTypes.REQUIRED }],
71-
condition: { when: 'ems_id', isNotEmpty: true },
72-
},
79+
...(idx === -1 ? fields : [
80+
...fields.slice(0, idx),
81+
{
82+
...fields[idx],
83+
resolveProps: ({ options }, _input, { getState }) => ({
84+
options: options.filter(({ condition }) => !condition || parseCondition(condition, getState().values).result),
85+
}),
86+
},
87+
...fields.slice(idx + 1),
88+
]),
7389
{
7490
component: 'enhanced-select',
7591
name: 'storage_resource_id',
7692
id: 'storage_resource_id',
7793
label: __('Storage Resources'),
78-
condition: { when: 'required_capabilities', isNotEmpty: true },
94+
condition: { when: 'compression', isNotEmpty: true },
7995
onInputChange: () => null,
8096
isRequired: true,
8197
helperText: __('Select storage resources to attach to the new service. Volumes for this service will be created on these resources.'),
@@ -85,8 +101,14 @@ const createSchema = (edit, ems, initialValues, state, setState) => {
85101
],
86102
isMulti: true,
87103
resolveProps: (_props, _field, { getState }) => {
88-
const capabilityValues = getState().values.required_capabilities.map(({ value }) => value);
89-
const emsId = getState().values.ems_id;
104+
const stateValues = getState().values;
105+
const emsId = stateValues.ems_id;
106+
const capabilityValues = [];
107+
108+
const capabilityNames = fields.find((object) => object.id === 'required_capabilities')
109+
.fields.map((capability) => capability.id);
110+
capabilityNames.forEach((capabilityName) => capabilityValues.push(stateValues[capabilityName]));
111+
90112
return {
91113
key: JSON.stringify(capabilityValues),
92114
loadOptions: async() => {

0 commit comments

Comments
 (0)