Add custom select for default boolean

Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
soupette 2021-10-11 18:24:12 +02:00
parent c2c5af77c4
commit f6e3ba8399
4 changed files with 151 additions and 71 deletions

View File

@ -0,0 +1,84 @@
/**
*
* BooleanDefaultValueSelect
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from 'react-intl';
import { Select, Option } from '@strapi/parts/Select';
const BooleanDefaultValueSelect = ({ intlLabel, name, options, onChange, value }) => {
const { formatMessage } = useIntl();
const label = intlLabel.id
? formatMessage(
{ id: intlLabel.id, defaultMessage: intlLabel.defaultMessage },
{ ...intlLabel.values }
)
: name;
const handleChange = value => {
let nextValue = '';
if (value === 'true') {
nextValue = true;
}
if (value === 'false') {
nextValue = false;
}
onChange({ target: { name, value: nextValue, type: 'select-default-boolean' } });
};
return (
<Select
label={label}
id={name}
name={name}
onChange={handleChange}
value={(value === null ? '' : value).toString()}
>
{options.map(({ metadatas: { intlLabel, disabled, hidden }, key, value }) => {
return (
<Option key={key} value={value} disabled={disabled} hidden={hidden}>
{/* No need to translate the options */}
{intlLabel.defaultMessage}
</Option>
);
})}
</Select>
);
};
BooleanDefaultValueSelect.defaultProps = {
value: null,
};
BooleanDefaultValueSelect.propTypes = {
intlLabel: PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
values: PropTypes.object,
}).isRequired,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
options: PropTypes.arrayOf(
PropTypes.shape({
metadatas: PropTypes.shape({
intlLabel: PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
}).isRequired,
disabled: PropTypes.bool,
hidden: PropTypes.bool,
}).isRequired,
key: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
}).isRequired
).isRequired,
value: PropTypes.any,
};
export default BooleanDefaultValueSelect;

View File

@ -0,0 +1,24 @@
/**
*
* Tests for BooleanDefaultValueSelect
*
*/
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider, lightTheme } from '@strapi/parts';
import BooleanDefaultValueSelect from '../index';
describe('<BooleanDefaultValueSelect />', () => {
it('renders and matches the snapshot', () => {
const {
container: { firstChild },
} = render(
<ThemeProvider theme={lightTheme}>
<BooleanDefaultValueSelect />
</ThemeProvider>
);
expect(firstChild).toMatchInlineSnapshot();
});
});

View File

