From aafe18967f66a351bd5274b59207ddb4360ba181 Mon Sep 17 00:00:00 2001 From: ascender1729 Date: Tue, 7 Oct 2025 19:07:58 +0530 Subject: [PATCH 1/5] fix: add separator and sorting for unpublished entries Implements proper sorting and visual separation for unpublished entries in collections when editorial workflow is enabled. Changes: - Add visual separator with "Unpublished Entries" heading - Implement sorting for unpublished entries using collection sort config - Extract unpublished entries logic into separate method - Add translation key for internationalization support - Pass sortFields through component chain Technical implementation: - Modify EntryListing to render published/unpublished separately - Create sortEntries() method for applying sort configuration - Add styled components for visual separation - Enhance component props to include sortFields Fixes #7542 --- .../components/Collection/Entries/Entries.js | 3 + .../Collection/Entries/EntriesCollection.js | 5 + .../Collection/Entries/EntryListing.js | 96 ++++++++++++++++--- .../EntriesCollection.spec.js.snap | 3 + packages/decap-cms-locales/src/en/index.js | 11 +++ 5 files changed, 105 insertions(+), 13 deletions(-) diff --git a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js index 0ef11da57274..12edd6d0d1fd 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js @@ -29,6 +29,7 @@ function Entries({ getWorkflowStatus, getUnpublishedEntries, filterTerm, + sortFields, }) { const loadingMessages = [ t('collection.entries.loadingEntries'), @@ -54,6 +55,7 @@ function Entries({ getWorkflowStatus={getWorkflowStatus} getUnpublishedEntries={getUnpublishedEntries} filterTerm={filterTerm} + sortFields={sortFields} /> {isFetching && page !== undefined && entries.size > 0 ? ( {t('collection.entries.loadingEntries')} @@ -77,6 +79,7 @@ Entries.propTypes = { getWorkflowStatus: PropTypes.func, getUnpublishedEntries: PropTypes.func, filterTerm: PropTypes.string, + sortFields: ImmutablePropTypes.list, }; export default translate()(Entries); diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js b/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js index 8847f3b7c242..b87188dd3759 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js @@ -18,6 +18,7 @@ import { selectEntriesLoaded, selectIsFetching, selectGroups, + selectEntriesSortFields, } from '../../../reducers/entries'; import { selectUnpublishedEntry, selectUnpublishedEntriesByStatus } from '../../../reducers'; import { selectCollectionEntriesCursor } from '../../../reducers/cursors'; @@ -144,6 +145,7 @@ export class EntriesCollection extends React.Component { getWorkflowStatus, getUnpublishedEntries, filterTerm, + sortFields, } = this.props; const EntriesToRender = ({ entries }) => { @@ -160,6 +162,7 @@ export class EntriesCollection extends React.Component { getWorkflowStatus={getWorkflowStatus} getUnpublishedEntries={getUnpublishedEntries} filterTerm={filterTerm} + sortFields={sortFields} /> ); }; @@ -206,6 +209,7 @@ function mapStateToProps(state, ownProps) { let entries = selectEntries(state.entries, collection); const groups = selectGroups(state.entries, collection); + const sortFields = selectEntriesSortFields(state.entries, collection.get('name')); if (collection.has('nested')) { const collectionFolder = collection.get('folder'); @@ -233,6 +237,7 @@ function mapStateToProps(state, ownProps) { page, entries, groups, + sortFields, entriesLoaded, isFetching, viewStyle, diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js index c6d0e391457d..7ced774e857e 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js @@ -3,10 +3,18 @@ import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import styled from '@emotion/styled'; import { Waypoint } from 'react-waypoint'; -import { Map, List } from 'immutable'; - -import { selectFields, selectInferredField } from '../../../reducers/collections'; +import { Map, List, fromJS } from 'immutable'; +import { translate } from 'react-polyglot'; +import orderBy from 'lodash/orderBy'; +import { colors } from 'decap-cms-ui-default'; + +import { + selectFields, + selectInferredField, + selectSortDataPath, +} from '../../../reducers/collections'; import { filterNestedEntries } from './EntriesCollection'; +import { SortDirection } from '../../../types/redux'; import EntryCard from './EntryCard'; const CardsGrid = styled.ul` @@ -18,6 +26,20 @@ const CardsGrid = styled.ul` margin-bottom: 16px; `; +const SectionSeparator = styled.div` + width: 100%; + margin: 24px 0 16px 12px; + padding-top: 16px; + border-top: 2px solid ${colors.textFieldBorder}; +`; + +const SectionHeading = styled.h3` + font-size: 16px; + font-weight: 600; + color: ${colors.textLead}; + margin: 0 0 8px; +`; + class EntryListing extends React.Component { static propTypes = { collections: ImmutablePropTypes.iterable.isRequired, @@ -29,6 +51,8 @@ class EntryListing extends React.Component { getUnpublishedEntries: PropTypes.func.isRequired, getWorkflowStatus: PropTypes.func.isRequired, filterTerm: PropTypes.string, + sortFields: ImmutablePropTypes.list, + t: PropTypes.func.isRequired, }; componentDidMount() { @@ -58,18 +82,30 @@ class EntryListing extends React.Component { return { titleField, descriptionField, imageField, remainingFields }; }; - getAllEntries = () => { - const { entries, collections, filterTerm } = this.props; + sortEntries = (entries, sortFields, collections) => { + if (!sortFields || sortFields.size === 0) { + return entries; + } + + const keys = sortFields.map(v => selectSortDataPath(collections, v.get('key'))); + const orders = sortFields.map(v => + v.get('direction') === SortDirection.Ascending ? 'asc' : 'desc', + ); + return fromJS(orderBy(entries.toJS(), keys.toArray(), orders.toArray())); + }; + + getUnpublishedEntriesList = () => { + const { entries, collections, filterTerm, sortFields } = this.props; const collectionName = Map.isMap(collections) ? collections.get('name') : null; if (!collectionName) { - return entries; + return List(); } const unpublishedEntries = this.props.getUnpublishedEntries(collectionName); if (!unpublishedEntries || unpublishedEntries.length === 0) { - return entries; + return List(); } let unpublishedList = List(unpublishedEntries.map(entry => entry)); @@ -91,25 +127,59 @@ class EntryListing extends React.Component { publishedSlugs.has(entry.get('slug')), ); - return entries.concat(uniqueUnpublished); + return this.sortEntries(uniqueUnpublished, sortFields, collections); }; renderCardsForSingleCollection = () => { - const { collections, viewStyle } = this.props; - const allEntries = this.getAllEntries(); + const { collections, viewStyle, entries, t } = this.props; const inferredFields = this.inferFields(collections); const entryCardProps = { collection: collections, inferredFields, viewStyle }; - return allEntries.map((entry, idx) => { + const publishedCards = entries.map((entry, idx) => { const workflowStatus = this.props.getWorkflowStatus( collections.get('name'), entry.get('slug'), ); return ( - + ); }); + + const unpublishedEntries = this.getUnpublishedEntriesList(); + + if (unpublishedEntries.size === 0) { + return publishedCards; + } + + const unpublishedCards = unpublishedEntries.map((entry, idx) => { + const workflowStatus = this.props.getWorkflowStatus( + collections.get('name'), + entry.get('slug'), + ); + + return ( + + ); + }); + + return [ + ...publishedCards.toArray(), + + {t('collection.entries.unpublishedHeader')} + , + ...unpublishedCards.toArray(), + ]; }; renderCardsForMultipleCollections = () => { @@ -148,4 +218,4 @@ class EntryListing extends React.Component { } } -export default EntryListing; +export default translate()(EntryListing); diff --git a/packages/decap-cms-core/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap b/packages/decap-cms-core/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap index 71e9ec1b5572..00b9bdb26969 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap +++ b/packages/decap-cms-core/src/components/Collection/Entries/__tests__/__snapshots__/EntriesCollection.spec.js.snap @@ -7,6 +7,7 @@ exports[`EntriesCollection should render connected component 1`] = ` collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\" }" cursor="[object Object]" entries="List [ Map { \\"slug\\": \\"index\\", \\"path\\": \\"src/pages/index.md\\", \\"data\\": Map { \\"title\\": \\"Root\\" } }, Map { \\"slug\\": \\"dir1/index\\", \\"path\\": \\"src/pages/dir1/index.md\\", \\"data\\": Map { \\"title\\": \\"File 1\\" } }, Map { \\"slug\\": \\"dir2/index\\", \\"path\\": \\"src/pages/dir2/index.md\\", \\"data\\": Map { \\"title\\": \\"File 2\\" } } ]" + sortfields="" /> `; @@ -18,6 +19,7 @@ exports[`EntriesCollection should render show only immediate children for nested collections="Map { \\"name\\": \\"pages\\", \\"label\\": \\"Pages\\", \\"folder\\": \\"src/pages\\", \\"nested\\": Map { \\"depth\\": 10, \\"subfolders\\": false } }" cursor="[object Object]" entries="List [ Map { \\"slug\\": \\"index\\", \\"path\\": \\"src/pages/index.md\\", \\"data\\": Map { \\"title\\": \\"Root\\" } } ]" + sortfields="" /> `; @@ -30,6 +32,7 @@ exports[`EntriesCollection should render with applied filter term for nested col cursor="[object Object]" entries="List [ Map { \\"slug\\": \\"dir3/dir4/index\\", \\"path\\": \\"src/pages/dir3/dir4/index.md\\", \\"data\\": Map { \\"title\\": \\"File 4\\" } } ]" filterterm="dir3/dir4" + sortfields="" /> `; diff --git a/packages/decap-cms-locales/src/en/index.js b/packages/decap-cms-locales/src/en/index.js index 5cba0d853d43..739dd4683db9 100644 --- a/packages/decap-cms-locales/src/en/index.js +++ b/packages/decap-cms-locales/src/en/index.js @@ -312,6 +312,17 @@ const en = { readyHeader: 'Ready', currentEntries: '%{smart_count} entry |||| %{smart_count} entries', }, + collection: { + sidebar: { + collections: 'Collections', + allCollections: 'All Collections', + searchAll: 'Search all', + searchIn: 'Search in', + }, + entries: { + unpublishedHeader: 'Unpublished Entries', + }, + }, }, }; From 7edb37fb245fd92f4c4dcb6381ae9f542e1503f6 Mon Sep 17 00:00:00 2001 From: ascender1729 Date: Wed, 8 Oct 2025 18:18:41 +0530 Subject: [PATCH 2/5] refactor: address PR review feedback - Add .claude/settings.local.json to .gitignore - Refactor EntryListing to use React.Fragment instead of array spread for better readability --- .gitignore | 1 + .../Collection/Entries/EntryListing.js | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 01ce5e521df5..5d55c107daf0 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ coverage/ .temp/ storybook-static/ .nx +.claude/settings.local.json diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js index 7ced774e857e..2ca6e277bc57 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js @@ -173,13 +173,15 @@ class EntryListing extends React.Component { ); }); - return [ - ...publishedCards.toArray(), - - {t('collection.entries.unpublishedHeader')} - , - ...unpublishedCards.toArray(), - ]; + return ( + + {publishedCards} + + {t('collection.entries.unpublishedHeader')} + + {unpublishedCards} + + ); }; renderCardsForMultipleCollections = () => { From dd3fe81c09630356c2e6eefbfd1023307e14ed1b Mon Sep 17 00:00:00 2001 From: Yan <61414485+yanthomasdev@users.noreply.github.com> Date: Mon, 25 May 2026 18:27:12 -0300 Subject: [PATCH 3/5] fix: address comments --- .../src/components/Collection/Entries/Entries.js | 2 +- .../components/Collection/Entries/EntryListing.js | 9 +++++---- packages/decap-cms-locales/src/en/index.js | 12 +----------- 3 files changed, 7 insertions(+), 16 deletions(-) diff --git a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js index 12edd6d0d1fd..62c347876bbb 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js @@ -79,7 +79,7 @@ Entries.propTypes = { getWorkflowStatus: PropTypes.func, getUnpublishedEntries: PropTypes.func, filterTerm: PropTypes.string, - sortFields: ImmutablePropTypes.list, + sortFields: PropTypes.array, }; export default translate()(Entries); diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js index 2ca6e277bc57..7b45e4513083 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js @@ -26,11 +26,12 @@ const CardsGrid = styled.ul` margin-bottom: 16px; `; -const SectionSeparator = styled.div` +const SectionSeparator = styled.li` width: 100%; margin: 24px 0 16px 12px; padding-top: 16px; border-top: 2px solid ${colors.textFieldBorder}; + list-style: none; `; const SectionHeading = styled.h3` @@ -51,7 +52,7 @@ class EntryListing extends React.Component { getUnpublishedEntries: PropTypes.func.isRequired, getWorkflowStatus: PropTypes.func.isRequired, filterTerm: PropTypes.string, - sortFields: ImmutablePropTypes.list, + sortFields: PropTypes.array, t: PropTypes.func.isRequired, }; @@ -83,7 +84,7 @@ class EntryListing extends React.Component { }; sortEntries = (entries, sortFields, collections) => { - if (!sortFields || sortFields.size === 0) { + if (!sortFields || sortFields.length === 0) { return entries; } @@ -91,7 +92,7 @@ class EntryListing extends React.Component { const orders = sortFields.map(v => v.get('direction') === SortDirection.Ascending ? 'asc' : 'desc', ); - return fromJS(orderBy(entries.toJS(), keys.toArray(), orders.toArray())); + return fromJS(orderBy(entries.toJS(), keys, orders)); }; getUnpublishedEntriesList = () => { diff --git a/packages/decap-cms-locales/src/en/index.js b/packages/decap-cms-locales/src/en/index.js index 00148d7eb1d2..d3d8ad37afd2 100644 --- a/packages/decap-cms-locales/src/en/index.js +++ b/packages/decap-cms-locales/src/en/index.js @@ -56,6 +56,7 @@ const en = { cachingEntries: 'Caching Entries...', longerLoading: 'This might take several minutes', noEntries: 'No Entries', + unpublishedHeader: 'Unpublished Entries', }, groups: { other: 'Other', @@ -313,17 +314,6 @@ const en = { readyHeader: 'Ready', currentEntries: '%{smart_count} entry |||| %{smart_count} entries', }, - collection: { - sidebar: { - collections: 'Collections', - allCollections: 'All Collections', - searchAll: 'Search all', - searchIn: 'Search in', - }, - entries: { - unpublishedHeader: 'Unpublished Entries', - }, - }, }, }; From 11c8e7d5a28b8f4bf370f61846758d9eb17622dd Mon Sep 17 00:00:00 2001 From: Yan <61414485+yanthomasdev@users.noreply.github.com> Date: Tue, 26 May 2026 14:25:18 -0300 Subject: [PATCH 4/5] feat: split unpublished entries into its own list --- .../Collection/Entries/EntryListing.js | 49 ++++++++++++------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js index 7b45e4513083..51e2d8c12b9f 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js @@ -6,7 +6,7 @@ import { Waypoint } from 'react-waypoint'; import { Map, List, fromJS } from 'immutable'; import { translate } from 'react-polyglot'; import orderBy from 'lodash/orderBy'; -import { colors } from 'decap-cms-ui-default'; +import { colors, lengths } from 'decap-cms-ui-default'; import { selectFields, @@ -26,15 +26,14 @@ const CardsGrid = styled.ul` margin-bottom: 16px; `; -const SectionSeparator = styled.li` - width: 100%; +const SectionSeparator = styled.div` + width: ${lengths.topCardWidth}; margin: 24px 0 16px 12px; padding-top: 16px; border-top: 2px solid ${colors.textFieldBorder}; - list-style: none; `; -const SectionHeading = styled.h3` +const SectionHeading = styled.p` font-size: 16px; font-weight: 600; color: ${colors.textLead}; @@ -132,7 +131,7 @@ class EntryListing extends React.Component { }; renderCardsForSingleCollection = () => { - const { collections, viewStyle, entries, t } = this.props; + const { collections, viewStyle, entries, page, t } = this.props; const inferredFields = this.inferFields(collections); const entryCardProps = { collection: collections, inferredFields, viewStyle }; @@ -155,7 +154,12 @@ class EntryListing extends React.Component { const unpublishedEntries = this.getUnpublishedEntriesList(); if (unpublishedEntries.size === 0) { - return publishedCards; + return ( + + {publishedCards} + {this.hasMore() && } + + ); } const unpublishedCards = unpublishedEntries.map((entry, idx) => { @@ -176,19 +180,22 @@ class EntryListing extends React.Component { return ( - {publishedCards} - + + {publishedCards} + {this.hasMore() && } + + {t('collection.entries.unpublishedHeader')} - {unpublishedCards} + {unpublishedCards} ); }; renderCardsForMultipleCollections = () => { - const { collections, entries } = this.props; + const { collections, entries, page } = this.props; const isSingleCollectionInList = collections.size === 1; - return entries.map((entry, idx) => { + const entryCards = entries.map((entry, idx) => { const collectionName = entry.get('collection'); const collection = collections.find(coll => coll.get('name') === collectionName); const collectionLabel = !isSingleCollectionInList && collection.get('label'); @@ -203,19 +210,23 @@ class EntryListing extends React.Component { }; return ; }); + + return ( + + {entryCards} + {this.hasMore() && } + + ); }; render() { - const { collections, page } = this.props; + const { collections } = this.props; return (
- - {Map.isMap(collections) - ? this.renderCardsForSingleCollection() - : this.renderCardsForMultipleCollections()} - {this.hasMore() && } - + {Map.isMap(collections) + ? this.renderCardsForSingleCollection() + : this.renderCardsForMultipleCollections()}
); } From 6c07b21bbfda20e4cc9720d2840c9c33007a5867 Mon Sep 17 00:00:00 2001 From: Yan <61414485+yanthomasdev@users.noreply.github.com> Date: Tue, 16 Jun 2026 16:46:41 -0300 Subject: [PATCH 5/5] fix: remove duplicate grouped entries --- .../components/Collection/Entries/Entries.js | 15 +++-- .../Collection/Entries/EntriesCollection.js | 23 ++++++- .../Collection/Entries/EntryListing.js | 67 +++++++++++++------ .../__tests__/EntriesCollection.spec.js | 33 ++++++++- 4 files changed, 107 insertions(+), 31 deletions(-) diff --git a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js index 18aed98625e3..97d463a68252 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/Entries.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/Entries.js @@ -31,6 +31,8 @@ function Entries({ getUnpublishedEntries, filterTerm, sortFields, + showPublishedEntries = true, + showUnpublishedEntries = true, }) { const loadingMessages = [ t('collection.entries.loadingEntries'), @@ -38,12 +40,13 @@ function Entries({ t('collection.entries.longerLoading'), ]; - if (isFetching && page === undefined) { + if (showPublishedEntries && isFetching && page === undefined) { return {loadingMessages}; } - const hasEntries = (entries && entries.size > 0) || cursor?.actions?.has('append_next'); - if (hasEntries) { + const hasEntries = + showPublishedEntries && ((entries && entries.size > 0) || cursor?.actions?.has('append_next')); + if (hasEntries || !showPublishedEntries) { return ( <> - {isFetching && page !== undefined && entries.size > 0 ? ( + {showPublishedEntries && isFetching && page !== undefined && entries.size > 0 ? ( {t('collection.entries.loadingEntries')} ) : null} @@ -81,6 +86,8 @@ Entries.propTypes = { getUnpublishedEntries: PropTypes.func, filterTerm: PropTypes.string, sortFields: PropTypes.array, + showPublishedEntries: PropTypes.bool, + showUnpublishedEntries: PropTypes.bool, }; export default translate()(Entries); diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js b/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js index b87188dd3759..db8c9bfbc8fc 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntriesCollection.js @@ -55,7 +55,10 @@ function withGroups(groups, entries, EntriesToRender, t) { return ( {title} - + ); }); @@ -148,7 +151,15 @@ export class EntriesCollection extends React.Component { sortFields, } = this.props; - const EntriesToRender = ({ entries }) => { + const EntriesToRender = ({ entries, showPublishedEntries, showUnpublishedEntries }) => { + const visibilityProps = {}; + if (showPublishedEntries !== undefined) { + visibilityProps.showPublishedEntries = showPublishedEntries; + } + if (showUnpublishedEntries !== undefined) { + visibilityProps.showUnpublishedEntries = showUnpublishedEntries; + } + return ( ); }; if (groups && groups.length > 0) { - return withGroups(groups, entries, EntriesToRender, t); + return ( + + {withGroups(groups, entries, EntriesToRender, t)} + + + ); } return ; diff --git a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js index 51e2d8c12b9f..b28b2102d19c 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/EntryListing.js @@ -52,9 +52,16 @@ class EntryListing extends React.Component { getWorkflowStatus: PropTypes.func.isRequired, filterTerm: PropTypes.string, sortFields: PropTypes.array, + showPublishedEntries: PropTypes.bool, + showUnpublishedEntries: PropTypes.bool, t: PropTypes.func.isRequired, }; + static defaultProps = { + showPublishedEntries: true, + showUnpublishedEntries: true, + }; + componentDidMount() { // Manually validate PropTypes - React 19 breaking change PropTypes.checkPropTypes(EntryListing.propTypes, this.props, 'prop', 'EntryListing'); @@ -131,29 +138,43 @@ class EntryListing extends React.Component { }; renderCardsForSingleCollection = () => { - const { collections, viewStyle, entries, page, t } = this.props; + const { + collections, + viewStyle, + entries, + page, + t, + showPublishedEntries, + showUnpublishedEntries, + } = this.props; const inferredFields = this.inferFields(collections); const entryCardProps = { collection: collections, inferredFields, viewStyle }; - const publishedCards = entries.map((entry, idx) => { - const workflowStatus = this.props.getWorkflowStatus( - collections.get('name'), - entry.get('slug'), - ); - - return ( - - ); - }); - - const unpublishedEntries = this.getUnpublishedEntriesList(); + const publishedCards = showPublishedEntries + ? entries.map((entry, idx) => { + const workflowStatus = this.props.getWorkflowStatus( + collections.get('name'), + entry.get('slug'), + ); + + return ( + + ); + }) + : List(); + + const unpublishedEntries = showUnpublishedEntries ? this.getUnpublishedEntriesList() : List(); if (unpublishedEntries.size === 0) { + if (!showPublishedEntries) { + return null; + } + return ( {publishedCards} @@ -180,10 +201,12 @@ class EntryListing extends React.Component { return ( - - {publishedCards} - {this.hasMore() && } - + {showPublishedEntries && ( + + {publishedCards} + {this.hasMore() && } + + )} {t('collection.entries.unpublishedHeader')} diff --git a/packages/decap-cms-core/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js b/packages/decap-cms-core/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js index 9d719a8248c0..8ef282154b15 100644 --- a/packages/decap-cms-core/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js +++ b/packages/decap-cms-core/src/components/Collection/Entries/__tests__/EntriesCollection.spec.js @@ -1,6 +1,6 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { fromJS } from 'immutable'; +import { Set, fromJS } from 'immutable'; import configureStore from 'redux-mock-store'; import { Provider } from 'react-redux'; @@ -8,8 +8,12 @@ import ConnectedEntriesCollection, { EntriesCollection, filterNestedEntries, } from '../EntriesCollection'; +import Entries from '../Entries'; -jest.mock('../Entries', () => 'mock-entries'); +jest.mock('../Entries', () => { + const React = require('react'); + return jest.fn(props => ); +}); const middlewares = []; const mockStore = configureStore(middlewares); @@ -102,6 +106,31 @@ describe('EntriesCollection', () => { expect(asFragment()).toMatchSnapshot(); }); + it('should render unpublished entries once when entries are grouped', () => { + Entries.mockClear(); + + const entries = fromJS([ + { slug: 'one', path: 'src/pages/one.md' }, + { slug: 'two', path: 'src/pages/two.md' }, + ]); + const groups = [ + { id: 'Groupone', label: 'Group', value: 'one', paths: Set(['src/pages/one.md']) }, + { id: 'Grouptwo', label: 'Group', value: 'two', paths: Set(['src/pages/two.md']) }, + ]; + + const { container } = render( + , + ); + const renderedEntries = container.querySelectorAll('mock-entries'); + + expect(renderedEntries).toHaveLength(3); + expect(Entries).toHaveBeenCalledTimes(3); + expect(Entries.mock.calls[0][0].showUnpublishedEntries).toBe(false); + expect(Entries.mock.calls[1][0].showUnpublishedEntries).toBe(false); + expect(Entries.mock.calls[2][0].showPublishedEntries).toBe(false); + expect(Entries.mock.calls[2][0].entries).toBe(entries); + }); + it('should render connected component', () => { const entriesArray = [ { slug: 'index', path: 'src/pages/index.md', data: { title: 'Root' } },