diff --git a/packages/core/content-manager/admin/src/pages/ListSettingsView/index.js b/packages/core/content-manager/admin/src/pages/ListSettingsView/index.js index 8aeaf31121..1c44194e11 100644 --- a/packages/core/content-manager/admin/src/pages/ListSettingsView/index.js +++ b/packages/core/content-manager/admin/src/pages/ListSettingsView/index.js @@ -32,7 +32,7 @@ const ListSettingsView = ({ layout, slug, updateLayout }) => { const { formatMessage } = useIntl(); const { emitEvent, updateMenu } = useGlobalContext(); const toggleModalForm = () => setIsModalFormOpen(prevState => !prevState); - const { labelForm, labelToEdit, initialData, modifiedData } = reducerState.toJS(); + const { labelForm, labelToEdit, initialData, modifiedData } = reducerState; const attributes = useMemo(() => { return get(modifiedData, ['attributes'], {}); }, [modifiedData]); diff --git a/packages/core/content-manager/admin/src/pages/ListSettingsView/init.js b/packages/core/content-manager/admin/src/pages/ListSettingsView/init.js index ce429df993..545b8271a6 100644 --- a/packages/core/content-manager/admin/src/pages/ListSettingsView/init.js +++ b/packages/core/content-manager/admin/src/pages/ListSettingsView/init.js @@ -1,11 +1,9 @@ -import { fromJS } from 'immutable'; - const init = (initialState, layout) => { - return fromJS({ - ...initialState.toJS(), + return { + ...initialState, initialData: layout, modifiedData: layout, - }); + }; }; export default init; diff --git a/packages/core/content-manager/admin/src/pages/ListSettingsView/reducer.js b/packages/core/content-manager/admin/src/pages/ListSettingsView/reducer.js index 2939d56c93..a791e00ffe 100644 --- a/packages/core/content-manager/admin/src/pages/ListSettingsView/reducer.js +++ b/packages/core/content-manager/admin/src/pages/ListSettingsView/reducer.js @@ -1,77 +1,95 @@ -import { fromJS } from 'immutable'; +import produce, { current } from 'immer'; +import set from 'lodash/set'; +import get from 'lodash/get'; -const initialState = fromJS({ +const initialState = { labelForm: {}, labelToEdit: '', initialData: {}, modifiedData: {}, status: 'resolved', -}); - -const reducer = (state, action) => { - const layoutPath = ['modifiedData', 'layouts', 'list']; - - switch (action.type) { - case 'ADD_FIELD': - return state.updateIn(layoutPath, list => list.push(action.item)); - - case 'MOVE_FIELD': - return state.updateIn(['modifiedData', 'layouts', 'list'], list => { - return list - .delete(action.originalIndex) - .insert(action.atIndex, list.get(action.originalIndex)); - }); - case 'ON_CHANGE': - return state.updateIn(['modifiedData', ...action.keys.split('.')], () => action.value); - case 'ON_CHANGE_LABEL_METAS': - return state.updateIn(['labelForm', action.name], () => action.value); - case 'ON_RESET': - return state.update('modifiedData', () => state.get('initialData')); - case 'REMOVE_FIELD': { - const defaultSortByPath = ['modifiedData', 'settings', 'defaultSortBy']; - const defaultSortBy = state.getIn(defaultSortByPath); - const attrPath = ['modifiedData', 'layouts', 'list', action.index]; - const attrToBeRemoved = state.getIn(attrPath); - - const firstAttr = state.getIn(['modifiedData', 'layouts', 'list', 1]); - const firstAttrType = state.getIn(['modifiedData', 'attributes', firstAttr, 'type']); - const attrToSelect = - firstAttrType !== 'media' && firstAttrType !== 'richtext' ? firstAttr : 'id'; - - return state - .removeIn(['modifiedData', 'layouts', 'list', action.index]) - .updateIn(defaultSortByPath, () => { - if (attrToBeRemoved === defaultSortBy) { - return attrToSelect; - } - - return defaultSortBy; - }); - } - case 'SET_LABEL_TO_EDIT': - return state - .update('labelToEdit', () => action.labelToEdit) - .updateIn(['labelForm', 'label'], () => - state.getIn(['modifiedData', 'metadatas', action.labelToEdit, 'list', 'label']) - ) - .updateIn(['labelForm', 'sortable'], () => - state.getIn(['modifiedData', 'metadatas', action.labelToEdit, 'list', 'sortable']) - ); - case 'UNSET_LABEL_TO_EDIT': - return state.update('labelToEdit', () => '').update('labelForm', () => fromJS({})); - case 'SUBMIT_LABEL_FORM': { - const metaPath = ['modifiedData', 'metadatas', state.get('labelToEdit'), 'list']; - - return state - .updateIn([...metaPath, 'label'], () => state.getIn(['labelForm', 'label'])) - .updateIn([...metaPath, 'sortable'], () => state.getIn(['labelForm', 'sortable'])); - } - case 'SUBMIT_SUCCEEDED': - return state.update('initialData', () => state.get('modifiedData')); - default: - return state; - } }; +const reducer = (state = initialState, action) => + // eslint-disable-next-line consistent-return + produce(state, draftState => { + const layoutFieldListPath = ['modifiedData', 'layouts', 'list']; + switch (action.type) { + case 'ADD_FIELD': { + const layoutFieldList = get(state, layoutFieldListPath, []); + set(draftState, layoutFieldListPath, [...layoutFieldList, action.item]); + break; + } + case 'MOVE_FIELD': { + const layoutFieldList = get(state, layoutFieldListPath, []); + const { originalIndex, atIndex } = action; + + if ( + layoutFieldList.length > 1 && + originalIndex <= layoutFieldList.length && + atIndex <= layoutFieldList.length + ) { + const item = layoutFieldList.splice(action.originalIndex, 1); + layoutFieldList.splice(action.atIndex, 0, item[0]); + set(draftState, layoutFieldListPath, layoutFieldList); + } + break; + } + case 'ON_CHANGE': { + set(draftState, ['modifiedData', ...action.keys.split('.')], action.value); + break; + } + case 'ON_CHANGE_LABEL_METAS': { + set(draftState, ['labelForm', action.name], action.value); + break; + } + case 'ON_RESET': { + draftState.modifiedData = state.initialData; + break; + } + case 'REMOVE_FIELD': { + const layoutFieldList = get(state, layoutFieldListPath, []); + set( + draftState, + layoutFieldListPath, + layoutFieldList.filter((_, index) => action.index !== index) + ); + break; + } + case 'SET_LABEL_TO_EDIT': { + const { labelToEdit } = action; + draftState.labelToEdit = labelToEdit; + draftState.labelForm.label = get( + current(draftState), + ['modifiedData', 'metadatas', labelToEdit, 'list', 'label'], + '' + ); + draftState.labelForm.sortable = get( + current(draftState), + ['modifiedData', 'metadatas', labelToEdit, 'list', 'sortable'], + '' + ); + break; + } + case 'UNSET_LABEL_TO_EDIT': { + draftState.labelToEdit = ''; + draftState.labelForm = {}; + break; + } + case 'SUBMIT_LABEL_FORM': { + const fieldMetadataPath = ['modifiedData', 'metadatas', state.labelToEdit, 'list']; + set(draftState, [...fieldMetadataPath, 'label'], state.labelForm.label); + set(draftState, [...fieldMetadataPath, 'sortable'], state.labelForm.sortable); + break; + } + case 'SUBMIT_SUCCEEDED': { + draftState.initialData = state.modifiedData; + break; + } + default: + return draftState; + } + }); + export default reducer; export { initialState }; diff --git a/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/init.test.js b/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/init.test.js index 746c1edc28..0a0b26fce7 100644 --- a/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/init.test.js +++ b/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/init.test.js @@ -1,4 +1,3 @@ -import { fromJS } from 'immutable'; import init from '../init'; describe('CONTENT MANAGER | containers | ListSettingsView | init', () => { @@ -11,7 +10,7 @@ describe('CONTENT MANAGER | containers | ListSettingsView | init', () => { initialData: { foo: 'bar' }, }; - const result = init(fromJS(initialState), layout).toJS(); + const result = init(initialState, layout); expect(result).toEqual(expected); }); diff --git a/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/reducer.test.js b/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/reducer.test.js new file mode 100644 index 0000000000..b96be5eb78 --- /dev/null +++ b/packages/core/content-manager/admin/src/pages/ListSettingsView/tests/reducer.test.js @@ -0,0 +1,330 @@ +import reducer from '../reducer'; + +describe('CONTENT MANAGER | CONTAINERS | ListView | reducer', () => { + let state; + + beforeEach(() => { + state = { + labelForm: {}, + labelToEdit: '', + initialData: {}, + modifiedData: {}, + status: 'resolved', + }; + }); + + it('should handle the default action correctly', () => { + const expected = state; + + expect(reducer(state, {})).toEqual(expected); + }); + + describe('ADD_FIELD', () => { + it('should add a field to the layout correctly', () => { + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['title'], + }, + }, + }; + const action = { type: 'ADD_FIELD', item: 'title' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('MOVE_FIELD', () => { + it('should replace the title by the description and vice-versa', () => { + state.modifiedData = { + layouts: { + list: ['id', 'description', 'title'], + }, + }; + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['id', 'title', 'description'], + }, + }, + }; + const action = { type: 'MOVE_FIELD', atIndex: 1, originalIndex: 2 }; + + expect(reducer(state, action)).toEqual(expected); + }); + it('should not change the field list if 1 item', () => { + state.modifiedData = { + layouts: { + list: ['id'], + }, + }; + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['id'], + }, + }, + }; + const action = { type: 'MOVE_FIELD', atIndex: 1, originalIndex: 2 }; + + expect(reducer(state, action)).toEqual(expected); + }); + it('should not change the field list if indices are wrong', () => { + state.modifiedData = { + layouts: { + list: ['id', 'description', 'id'], + }, + }; + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['id', 'description', 'id'], + }, + }, + }; + const action = { type: 'MOVE_FIELD', atIndex: 5, originalIndex: 7 }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('ON_CHANGE', () => { + it('should set the value related to the passed keys', () => { + const expected = { + ...state, + modifiedData: { + settings: { + pageSize: 50, + }, + }, + }; + const action = { type: 'ON_CHANGE', keys: 'settings.pageSize', value: 50 }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('ON_CHANGE_LABEL_METAS', () => { + it('should set the attribute metas label in the label form', () => { + const expected = { + ...state, + labelForm: { + label: 'Cover', + }, + }; + const action = { type: 'ON_CHANGE_LABEL_METAS', name: 'label', value: 'Cover' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('ON_RESET', () => { + it('should set the current modified data to the initial state', () => { + state.modifiedData = { + layouts: { + list: ['id', 'description', 'title'], + }, + }; + const expected = { + ...state, + modifiedData: {}, + }; + const action = { type: 'ON_RESET' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('REMOVE_FIELD', () => { + it('should remove the field', () => { + state.modifiedData = { + layouts: { + list: ['id', 'description', 'title'], + }, + settings: { + defaultSortBy: 'id', + }, + }; + const expected = { + ...state, + modifiedData: { + layouts: { + list: ['id', 'title'], + }, + settings: { + defaultSortBy: 'id', + }, + }, + }; + const action = { type: 'REMOVE_FIELD', index: 1 }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('SET_LABEL_TO_EDIT', () => { + it('should set the label form data of the field to edit', () => { + state.modifiedData = { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }; + const expected = { + ...state, + labelToEdit: 'cover', + labelForm: { + label: 'Cover', + sortable: false, + }, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + }; + const action = { type: 'SET_LABEL_TO_EDIT', labelToEdit: 'cover' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('UNSET_LABEL_TO_EDIT', () => { + it('should unset the label to edit and clean the label form', () => { + state = { + ...state, + labelToEdit: 'cover', + labelForm: { + label: 'Cover', + sortable: false, + }, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + }; + const expected = { + ...state, + labelToEdit: '', + labelForm: {}, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + }; + const action = { type: 'UNSET_LABEL_TO_EDIT', labelToEdit: 'cover' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('SUBMIT_LABEL_FORM', () => { + it('should submit the label and the sortable value of the field to edit', () => { + state = { + ...state, + labelToEdit: 'cover', + labelForm: { + label: 'New Cover', + sortable: true, + }, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + }; + const expected = { + ...state, + labelToEdit: 'cover', + labelForm: { + label: 'New Cover', + sortable: true, + }, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'New Cover', + sortable: true, + }, + }, + }, + }, + }; + const action = { type: 'SUBMIT_LABEL_FORM', labelToEdit: 'cover' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); + + describe('SUBMIT_SUCCEEDED', () => { + it('should submit the label and the sortable value of the field to edit', () => { + state.modifiedData = { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }; + const expected = { + ...state, + initialData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + modifiedData: { + metadatas: { + cover: { + list: { + label: 'Cover', + sortable: false, + }, + }, + }, + }, + }; + const action = { type: 'SUBMIT_SUCCEEDED' }; + + expect(reducer(state, action)).toEqual(expected); + }); + }); +});