@ -14,18 +14,29 @@ const advancedForm = {
items: [
{
autoFocus: true,
type: 'enum',
type: 'select-default-boolean',
intlLabel: {
id: getTrad('form.attribute.settings.default'),
defaultMessage: 'Default value',
},
name: 'default',
options: [],
// options: [
// { value: 'true', label: 'TRUE' },
// { value: '', label: 'NULL' },
// { value: 'false', label: 'FALSE' },
// ],
options: [
{
value: 'true',
key: 'true',
metadatas: { intlLabel: { id: 'true', defaultMessage: 'true' } },
},
{
value: '',
key: 'null',
metadatas: { intlLabel: { id: 'null', defaultMessage: 'null' } },
},
{
value: 'false',
key: 'false',
metadatas: { intlLabel: { id: 'false', defaultMessage: 'false' } },
},
],
// validations: {},
},
],

View File

@ -1,12 +1,5 @@
import React, { useCallback, useEffect, useState, useMemo } from 'react';
import {
// HeaderModal,
// HeaderModalTitle,
// Modal,
// ModalBody,
// ModalFooter,
// ModalForm,
GenericInput,
getYupInnerErrors,
useTracking,
@ -18,7 +11,6 @@ import { useIntl } from 'react-intl';
import { useHistory, useLocation } from 'react-router-dom';
import { get, has, isEmpty, set, toLower } from 'lodash';
import upperFirst from 'lodash/upperFirst';
import toString from 'lodash/toString';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import AddIcon from '@strapi/icons/AddIcon';
import { Box } from '@strapi/parts/Box';
@ -37,6 +29,7 @@ import AttributeOptions from '../AttributeOptions';
import DraftAndPublishToggle from '../DraftAndPublishToggle';
import FormModalHeader from '../FormModalHeader';
import BooleanDefaultValueSelect from '../BooleanDefaultValueSelect';
import CustomRadioGroup from '../CustomRadioGroup';
import ContentTypeRadioGroup from '../ContentTypeRadioGroup';
// import ComponentIconPicker from '../ComponentIconPicker';
@ -65,8 +58,8 @@ import {
SET_DATA_TO_EDIT,
SET_DYNAMIC_ZONE_DATA_SCHEMA,
SET_ATTRIBUTE_DATA_SCHEMA,
ADD_COMPONENTS_TO_DYNAMIC_ZONE,
ON_CHANGE_ALLOWED_TYPE,
// ADD_COMPONENTS_TO_DYNAMIC_ZONE,
// ON_CHANGE_ALLOWED_TYPE,
SET_ERRORS,
ON_CHANGE,
RESET_PROPS_AND_SET_THE_FORM_FOR_ADDING_A_COMPO_TO_A_DZ,
@ -561,24 +554,24 @@ const FormModal = () => {
}, '');
};
const handleClickAddComponentsToDynamicZone = ({
target: { name, components, shouldAddComponents },
}) => {
dispatch({
type: ADD_COMPONENTS_TO_DYNAMIC_ZONE,
name,
components,
shouldAddComponents,
});
};
// const handleClickAddComponentsToDynamicZone = ({
// target: { name, components, shouldAddComponents },
// }) => {
// dispatch({
// type: ADD_COMPONENTS_TO_DYNAMIC_ZONE,
// name,
// components,
// shouldAddComponents,
// });
// };
const handleChangeMediaAllowedTypes = ({ target: { name, value } }) => {
dispatch({
type: ON_CHANGE_ALLOWED_TYPE,
name,
value,
});
};
// const handleChangeMediaAllowedTypes = ({ target: { name, value } }) => {
// dispatch({
// type: ON_CHANGE_ALLOWED_TYPE,
// name,
// value,
// });
// };
const handleChange = useCallback(
({ target: { name, value, type, ...rest } }) => {
@ -589,11 +582,12 @@ const FormModal = () => {
'maxLength',
'minLength',
'regex',
'default',
];
let val;
if (['default', ...namesThatCanResetToNullValue].includes(name) && value === '') {
if (namesThatCanResetToNullValue.includes(name) && value === '') {
val = null;
} else if (
type === 'radio' &&
@ -607,17 +601,6 @@ const FormModal = () => {
// The boolean default accepts 3 different values
// This check has been added to allow a reset to null for the bool
} else if (type === 'radio' && name === 'default') {
if (value === 'false') {
val = false;
} else if (value === 'true') {
val = true;
} else {
val = null;
}
// We store an array for the enum
// FIXME
} else if (name === 'enum') {
val = value.split('\n');
} else {
@ -1113,6 +1096,7 @@ const FormModal = () => {
const genericInputProps = {
customInputs: {
'select-default-boolean': BooleanDefaultValueSelect,
'radio-group': CustomRadioGroup,
'content-type-radio-group': ContentTypeRadioGroup,
'toggle-draft-publish': DraftAndPublishToggle,
@ -1233,21 +1217,7 @@ const FormModal = () => {
// FIXME
// Condition for the boolean default value
// The radio input doesn't accept false, true or null as value
// So we pass them as string
// This way the data stays accurate and we don't have to operate
// any data mutation
if (
input.name === 'default' &&
state.attributeType === 'boolean'
) {
value = toString(retrievedValue);
// Same here for the enum
} else if (
input.name === 'enum' &&
Array.isArray(retrievedValue)
) {
if (input.name === 'enum' && Array.isArray(retrievedValue)) {
value = retrievedValue.join('\n');
} else if (input.name === 'uid') {
value = input.value;
@ -1319,16 +1289,7 @@ const FormModal = () => {
// So we pass them as string
// This way the data stays accurate and we don't have to operate
// any data mutation
if (
input.name === 'default' &&
state.attributeType === 'boolean'
) {
value = toString(retrievedValue);
// Same here for the enum
} else if (
input.name === 'enum' &&
Array.isArray(retrievedValue)
) {
if (input.name === 'enum' && Array.isArray(retrievedValue)) {
value = retrievedValue.join('\n');
} else if (input.name === 'uid') {
value = input.value;