mirror of
https://github.com/strapi/strapi.git
synced 2025-09-20 22:10:06 +00:00
Add components select
Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
parent
97950e25b1
commit
25065ba0c8
@ -1,5 +1,3 @@
|
||||
export const ADD_COMPONENTS_TO_DYNAMIC_ZONE =
|
||||
'ContentTypeBuilder/FormModal/ADD_COMPONENTS_TO_DYNAMIC_ZONE';
|
||||
export const ON_CHANGE = 'ContentTypeBuilder/FormModal/ON_CHANGE';
|
||||
export const ON_CHANGE_RELATION_TARGET = 'ContentTypeBuilder/FormModal/ON_CHANGE_RELATION_TARGET';
|
||||
export const ON_CHANGE_RELATION_TYPE = 'ContentTypeBuilder/FormModal/ON_CHANGE_RELATION_TYPE';
|
||||
|
@ -35,7 +35,7 @@ const form = {
|
||||
},
|
||||
{
|
||||
name: 'components',
|
||||
type: 'componentSelect',
|
||||
type: 'select-components',
|
||||
intlLabel: {
|
||||
id: getTrad('modalForm.attributes.select-components'),
|
||||
defaultMessage: 'Select the components',
|
||||
|
@ -37,6 +37,7 @@ import ComponentIconPicker from '../ComponentIconPicker';
|
||||
import Relation from '../Relation';
|
||||
import SelectCategory from '../SelectCategory';
|
||||
import SelectComponent from '../SelectComponent';
|
||||
import SelectComponents from '../SelectComponents';
|
||||
// import SelectCategory from '../WrapperSelect';
|
||||
// import WrapperSelect from '../WrapperSelect';
|
||||
import findAttribute from '../../utils/findAttribute';
|
||||
@ -59,7 +60,6 @@ import {
|
||||
SET_DATA_TO_EDIT,
|
||||
SET_DYNAMIC_ZONE_DATA_SCHEMA,
|
||||
SET_ATTRIBUTE_DATA_SCHEMA,
|
||||
// ADD_COMPONENTS_TO_DYNAMIC_ZONE,
|
||||
SET_ERRORS,
|
||||
ON_CHANGE,
|
||||
RESET_PROPS_AND_SET_THE_FORM_FOR_ADDING_A_COMPO_TO_A_DZ,
|
||||
@ -554,17 +554,6 @@ const FormModal = () => {
|
||||
}, '');
|
||||
};
|
||||
|
||||
// const handleClickAddComponentsToDynamicZone = ({
|
||||
// target: { name, components, shouldAddComponents },
|
||||
// }) => {
|
||||
// dispatch({
|
||||
// type: ADD_COMPONENTS_TO_DYNAMIC_ZONE,
|
||||
// name,
|
||||
// components,
|
||||
// shouldAddComponents,
|
||||
// });
|
||||
// };
|
||||
|
||||
const handleChange = useCallback(
|
||||
({ target: { name, value, type, ...rest } }) => {
|
||||
const namesThatCanResetToNullValue = [
|
||||
@ -1099,11 +1088,13 @@ const FormModal = () => {
|
||||
relation: Relation,
|
||||
'select-category': SelectCategory,
|
||||
'select-component': SelectComponent,
|
||||
'select-components': SelectComponents,
|
||||
'select-default-boolean': BooleanDefaultValueSelect,
|
||||
'toggle-draft-publish': DraftAndPublishToggle,
|
||||
...inputsFromPlugins,
|
||||
},
|
||||
componentToCreate,
|
||||
dynamicZoneTarget: state.dynamicZoneTarget,
|
||||
formErrors,
|
||||
isAddingAComponentToAnotherComponent,
|
||||
isCreatingComponentWhileAddingAField,
|
||||
|
@ -3,7 +3,6 @@ import pluralize from 'pluralize';
|
||||
import set from 'lodash/set';
|
||||
import snakeCase from 'lodash/snakeCase';
|
||||
import getRelationType from '../../utils/getRelationType';
|
||||
import makeUnique from '../../utils/makeUnique';
|
||||
import { createComponentUid } from './utils/createUid';
|
||||
import { shouldPluralizeName, shouldPluralizeTargetAttribute } from './utils/relations';
|
||||
import * as actions from './constants';
|
||||
@ -20,22 +19,6 @@ const reducer = (state = initialState, action) =>
|
||||
// eslint-disable-next-line consistent-return
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE: {
|
||||
const { components, shouldAddComponents } = action;
|
||||
let currentList = [];
|
||||
|
||||
if (shouldAddComponents) {
|
||||
currentList = [...state.modifiedData.components, ...components];
|
||||
} else {
|
||||
currentList = state.modifiedData.components.filter(comp => {
|
||||
return components.indexOf(comp) === -1;
|
||||
});
|
||||
}
|
||||
|
||||
draftState.modifiedData.components = makeUnique(currentList);
|
||||
|
||||
break;
|
||||
}
|
||||
case actions.ON_CHANGE: {
|
||||
const { keys, value } = action;
|
||||
const obj = state.modifiedData;
|
||||
|
@ -2,66 +2,6 @@ import reducer, { initialState } from '../reducer';
|
||||
import * as actions from '../constants';
|
||||
|
||||
describe('CTB | components | FormModal | reducer | actions', () => {
|
||||
describe('ADD_COMPONENTS_TO_DYNAMIC_ZONE', () => {
|
||||
it('Should add the components correctly', () => {
|
||||
const action = {
|
||||
type: actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE,
|
||||
components: ['default.test', 'default.test2', 'default.test3'],
|
||||
shouldAddComponents: true,
|
||||
name: 'components',
|
||||
};
|
||||
|
||||
const state = {
|
||||
initialData: {},
|
||||
modifiedData: {
|
||||
type: 'dynamiczone',
|
||||
name: 'dz',
|
||||
components: ['default.test'],
|
||||
},
|
||||
};
|
||||
|
||||
const expected = {
|
||||
initialData: {},
|
||||
modifiedData: {
|
||||
type: 'dynamiczone',
|
||||
name: 'dz',
|
||||
components: ['default.test', 'default.test2', 'default.test3'],
|
||||
},
|
||||
};
|
||||
|
||||
expect(reducer(state, action)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('Should remove the components correctly', () => {
|
||||
const action = {
|
||||
type: actions.ADD_COMPONENTS_TO_DYNAMIC_ZONE,
|
||||
components: ['default.test2', 'default.test3'],
|
||||
shouldAddComponents: false,
|
||||
name: 'components',
|
||||
};
|
||||
|
||||
const state = {
|
||||
initialData: {},
|
||||
modifiedData: {
|
||||
type: 'dynamiczone',
|
||||
name: 'dz',
|
||||
components: ['default.test', 'default.test2', 'default.test3'],
|
||||
},
|
||||
};
|
||||
|
||||
const expected = {
|
||||
initialData: {},
|
||||
modifiedData: {
|
||||
type: 'dynamiczone',
|
||||
name: 'dz',
|
||||
components: ['default.test'],
|
||||
},
|
||||
};
|
||||
|
||||
expect(reducer(state, action)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe(actions.ON_CHANGE, () => {
|
||||
it('Should update the modifiedData object correctly', () => {
|
||||
const action = {
|
||||
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Tests for SelectComponent
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { ThemeProvider, lightTheme } from '@strapi/parts';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import SelectComponent from '../index';
|
||||
|
||||
const messages = {
|
||||
'content-type-builder.component.name': 'Select Component',
|
||||
};
|
||||
|
||||
describe('<SelectComponent />', () => {
|
||||
it('renders and matches the snapshot', () => {
|
||||
const {
|
||||
container: { firstChild },
|
||||
} = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<IntlProvider locale="en" messages={messages} defaultLocale="en">
|
||||
<SelectComponent />
|
||||
</IntlProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
||||
expect(firstChild).toMatchInlineSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,85 @@
|
||||
/**
|
||||
*
|
||||
* SelectComponents
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { MultiSelectNested } from '@strapi/parts/Select';
|
||||
import { useIntl } from 'react-intl';
|
||||
import useDataManager from '../../hooks/useDataManager';
|
||||
import findAttribute from '../../utils/findAttribute';
|
||||
import { getTrad } from '../../utils';
|
||||
|
||||
const SelectComponents = ({ dynamicZoneTarget, intlLabel, name, onChange, value }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const { componentsGroupedByCategory, modifiedData } = useDataManager();
|
||||
const dzSchema =
|
||||
findAttribute(modifiedData.contentType.schema.attributes, dynamicZoneTarget) || {};
|
||||
const alreadyUsedComponents = dzSchema.components || [];
|
||||
const filteredComponentsGroupedByCategory = Object.keys(componentsGroupedByCategory).reduce(
|
||||
(acc, current) => {
|
||||
const filteredComponents = componentsGroupedByCategory[current].filter(({ uid }) => {
|
||||
return !alreadyUsedComponents.includes(uid);
|
||||
});
|
||||
|
||||
if (filteredComponents.length > 0) {
|
||||
acc[current] = filteredComponents;
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
const options = Object.entries(filteredComponentsGroupedByCategory).reduce((acc, current) => {
|
||||
const [categoryName, components] = current;
|
||||
const section = {
|
||||
label: categoryName,
|
||||
children: components.map(({ uid, schema: { name } }) => {
|
||||
return { label: name, value: uid };
|
||||
}),
|
||||
};
|
||||
|
||||
acc.push(section);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
const displayedValue = formatMessage(
|
||||
{
|
||||
id: getTrad('components.SelectComponents.displayed-value'),
|
||||
defaultMessage:
|
||||
'{number, plural, =0 {# components} one {# component} other {# components}} selected',
|
||||
},
|
||||
{ number: value.length }
|
||||
);
|
||||
|
||||
return (
|
||||
<MultiSelectNested
|
||||
id="select1"
|
||||
label={formatMessage(intlLabel)}
|
||||
customizeContent={() => displayedValue}
|
||||
name={name}
|
||||
onChange={values => {
|
||||
onChange({ target: { name, value: values, type: 'select-components' } });
|
||||
}}
|
||||
options={options}
|
||||
value={value || []}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
SelectComponents.propTypes = {
|
||||
intlLabel: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
values: PropTypes.object,
|
||||
}).isRequired,
|
||||
dynamicZoneTarget: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
value: PropTypes.array.isRequired,
|
||||
};
|
||||
|
||||
export default SelectComponents;
|
@ -190,5 +190,6 @@
|
||||
"table.button.no-fields": "Add new field",
|
||||
"table.headers.name": "Name",
|
||||
"table.headers.type": "Type",
|
||||
"ComponentIconPicker.search.placeholder": "Search for an icon"
|
||||
"ComponentIconPicker.search.placeholder": "Search for an icon",
|
||||
"components.SelectComponents.displayed-value": "{number, plural, =0 {# components} one {# component} other {# components}} selected"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user