mirror of
https://github.com/strapi/strapi.git
synced 2025-09-20 14:00:48 +00:00
Created TabForm component
Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
parent
7148972f00
commit
ae94a75d93
@ -4,7 +4,7 @@ import { Text } from '@strapi/parts/Text';
|
|||||||
|
|
||||||
const Wrapper = styled(Stack)`
|
const Wrapper = styled(Stack)`
|
||||||
position: relative;
|
position: relative;
|
||||||
align--items: stretch;
|
align-items: stretch;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
GenericInput,
|
|
||||||
getYupInnerErrors,
|
getYupInnerErrors,
|
||||||
useTracking,
|
useTracking,
|
||||||
useNotification,
|
useNotification,
|
||||||
@ -16,9 +15,8 @@ import AddIcon from '@strapi/icons/AddIcon';
|
|||||||
import { Box } from '@strapi/parts/Box';
|
import { Box } from '@strapi/parts/Box';
|
||||||
import { Button } from '@strapi/parts/Button';
|
import { Button } from '@strapi/parts/Button';
|
||||||
import { Divider } from '@strapi/parts/Divider';
|
import { Divider } from '@strapi/parts/Divider';
|
||||||
import { Grid, GridItem } from '@strapi/parts/Grid';
|
|
||||||
import { ModalLayout, ModalBody, ModalFooter } from '@strapi/parts/ModalLayout';
|
import { ModalLayout, ModalBody, ModalFooter } from '@strapi/parts/ModalLayout';
|
||||||
import { H2, H3 } from '@strapi/parts/Text';
|
import { H2 } from '@strapi/parts/Text';
|
||||||
import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/parts/Tabs';
|
import { Tabs, Tab, TabGroup, TabPanels, TabPanel } from '@strapi/parts/Tabs';
|
||||||
import { Row } from '@strapi/parts/Row';
|
import { Row } from '@strapi/parts/Row';
|
||||||
import { Stack } from '@strapi/parts/Stack';
|
import { Stack } from '@strapi/parts/Stack';
|
||||||
@ -31,8 +29,10 @@ import DraftAndPublishToggle from '../DraftAndPublishToggle';
|
|||||||
import FormModalHeader from '../FormModalHeader';
|
import FormModalHeader from '../FormModalHeader';
|
||||||
|
|
||||||
import BooleanDefaultValueSelect from '../BooleanDefaultValueSelect';
|
import BooleanDefaultValueSelect from '../BooleanDefaultValueSelect';
|
||||||
|
import CheckboxWithNumberField from '../CheckboxWithNumberField';
|
||||||
import CustomRadioGroup from '../CustomRadioGroup';
|
import CustomRadioGroup from '../CustomRadioGroup';
|
||||||
import ContentTypeRadioGroup from '../ContentTypeRadioGroup';
|
import ContentTypeRadioGroup from '../ContentTypeRadioGroup';
|
||||||
|
import TabForm from '../TabForm';
|
||||||
// import ComponentIconPicker from '../ComponentIconPicker';
|
// import ComponentIconPicker from '../ComponentIconPicker';
|
||||||
// import RelationForm from '../RelationForm';
|
// import RelationForm from '../RelationForm';
|
||||||
// import WrapperSelect from '../WrapperSelect';
|
// import WrapperSelect from '../WrapperSelect';
|
||||||
@ -51,7 +51,6 @@ import {
|
|||||||
import forms from './forms';
|
import forms from './forms';
|
||||||
import { createComponentUid, createUid } from './utils/createUid';
|
import { createComponentUid, createUid } from './utils/createUid';
|
||||||
import { INITIAL_STATE_DATA } from './utils/staticData';
|
import { INITIAL_STATE_DATA } from './utils/staticData';
|
||||||
// import CustomButton from './CustomButton';
|
|
||||||
import makeSelectFormModal from './selectors';
|
import makeSelectFormModal from './selectors';
|
||||||
import {
|
import {
|
||||||
SET_DATA_TO_EDIT,
|
SET_DATA_TO_EDIT,
|
||||||
@ -65,7 +64,6 @@ import {
|
|||||||
RESET_PROPS_AND_SAVE_CURRENT_DATA,
|
RESET_PROPS_AND_SAVE_CURRENT_DATA,
|
||||||
RESET_PROPS,
|
RESET_PROPS,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import CheckboxWithNumberField from '../CheckboxWithNumberField';
|
|
||||||
|
|
||||||
/* eslint-disable indent */
|
/* eslint-disable indent */
|
||||||
/* eslint-disable react/no-array-index-key */
|
/* eslint-disable react/no-array-index-key */
|
||||||
@ -1206,178 +1204,28 @@ const FormModal = () => {
|
|||||||
<TabPanels>
|
<TabPanels>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Stack size={6}>
|
<Stack size={6}>
|
||||||
{state.settingType === 'base' &&
|
{state.settingType === 'base' && (
|
||||||
baseForm.map((section, sectionIndex) => {
|
<TabForm
|
||||||
// Don't display an empty section
|
form={baseForm}
|
||||||
if (section.items.length === 0) {
|
formErrors={formErrors}
|
||||||
return null;
|
genericInputProps={genericInputProps}
|
||||||
}
|
modifiedData={modifiedData}
|
||||||
|
|
||||||
return (
|
|
||||||
<Box key={sectionIndex}>
|
|
||||||
{section.sectionTitle && (
|
|
||||||
<Box paddingBottom={4}>
|
|
||||||
<H3>{formatMessage(section.sectionTitle)}</H3>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Grid gap={4}>
|
|
||||||
{section.items.map((input, i) => {
|
|
||||||
const key = `${sectionIndex}.${i}`;
|
|
||||||
|
|
||||||
const retrievedValue = get(modifiedData, input.name, '');
|
|
||||||
|
|
||||||
let value;
|
|
||||||
|
|
||||||
if (input.name === 'enum' && Array.isArray(retrievedValue)) {
|
|
||||||
value = retrievedValue.join('\n');
|
|
||||||
} else if (input.name === 'uid') {
|
|
||||||
value = input.value;
|
|
||||||
} else if (
|
|
||||||
input.name === 'allowedTypes' &&
|
|
||||||
retrievedValue === ''
|
|
||||||
) {
|
|
||||||
value = null;
|
|
||||||
} else {
|
|
||||||
value = retrievedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When extending the yup schema of an existing field (like in https://github.com/strapi/strapi/blob/293ff3b8f9559236609d123a2774e3be05ce8274/packages/strapi-plugin-i18n/admin/src/index.js#L52)
|
|
||||||
// and triggering a yup validation error in the UI (missing a required field for example)
|
|
||||||
// We got an object that looks like: formErrors = { "pluginOptions.i18n.localized": {...} }
|
|
||||||
// In order to deal with this error, we can't rely on lodash.get to resolve this key
|
|
||||||
// - lodash will try to access {pluginOptions: {i18n: {localized: true}}})
|
|
||||||
// - and we just want to access { "pluginOptions.i18n.localized": {...} }
|
|
||||||
// NOTE: this is a hack
|
|
||||||
const pluginOptionError = Object.keys(formErrors).find(
|
|
||||||
key => key === input.name
|
|
||||||
);
|
|
||||||
|
|
||||||
// Retrieve the error for a specific input
|
|
||||||
const errorId = pluginOptionError
|
|
||||||
? formErrors[pluginOptionError].id
|
|
||||||
: get(
|
|
||||||
formErrors,
|
|
||||||
[
|
|
||||||
...input.name
|
|
||||||
.split('.')
|
|
||||||
// The filter here is used when creating a component
|
|
||||||
// in the component step 1 modal
|
|
||||||
// Since the component info is stored in the
|
|
||||||
// componentToCreate object we can access the error
|
|
||||||
// By removing the key
|
|
||||||
.filter(key => key !== 'componentToCreate'),
|
|
||||||
'id',
|
|
||||||
],
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GridItem col={input.size || 6} key={input.name || key}>
|
|
||||||
<GenericInput
|
|
||||||
{...input}
|
|
||||||
{...genericInputProps}
|
|
||||||
error={errorId}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value={value}
|
|
||||||
/>
|
/>
|
||||||
</GridItem>
|
)}
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel>
|
<TabPanel>
|
||||||
<Stack size={6}>
|
<Stack size={6}>
|
||||||
{state.settingType === 'advanced' &&
|
{state.settingType === 'advanced' && (
|
||||||
advancedForm.map((section, sectionIndex) => {
|
<TabForm
|
||||||
// Don't display an empty section
|
form={advancedForm}
|
||||||
if (section.items.length === 0) {
|
formErrors={formErrors}
|
||||||
return null;
|
genericInputProps={genericInputProps}
|
||||||
}
|
modifiedData={modifiedData}
|
||||||
|
|
||||||
return (
|
|
||||||
<Box key={sectionIndex}>
|
|
||||||
{section.sectionTitle && (
|
|
||||||
<Box paddingBottom={4}>
|
|
||||||
<H3>{formatMessage(section.sectionTitle)}</H3>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
<Grid gap={4}>
|
|
||||||
{section.items.map((input, i) => {
|
|
||||||
const key = `${sectionIndex}.${i}`;
|
|
||||||
|
|
||||||
let value;
|
|
||||||
|
|
||||||
const retrievedValue = get(modifiedData, input.name, '');
|
|
||||||
|
|
||||||
// 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 === 'enum' && Array.isArray(retrievedValue)) {
|
|
||||||
value = retrievedValue.join('\n');
|
|
||||||
} else if (input.name === 'uid') {
|
|
||||||
value = input.value;
|
|
||||||
} else if (
|
|
||||||
input.name === 'allowedTypes' &&
|
|
||||||
retrievedValue === ''
|
|
||||||
) {
|
|
||||||
value = null;
|
|
||||||
} else {
|
|
||||||
value = retrievedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When extending the yup schema of an existing field (like in https://github.com/strapi/strapi/blob/293ff3b8f9559236609d123a2774e3be05ce8274/packages/strapi-plugin-i18n/admin/src/index.js#L52)
|
|
||||||
// and triggering a yup validation error in the UI (missing a required field for example)
|
|
||||||
// We got an object that looks like: formErrors = { "pluginOptions.i18n.localized": {...} }
|
|
||||||
// In order to deal with this error, we can't rely on lodash.get to resolve this key
|
|
||||||
// - lodash will try to access {pluginOptions: {i18n: {localized: true}}})
|
|
||||||
// - and we just want to access { "pluginOptions.i18n.localized": {...} }
|
|
||||||
// NOTE: this is a hack
|
|
||||||
const pluginOptionError = Object.keys(formErrors).find(
|
|
||||||
key => key === input.name
|
|
||||||
);
|
|
||||||
|
|
||||||
// Retrieve the error for a specific input
|
|
||||||
const errorId = pluginOptionError
|
|
||||||
? formErrors[pluginOptionError].id
|
|
||||||
: get(
|
|
||||||
formErrors,
|
|
||||||
[
|
|
||||||
...input.name
|
|
||||||
.split('.')
|
|
||||||
// The filter here is used when creating a component
|
|
||||||
// in the component step 1 modal
|
|
||||||
// Since the component info is stored in the
|
|
||||||
// componentToCreate object we can access the error
|
|
||||||
// By removing the key
|
|
||||||
.filter(key => key !== 'componentToCreate'),
|
|
||||||
'id',
|
|
||||||
],
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<GridItem col={input.size || 6} key={input.name || key}>
|
|
||||||
<GenericInput
|
|
||||||
{...input}
|
|
||||||
{...genericInputProps}
|
|
||||||
error={errorId}
|
|
||||||
isCreating={isCreating}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
value={value}
|
|
||||||
/>
|
/>
|
||||||
</GridItem>
|
)}
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Stack>
|
</Stack>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
*
|
||||||
|
* TabForm
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { GenericInput } from '@strapi/helper-plugin';
|
||||||
|
import { Box } from '@strapi/parts/Box';
|
||||||
|
import { Grid, GridItem } from '@strapi/parts/Grid';
|
||||||
|
import { H3 } from '@strapi/parts/Text';
|
||||||
|
|
||||||
|
/* eslint-disable react/no-array-index-key */
|
||||||
|
const TabForm = ({ form, formErrors, genericInputProps, modifiedData, onChange }) => {
|
||||||
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
return form.map((section, sectionIndex) => {
|
||||||
|
// Don't display an empty section
|
||||||
|
if (section.items.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box key={sectionIndex}>
|
||||||
|
{section.sectionTitle && (
|
||||||
|
<Box paddingBottom={4}>
|
||||||
|
<H3>{formatMessage(section.sectionTitle)}</H3>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
<Grid gap={4}>
|
||||||
|
{section.items.map((input, i) => {
|
||||||
|
const key = `${sectionIndex}.${i}`;
|
||||||
|
|
||||||
|
const retrievedValue = get(modifiedData, input.name, '');
|
||||||
|
|
||||||
|
let value;
|
||||||
|
|
||||||
|
if (input.name === 'enum' && Array.isArray(retrievedValue)) {
|
||||||
|
value = retrievedValue.join('\n');
|
||||||
|
} else if (input.name === 'uid') {
|
||||||
|
value = input.value;
|
||||||
|
} else if (input.name === 'allowedTypes' && retrievedValue === '') {
|
||||||
|
value = null;
|
||||||
|
} else {
|
||||||
|
value = retrievedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When extending the yup schema of an existing field (like in https://github.com/strapi/strapi/blob/293ff3b8f9559236609d123a2774e3be05ce8274/packages/strapi-plugin-i18n/admin/src/index.js#L52)
|
||||||
|
// and triggering a yup validation error in the UI (missing a required field for example)
|
||||||
|
// We got an object that looks like: formErrors = { "pluginOptions.i18n.localized": {...} }
|
||||||
|
// In order to deal with this error, we can't rely on lodash.get to resolve this key
|
||||||
|
// - lodash will try to access {pluginOptions: {i18n: {localized: true}}})
|
||||||
|
// - and we just want to access { "pluginOptions.i18n.localized": {...} }
|
||||||
|
// NOTE: this is a hack
|
||||||
|
const pluginOptionError = Object.keys(formErrors).find(key => key === input.name);
|
||||||
|
|
||||||
|
// Retrieve the error for a specific input
|
||||||
|
const errorId = pluginOptionError
|
||||||
|
? formErrors[pluginOptionError].id
|
||||||
|
: get(
|
||||||
|
formErrors,
|
||||||
|
[
|
||||||
|
...input.name
|
||||||
|
.split('.')
|
||||||
|
// The filter here is used when creating a component
|
||||||
|
// in the component step 1 modal
|
||||||
|
// Since the component info is stored in the
|
||||||
|
// componentToCreate object we can access the error
|
||||||
|
// By removing the key
|
||||||
|
.filter(key => key !== 'componentToCreate'),
|
||||||
|
'id',
|
||||||
|
],
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GridItem col={input.size || 6} key={input.name || key}>
|
||||||
|
<GenericInput
|
||||||
|
{...input}
|
||||||
|
{...genericInputProps}
|
||||||
|
error={errorId}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
/>
|
||||||
|
</GridItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TabForm.propTypes = {
|
||||||
|
form: PropTypes.arrayOf(PropTypes.object).isRequired,
|
||||||
|
formErrors: PropTypes.object.isRequired,
|
||||||
|
genericInputProps: PropTypes.object.isRequired,
|
||||||
|
modifiedData: PropTypes.object.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TabForm;
|
Loading…
x
Reference in New Issue
Block a user