From ced4d741bdfc71889fc18c79e6c67faef2c9fb8c Mon Sep 17 00:00:00 2001 From: Irakli Chalagashvili Date: Mon, 15 May 2023 18:27:33 +0400 Subject: [PATCH 1/2] ENG-4852 implement virtual root support in appbuilder --- sass/pages/common/PageTreeCompact.scss | 4 + src/state/pages/actions.js | 9 ++ src/state/pages/reducer.js | 19 +++- src/state/pages/selectors.js | 1 + src/state/pages/types.js | 1 + src/ui/pages/common/PageTree.js | 33 +++++- src/ui/pages/common/PageTreeCompact.js | 104 ++++++++++-------- src/ui/pages/common/PageTreeContainer.js | 3 +- src/ui/pages/config/ContentPages.js | 4 + src/ui/pages/config/ContentPagesContainer.js | 3 +- test/state/pages/actions.test.js | 9 +- test/state/pages/reducer.test.js | 12 ++ test/state/pages/selectors.test.js | 6 + .../ui/pages/common/PageTreeContainer.test.js | 4 +- .../pages/config/ContentPageContainer.test.js | 4 +- 15 files changed, 163 insertions(+), 53 deletions(-) diff --git a/sass/pages/common/PageTreeCompact.scss b/sass/pages/common/PageTreeCompact.scss index 91b25a229..6f6f059a4 100644 --- a/sass/pages/common/PageTreeCompact.scss +++ b/sass/pages/common/PageTreeCompact.scss @@ -47,6 +47,10 @@ width: 40px; } + &__virtual-root { + cursor: not-allowed !important; // sass-lint:disable-line no-important + } + // sass-lint:disable force-element-nesting nesting-depth no-qualifying-elements class-name-format &.table > tbody > &__row--selected { &, diff --git a/src/state/pages/actions.js b/src/state/pages/actions.js index f35cf4730..86ce12c6e 100644 --- a/src/state/pages/actions.js +++ b/src/state/pages/actions.js @@ -21,6 +21,7 @@ import { MOVE_PAGE, SET_FREE_PAGES, SET_SELECTED_PAGE, REMOVE_PAGE, UPDATE_PAGE, SEARCH_PAGES, CLEAR_SEARCH, SET_REFERENCES_SELECTED_PAGE, CLEAR_TREE, BATCH_TOGGLE_EXPANDED, COLLAPSE_ALL, SET_DASHBOARD_PAGES, + SET_VIRTUAL_ROOT, } from 'state/pages/types'; import { HOMEPAGE_CODE, PAGE_STATUS_DRAFT, PAGE_STATUS_PUBLISHED, PAGE_STATUS_UNPUBLISHED, SEO_ENABLED } from 'state/pages/const'; import { history, ROUTE_PAGE_TREE, ROUTE_PAGE_CLONE, ROUTE_PAGE_ADD } from 'app-init/router'; @@ -167,6 +168,11 @@ export const setDashboardPages = pages => ({ }, }); +export const setVirtualRoot = virtualRoot => ({ + type: SET_VIRTUAL_ROOT, + payload: virtualRoot, +}); + const wrapApiCall = apiFunc => (...args) => async (dispatch) => { const response = await apiFunc(...args); const json = await response.json(); @@ -222,6 +228,9 @@ export const fetchPageTree = pageCode => async (dispatch) => { fetchPage(pageCode)(dispatch), fetchPageChildren(pageCode)(dispatch), ]); + const rootMetadata = responses[1] ? responses[1].metaData : {}; + const { virtualRoot } = rootMetadata; + dispatch(setVirtualRoot(virtualRoot)); return [responses[0].payload].concat(responses[1].payload); } const response = await fetchPageChildren(pageCode)(dispatch); diff --git a/src/state/pages/reducer.js b/src/state/pages/reducer.js index 902f5b615..9bcaddc2b 100644 --- a/src/state/pages/reducer.js +++ b/src/state/pages/reducer.js @@ -18,7 +18,9 @@ import { BATCH_TOGGLE_EXPANDED, COLLAPSE_ALL, SET_DASHBOARD_PAGES, + SET_VIRTUAL_ROOT, } from 'state/pages/types'; +import { HOMEPAGE_CODE } from './const'; // creates a map from an array const toMap = (array, propKey) => array.reduce((acc, page) => { @@ -138,9 +140,15 @@ const childrenMap = (state = {}, action = {}) => { const titlesMap = (state = {}, action = {}) => { switch (action.type) { case ADD_PAGES: { + const mapOfTitles = toMap(action.payload.pages, 'titles'); + if (mapOfTitles[HOMEPAGE_CODE]) { + Object.keys(mapOfTitles[HOMEPAGE_CODE]).forEach((key) => { + mapOfTitles[HOMEPAGE_CODE][key] = 'Root'; + }); + } return { ...state, - ...toMap(action.payload.pages, 'titles'), + ...mapOfTitles, }; } case UPDATE_PAGE: { @@ -285,6 +293,14 @@ export const dashboard = (state = [], action = {}) => { } }; +export const virtualRoot = (state = {}, action = {}) => { + switch (action.type) { + case SET_VIRTUAL_ROOT: + return action.payload; + default: return state; + } +}; + export default combineReducers({ map: reducer, childrenMap, @@ -295,4 +311,5 @@ export default combineReducers({ selected, search, dashboard, + virtualRoot, }); diff --git a/src/state/pages/selectors.js b/src/state/pages/selectors.js index a953d2bde..4c235ecc5 100644 --- a/src/state/pages/selectors.js +++ b/src/state/pages/selectors.js @@ -16,6 +16,7 @@ export const getViewPages = state => state.pages.viewPages; export const getSelectedPage = state => state.pages.selected; export const getSearchPagesRaw = state => state.pages.search; export const getDashboardPages = state => state.pages.dashboard; +export const getIsVirtualRootOn = state => state.pages.virtualRoot; export const getSearchPages = createSelector( [getSearchPagesRaw, getChildrenMap], diff --git a/src/state/pages/types.js b/src/state/pages/types.js index cbbc8a9e3..ef30e4e09 100644 --- a/src/state/pages/types.js +++ b/src/state/pages/types.js @@ -16,3 +16,4 @@ export const BATCH_TOGGLE_EXPANDED = 'pages/set-all-pages-expanded'; export const COLLAPSE_ALL = 'pages/collapse-all-pages'; export const SET_VIEWPAGES = 'pages/set-viewpages'; export const SET_DASHBOARD_PAGES = 'pages/set-dashboard-pages'; +export const SET_VIRTUAL_ROOT = 'pages/set-virtual-root'; diff --git a/src/ui/pages/common/PageTree.js b/src/ui/pages/common/PageTree.js index 95e9bd880..aaf42658e 100644 --- a/src/ui/pages/common/PageTree.js +++ b/src/ui/pages/common/PageTree.js @@ -16,13 +16,24 @@ import PublishPageModalContainer from 'ui/pages/common/PublishPageModalContainer import UnpublishPageModalContainer from 'ui/pages/common/UnpublishPageModalContainer'; import PageListSearchTable from 'ui/pages/list/PageListSearchTable'; import MovePageModalContainer from 'ui/pages/common/MovePageModalContainer'; -import { PAGE_MOVEMENT_OPTIONS } from 'state/pages/const'; +import { HOMEPAGE_CODE, PAGE_MOVEMENT_OPTIONS } from 'state/pages/const'; + +export const getIsRootAndVirtual = (page, virtualRootOn) => { + if (!page) { + return false; + } + if (page.code === HOMEPAGE_CODE && virtualRootOn) { + return true; + } + return false; +}; class PageTree extends Component { constructor(props) { super(props); this.handleDrop = this.handleDrop.bind(this); this.renderActionCell = this.renderActionCell.bind(this); + this.renderStatusCell = this.renderStatusCell.bind(this); } componentDidMount() { @@ -109,9 +120,7 @@ class PageTree extends Component { className: 'text-center PageTree__thead', style: { width: '10%' }, }, - Cell: ({ value }) => ( - - ), + Cell: this.renderStatusCell, cellAttributes: { className: 'text-center', }, @@ -150,6 +159,12 @@ class PageTree extends Component { } renderActionCell({ original: page }) { + const isRootAndVirtual = getIsRootAndVirtual(page, this.props.virtualRootOn); + + if (isRootAndVirtual) { + return null; + } + return ( + ); + } + render() { const { searchPages, @@ -258,6 +281,7 @@ PageTree.propTypes = { onSetColumnOrder: PropTypes.func, columnOrder: PropTypes.arrayOf(PropTypes.string), myGroupIds: PropTypes.arrayOf(PropTypes.string), + virtualRootOn: PropTypes.bool, }; PageTree.defaultProps = { @@ -270,6 +294,7 @@ PageTree.defaultProps = { onSetColumnOrder: () => {}, columnOrder: ['title', 'status', 'displayedInMenu'], myGroupIds: [], + virtualRootOn: false, }; export default PageTree; diff --git a/src/ui/pages/common/PageTreeCompact.js b/src/ui/pages/common/PageTreeCompact.js index 76d512272..56334d836 100644 --- a/src/ui/pages/common/PageTreeCompact.js +++ b/src/ui/pages/common/PageTreeCompact.js @@ -6,6 +6,7 @@ import PageStatusIcon from 'ui/pages/common/PageStatusIcon'; import TreeNodeExpandedIcon from 'ui/common/tree-node/TreeNodeExpandedIcon'; import RowSpinner from 'ui/pages/common/RowSpinner'; import { PAGE_STATUS_DRAFT, PAGE_STATUS_PUBLISHED, PAGE_STATUS_UNPUBLISHED } from 'state/pages/const'; +import { getIsRootAndVirtual } from './PageTree'; class PageTreeCompact extends Component { renderRows() { @@ -13,7 +14,7 @@ class PageTreeCompact extends Component { pages, onClickDetails, onClickAdd, onClickEdit, onClickConfigure, onClickClone, onClickDelete, onClickUnPublish, onClickPublish, onRowClick, onClickViewPublishedPage, onClickPreview, selectedPage, - domain, locale, loadOnPageSelect, myGroupIds, + domain, locale, loadOnPageSelect, myGroupIds, virtualRootOn, } = this.props; const handleClick = (handler, page) => (e) => { @@ -95,11 +96,15 @@ class PageTreeCompact extends Component { ); + const isRootAndVirtual = getIsRootAndVirtual(page, virtualRootOn); + return ( onRowClick(page)} + className={`PageTreeCompact__row ${selectedPage && selectedPage.code === page.code ? 'PageTreeCompact__row--selected' : ''} + ${isRootAndVirtual ? 'PageTreeCompact__virtual-root' : ''} + `} + onClick={() => (isRootAndVirtual ? null : onRowClick(page))} >
@@ -129,51 +134,62 @@ class PageTreeCompact extends Component { )}
- - - + { + isRootAndVirtual ? null : ( + + + + ) + } -
e.stopPropagation()} role="none"> - - - - - {onClickEdit && ( - - - + { + isRootAndVirtual ? null : ( +
e.stopPropagation()} role="none"> + + + + + {onClickEdit && ( + + + )} - {onClickConfigure && ( - - - + disabled={disableDueToLackOfGroupAccess} + > + + )} - {onClickDetails && ( - - - + {onClickDetails && ( + + + )} - - - - {renderDeleteItem()} - {changePublishStatus} - - - - {viewPublishedPage} - -
+ + + + {renderDeleteItem()} + {changePublishStatus} + + + + {viewPublishedPage} +
+
+ + ) + } ); @@ -222,6 +238,7 @@ PageTreeCompact.propTypes = { loadOnPageSelect: PropTypes.bool, className: PropTypes.string, myGroupIds: PropTypes.arrayOf(PropTypes.string), + virtualRootOn: PropTypes.bool, }; PageTreeCompact.defaultProps = { @@ -235,6 +252,7 @@ PageTreeCompact.defaultProps = { onRowClick: () => {}, className: '', myGroupIds: [], + virtualRootOn: false, }; export default PageTreeCompact; diff --git a/src/ui/pages/common/PageTreeContainer.js b/src/ui/pages/common/PageTreeContainer.js index 4579ebfff..1678dc028 100644 --- a/src/ui/pages/common/PageTreeContainer.js +++ b/src/ui/pages/common/PageTreeContainer.js @@ -29,7 +29,7 @@ import { import { setColumnOrder } from 'state/table-column-order/actions'; import { getColumnOrder } from 'state/table-column-order/selectors'; -import { getPageTreePages, getSearchPages } from 'state/pages/selectors'; +import { getIsVirtualRootOn, getPageTreePages, getSearchPages } from 'state/pages/selectors'; import { PAGE_INIT_VALUES } from 'ui/pages/common/const'; import { setAppTourLastStep } from 'state/app-tour/actions'; import { getDomain } from '@entando/apimanager'; @@ -47,6 +47,7 @@ export const mapStateToProps = state => ({ columnOrder: getColumnOrder(state, 'pageList'), pageSearchColumnOrder: getColumnOrder(state, 'pageSearch'), myGroupIds: getMyGroupsList(state), + virtualRootOn: getIsVirtualRootOn(state), }); export const mapDispatchToProps = (dispatch, ownProps) => ({ diff --git a/src/ui/pages/config/ContentPages.js b/src/ui/pages/config/ContentPages.js index bec0d0b1b..40b293d57 100644 --- a/src/ui/pages/config/ContentPages.js +++ b/src/ui/pages/config/ContentPages.js @@ -106,6 +106,7 @@ class ContentPages extends Component { const { loading, onExpandPage, pages, intl, searchPages, onClear, loadOnPageSelect, onLoadPage, myGroupIds, + virtualRootOn, } = this.props; const { expanded } = this.state; @@ -174,6 +175,7 @@ class ContentPages extends Component { {...this.props} className={loadOnPageSelect ? 'ContentPages__pagetree--loadable' : ''} pages={pages} + virtualRootOn={virtualRootOn} selectedPage={selectedPage} onExpandPage={onExpandPage} onRowClick={this.handlePageSelect} @@ -206,6 +208,7 @@ ContentPages.propTypes = { onLoadPage: PropTypes.func, onSearchPageChange: PropTypes.func.isRequired, myGroupIds: PropTypes.arrayOf(PropTypes.string), + virtualRootOn: PropTypes.bool, }; ContentPages.defaultProps = { onWillMount: () => {}, @@ -221,6 +224,7 @@ ContentPages.defaultProps = { loadOnPageSelect: true, onLoadPage: () => {}, myGroupIds: [], + virtualRootOn: false, }; export default injectIntl(ContentPages); diff --git a/src/ui/pages/config/ContentPagesContainer.js b/src/ui/pages/config/ContentPagesContainer.js index 7f3b07046..bae73ce90 100644 --- a/src/ui/pages/config/ContentPagesContainer.js +++ b/src/ui/pages/config/ContentPagesContainer.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; import { convertToQueryString, routeConverter } from '@entando/utils'; -import { getPageTreePages, getSearchPages, getSelectedPage } from 'state/pages/selectors'; +import { getIsVirtualRootOn, getPageTreePages, getSearchPages, getSelectedPage } from 'state/pages/selectors'; import { setVisibleModal, setInfo } from 'state/modal/actions'; import { MODAL_ID } from 'ui/pages/common/DeletePageModal'; import { MODAL_ID as UNPUBLISH_MODAL_ID } from 'ui/pages/common/UnpublishPageModal'; @@ -40,6 +40,7 @@ export const mapStateToProps = state => ({ pageSize: getPageSize(state), domain: getDomain(state), myGroupIds: getMyGroupsList(state), + virtualRootOn: getIsVirtualRootOn(state), }); export const mapDispatchToProps = dispatch => ({ diff --git a/test/state/pages/actions.test.js b/test/state/pages/actions.test.js index 132fff563..66ac625bb 100644 --- a/test/state/pages/actions.test.js +++ b/test/state/pages/actions.test.js @@ -14,13 +14,14 @@ import { fetchPageForm, sendPutPage, setFreePages, fetchFreePages, fetchPageSettings, publishSelectedPage, unpublishSelectedPage, loadSelectedPage, removePage, sendDeletePage, clearSearchPage, clearSearch, setReferenceSelectedPage, clonePage, clearTree, sendPutPageSettings, sendPatchPage, - fetchPageTreeAll, setBatchExpanded, fetchDashboardPages, + fetchPageTreeAll, setBatchExpanded, fetchDashboardPages, setVirtualRoot, } from 'state/pages/actions'; import { ADD_PAGES, SET_PAGE_LOADING, SET_PAGE_LOADED, SET_PAGE_EXPANDED, MOVE_PAGE, SET_PAGE_PARENT, SET_FREE_PAGES, SET_SELECTED_PAGE, REMOVE_PAGE, UPDATE_PAGE, CLEAR_SEARCH, SEARCH_PAGES, SET_REFERENCES_SELECTED_PAGE, CLEAR_TREE, BATCH_TOGGLE_EXPANDED, SET_DASHBOARD_PAGES, + SET_VIRTUAL_ROOT, } from 'state/pages/types'; import { SET_PUBLISHED_PAGE_CONFIG } from 'state/page-config/types'; @@ -200,6 +201,12 @@ describe('state/pages/actions', () => { expect(action.payload).toEqual({ pageCode: PAGE_CODE }); }); + it('setVirtualRoot() should return a well formed action', () => { + const action = setVirtualRoot(true); + expect(action.type).toBe(SET_VIRTUAL_ROOT); + expect(action.payload).toEqual(true); + }); + it('setPageExpanded() should return a well formed action', () => { const action = setPageExpanded(PAGE_CODE, true); expect(action.type).toBe(SET_PAGE_EXPANDED); diff --git a/test/state/pages/reducer.test.js b/test/state/pages/reducer.test.js index e03a85c89..ea6487686 100644 --- a/test/state/pages/reducer.test.js +++ b/test/state/pages/reducer.test.js @@ -9,6 +9,7 @@ import { addPages, setPageParentSync, movePageSync, setPageExpanded, setPageLoading, setPageLoaded, setFreePages, setSelectedPage, removePage, updatePage, setSearchPages, clearSearch, setReferenceSelectedPage, clearTree, collapseAll, setBatchExpanded, setDashboardPages, + setVirtualRoot, } from 'state/pages/actions'; import { HOMEPAGE_CODE } from 'state/pages/const'; @@ -157,6 +158,17 @@ describe('state/pages/reducer', () => { }); }); + describe('action SET_VIRTUAL_ROOT', () => { + let newState; + it('should set the virtual root to true', () => { + newState = reducer(state, setVirtualRoot(true)); + expect(newState.virtualRoot).toBe(true); + + newState = reducer(newState, setVirtualRoot(false)); + expect(newState.virtualRoot).toBe(false); + }); + }); + describe('action SET_PAGE_LOADING', () => { let newState; const PAGE_CODE = HOMEPAGE_CODE; diff --git a/test/state/pages/selectors.test.js b/test/state/pages/selectors.test.js index 727d65099..8d6d32aac 100644 --- a/test/state/pages/selectors.test.js +++ b/test/state/pages/selectors.test.js @@ -8,6 +8,7 @@ import { getPages, getPagesMap, getChildrenMap, getTitlesMap, getStatusMap, getPositionMap, getPageTreePages, getCharsets, getContentTypes, getFreePages, getReferencesFromSelectedPage, getSelectedPagePreviewURI, getSelectedPageLocaleTitle, getSelectedPublishedPageURI, + getIsVirtualRootOn, } from 'state/pages/selectors'; import { PREVIEW_NAMESPACE } from 'ui/pages/config/const'; @@ -56,6 +57,7 @@ const MOCK_STATE = { }, freePages: [], selected: { ...HOMEPAGE_PAYLOAD, references: { jacmsContentManager: true } }, + virtualRoot: true, }, }; @@ -90,6 +92,10 @@ describe('state/pages/selectors', () => { const selected = getStatusMap(MOCK_STATE); expect(selected).toBe(MOCK_STATE.pages.statusMap); }); + it('getIsVirtualRootOn(state) returns the virtualRoot boolean', () => { + const selected = getIsVirtualRootOn(MOCK_STATE); + expect(selected).toBe(MOCK_STATE.pages.virtualRoot); + }); describe('getPositionMap(state)', () => { it('returns a calculated position map', () => { diff --git a/test/ui/pages/common/PageTreeContainer.test.js b/test/ui/pages/common/PageTreeContainer.test.js index 6c8979564..59c57d0cf 100644 --- a/test/ui/pages/common/PageTreeContainer.test.js +++ b/test/ui/pages/common/PageTreeContainer.test.js @@ -2,7 +2,7 @@ import { mapStateToProps, mapDispatchToProps, } from 'ui/pages/common/PageTreeContainer'; -import { getPageTreePages, getSearchPages } from 'state/pages/selectors'; +import { getPageTreePages, getSearchPages, getIsVirtualRootOn } from 'state/pages/selectors'; import { setVisibleModal, setInfo } from 'state/modal/actions'; import { setSelectedPage, @@ -50,6 +50,7 @@ jest.mock('state/modal/actions', () => ({ jest.mock('state/pages/selectors', () => ({ getPageTreePages: jest.fn(), getSearchPages: jest.fn(), + getIsVirtualRootOn: jest.fn(), })); jest.mock('state/groups/selectors', () => ({ @@ -59,6 +60,7 @@ jest.mock('state/groups/selectors', () => ({ getMyGroupsList.mockReturnValue(['administrators', 'free']); getPageTreePages.mockReturnValue('pages'); getSearchPages.mockReturnValue([]); +getIsVirtualRootOn.mockReturnValue(false); const dispatchMock = jest.fn(); diff --git a/test/ui/pages/config/ContentPageContainer.test.js b/test/ui/pages/config/ContentPageContainer.test.js index c44f4c52f..e08ac167c 100644 --- a/test/ui/pages/config/ContentPageContainer.test.js +++ b/test/ui/pages/config/ContentPageContainer.test.js @@ -1,6 +1,6 @@ import { mapStateToProps, mapDispatchToProps } from 'ui/pages/config/ContentPagesContainer'; import { HOMEPAGE_PAYLOAD, SEARCH_PAGES } from 'test/mocks/pages'; -import { getPageTreePages, getSearchPages, getSelectedPage } from 'state/pages/selectors'; +import { getPageTreePages, getSearchPages, getSelectedPage, getIsVirtualRootOn } from 'state/pages/selectors'; import { getUserPreferences } from 'state/user-preferences/selectors'; import { getMyGroupsList } from 'state/groups/selectors'; @@ -8,6 +8,7 @@ jest.mock('state/pages/selectors', () => ({ getPageTreePages: jest.fn(), getSearchPages: jest.fn(), getSelectedPage: jest.fn(), + getIsVirtualRootOn: jest.fn(), })); jest.mock('state/pagination/selectors', () => ({ @@ -29,6 +30,7 @@ getSearchPages.mockReturnValue([SEARCH_PAGES]); getSelectedPage.mockReturnValue(HOMEPAGE_PAYLOAD); getUserPreferences.mockReturnValue({}); getMyGroupsList.mockReturnValue(['administrators', 'free']); +getIsVirtualRootOn.mockReturnValue(false); describe('ContentPagesContainer', () => { describe('mapStateToProps', () => { From 16ca00099c9f93d20e71bb6e3162cf2990fed96a Mon Sep 17 00:00:00 2001 From: Irakli Chalagashvili Date: Fri, 19 May 2023 18:41:48 +0400 Subject: [PATCH 2/2] ENG-4852 fix admin user was seeing root also --- src/state/pages/reducer.js | 11 ++--------- src/state/pages/selectors.js | 11 ++++++++--- test/state/pages/selectors.test.js | 10 ++++++++++ 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/state/pages/reducer.js b/src/state/pages/reducer.js index 9bcaddc2b..fbb3aa851 100644 --- a/src/state/pages/reducer.js +++ b/src/state/pages/reducer.js @@ -20,7 +20,6 @@ import { SET_DASHBOARD_PAGES, SET_VIRTUAL_ROOT, } from 'state/pages/types'; -import { HOMEPAGE_CODE } from './const'; // creates a map from an array const toMap = (array, propKey) => array.reduce((acc, page) => { @@ -140,15 +139,9 @@ const childrenMap = (state = {}, action = {}) => { const titlesMap = (state = {}, action = {}) => { switch (action.type) { case ADD_PAGES: { - const mapOfTitles = toMap(action.payload.pages, 'titles'); - if (mapOfTitles[HOMEPAGE_CODE]) { - Object.keys(mapOfTitles[HOMEPAGE_CODE]).forEach((key) => { - mapOfTitles[HOMEPAGE_CODE][key] = 'Root'; - }); - } return { ...state, - ...mapOfTitles, + ...toMap(action.payload.pages, 'titles'), }; } case UPDATE_PAGE: { @@ -293,7 +286,7 @@ export const dashboard = (state = [], action = {}) => { } }; -export const virtualRoot = (state = {}, action = {}) => { +export const virtualRoot = (state = false, action = {}) => { switch (action.type) { case SET_VIRTUAL_ROOT: return action.payload; diff --git a/src/state/pages/selectors.js b/src/state/pages/selectors.js index 4c235ecc5..05cbb176a 100644 --- a/src/state/pages/selectors.js +++ b/src/state/pages/selectors.js @@ -104,8 +104,9 @@ const PAGE_STATUS_DEFAULTS = { }; export const getPageTreePages = createSelector( - [getPagesMap, getChildrenMap, getStatusMap, getTitlesMap, getLocale, getDefaultLanguage], - (pages, pageChildren, pagesStatus, pagesTitles, locale, defaultLang) => ( + [getPagesMap, getChildrenMap, getStatusMap, getTitlesMap, getLocale, getDefaultLanguage, + getIsVirtualRootOn], + (pages, pageChildren, pagesStatus, pagesTitles, locale, defaultLang, virtualRootOn) => ( getPagesOrder(pageChildren) .filter(pageCode => isVisible(pageCode, pages, pagesStatus)) .map((pageCode) => { @@ -118,12 +119,16 @@ export const getPageTreePages = createSelector( .some(el => pages[el] && pages[el].status === PAGE_STATUS_PUBLISHED); } - const title = pagesTitles[pageCode][locale] + let title = pagesTitles[pageCode][locale] || pagesTitles[pageCode][defaultLang] || pagesTitles[pageCode][ Object.keys(pagesTitles[pageCode]).find(langCode => pagesTitles[pageCode][langCode]) ]; + if (pageCode === HOMEPAGE_CODE && virtualRootOn) { + title = 'Root'; + } + return ({ ...pages[pageCode], ...PAGE_STATUS_DEFAULTS, diff --git a/test/state/pages/selectors.test.js b/test/state/pages/selectors.test.js index 8d6d32aac..4a5d77951 100644 --- a/test/state/pages/selectors.test.js +++ b/test/state/pages/selectors.test.js @@ -114,6 +114,8 @@ describe('state/pages/selectors', () => { let pageTreePages; beforeEach(() => { pageTreePages = getPageTreePages(MOCK_STATE); + // make virtualRoot false + MOCK_STATE.pages.virtualRoot = false; }); it('only returns expanded rows and their children', () => { expect(pageTreePages.length).toBe(4); // homepage and its 3 children @@ -160,6 +162,14 @@ describe('state/pages/selectors', () => { expect(pageTreePages[2].loaded).toBe(false); expect(pageTreePages[3].loaded).toBe(false); }); + it('shows Root for home if virtualRoot is true', () => { + MOCK_STATE.pages.virtualRoot = true; + pageTreePages = getPageTreePages(MOCK_STATE); + expect(pageTreePages[0].title).toBe('Root'); + expect(pageTreePages[1].title).toBe(pageTreePages[1].titles[LOCALE_MOCK]); + expect(pageTreePages[2].title).toBe(pageTreePages[2].titles[LOCALE_MOCK]); + expect(pageTreePages[3].title).toBe(pageTreePages[3].titles[LOCALE_MOCK]); + }); }); describe('getCharsets(state)', () => {