From b266d9fe5a2d94ddbadad4c711affd5636ae504f Mon Sep 17 00:00:00 2001 From: Georges Lebreton <102960844+Georges-GNM@users.noreply.github.com> Date: Wed, 5 Feb 2025 09:50:12 +0000 Subject: [PATCH] Restrict splash to one story (#1753) * wip commit - added splash and number of cards in group to insert logic * wip commit - functioning insert to bottom of group case * tidy - inserting to empty splash works, inserting at bottom or top moves the card we're inserting to standard * include handle move logic - same level of functioning * working move/insert to top of splash * lint * remove unused imports * Indicate new props as optional * add new props to dnd test * remove unnecessary number of cards in group prop * rename groupsIds to groupIds * access groupIds in collection instead of taken from state and adding to store * lint * refactor if statements to use early returns * refactor to add dropSource variable from isDropFromCapiFeed * default numberOfArticlesAlreadyInGroup to 0 if there is no cards[].length * lint --- .../src/components/FrontsEdit/Collection.tsx | 6 +- .../CollectionComponents/Collection.tsx | 7 +- .../components/FrontsEdit/FrontContent.tsx | 127 +++++++++++++++++- .../src/components/clipboard/CardLevel.tsx | 6 + .../src/components/clipboard/GroupLevel.tsx | 6 + fronts-client/src/lib/dnd/Level.tsx | 8 ++ .../src/lib/dnd/__tests__/dnd.spec.tsx | 27 +++- 7 files changed, 177 insertions(+), 10 deletions(-) diff --git a/fronts-client/src/components/FrontsEdit/Collection.tsx b/fronts-client/src/components/FrontsEdit/Collection.tsx index 8b18361269a..fa0d837ff09 100644 --- a/fronts-client/src/components/FrontsEdit/Collection.tsx +++ b/fronts-client/src/components/FrontsEdit/Collection.tsx @@ -151,7 +151,7 @@ class CollectionContext extends React.Component canPublish={browsingStage !== 'live'} browsingStage={browsingStage} > - {(group, isUneditable, showGroupName) => ( + {(group, isUneditable, groupIds, showGroupName) => (
React.ReactNode; alsoOn: { [id: string]: AlsoOnDetail }; @@ -223,6 +224,8 @@ class Collection extends React.Component { const isUneditable = isCollectionLocked || browsingStage !== cardSets.draft; + const groupIds = groups.map((group) => group.uuid); + return ( <> { ) : null } > - {groups.map((group) => children(group, isUneditable, true))} + {groups.map((group) => children(group, isUneditable, groupIds, true))} {hasContent && ( @@ -357,7 +360,7 @@ class Collection extends React.Component { launched they will not appear here. - {children(previousGroup, true, false)} + {children(previousGroup, true, groupIds, false)} )} diff --git a/fronts-client/src/components/FrontsEdit/FrontContent.tsx b/fronts-client/src/components/FrontsEdit/FrontContent.tsx index cb1fdf8e7e6..9acc0d1bc10 100644 --- a/fronts-client/src/components/FrontsEdit/FrontContent.tsx +++ b/fronts-client/src/components/FrontsEdit/FrontContent.tsx @@ -179,13 +179,132 @@ class FrontContent extends React.Component { } public handleMove = (move: Move) => { - events.dropArticle(this.props.id, 'collection'); - this.props.moveCard(move.to, move.data, move.from || null, 'collection'); + const numberOfArticlesAlreadyInGroup = move.to.cards?.length ?? 0; + + // if we are inserting an article into any group that is not the splash, then we just insert + // we also just insert if we're in the splash and there's no other article already in the splash + if ( + move.to.groupName !== 'splash' || + numberOfArticlesAlreadyInGroup === 0 + ) { + events.dropArticle(this.props.id, 'collection'); + this.props.moveCard(move.to, move.data, move.from || null, 'collection'); + return; + } + + // if we're in the splash and we insert an article and there's already another article, then we also look at the index we're inserting to + // if we're inserting to index 0, i.e. top of the group, then we want to grab the pre-existing article and move it to the other group + if ( + !!move.to.groupIds && + move.to.cards !== undefined && + move.to.index === 0 + ) { + //we do the regular move steps for the article we're moving to splash + events.dropArticle(this.props.id, 'collection'); + this.props.moveCard(move.to, move.data, move.from || null, 'collection'); + + //then we need to move the other article to the other group + const otherGroup = move.to.groupIds.filter( + (groupId) => groupId !== move.to.id, + )[0]; + const existingCardData = move.to.cards[0]; + const existingCardTo = { + index: 0, + id: otherGroup, + type: 'group', + groupIds: move.to.groupIds, + }; + const existingCardMoveData: Move = { + data: existingCardData, + from: false, + to: existingCardTo, + }; + this.handleMove(existingCardMoveData); + return; + } + + // if we're in the splash and we insert an article and there's already another article, then we also look at the index we're inserting to + // if we're inserting to index 1, i.e. bottom of the group, then we add this story to the other group + if ( + !!move.to.groupIds && + numberOfArticlesAlreadyInGroup > 0 && + move.to.index > 0 + ) { + const otherGroup = move.to.groupIds.filter( + (groupId) => groupId !== move.to.id, + )[0]; + + const amendedTo = { + index: 0, + id: otherGroup, + type: 'group', + groupIds: move.to.groupIds, + }; + events.dropArticle(this.props.id, 'collection'); + + this.props.moveCard( + amendedTo, + move.data, + move.from || null, + 'collection', + ); + return; + } }; public handleInsert = (e: React.DragEvent, to: PosSpec) => { - events.dropArticle(this.props.id, isDropFromCAPIFeed(e) ? 'feed' : 'url'); - this.props.insertCardFromDropEvent(e, to, 'collection'); + const numberOfArticlesAlreadyInGroup = to.cards?.length ?? 0; + + const dropSource = isDropFromCAPIFeed(e) ? 'feed' : 'url'; + + // if we are inserting an article into any group that is not the splash, then we just insert + // we also just insert if we're in the splash and there's no other article already in the splash + if (to.groupName !== 'splash' || numberOfArticlesAlreadyInGroup === 0) { + events.dropArticle(this.props.id, dropSource); + this.props.insertCardFromDropEvent(e, to, 'collection'); + return; + } + + // if we're in the splash and we insert an article and there's already another article, then we also look at the index we're inserting to + // if we're inserting to index 0, i.e. top of the group, then we want to grab the pre-existing article and move it to the other group + if (!!to.groupIds && to.cards !== undefined && to.index === 0) { + // we do the regular insert steps for the article we're inserting to splash + events.dropArticle(this.props.id, dropSource); + this.props.insertCardFromDropEvent(e, to, 'collection'); + + // then we need to move the other article to the other group + const otherGroup = to.groupIds.filter((groupId) => groupId !== to.id)[0]; + const existingCardData = to.cards[0]; + const existingCardTo = { + index: 0, + id: otherGroup, + type: 'group', + groupIds: to.groupIds, + }; + const existingCardMoveData: Move = { + data: existingCardData, + from: false, + to: existingCardTo, + }; + this.handleMove(existingCardMoveData); + return; + } + + // if we're in the splash and we insert an article and there's already another article, then we also look at the index we're inserting to + // if we're inserting to index 1, i.e. bottom of the group, then we add this story to the other group + if (!!to.groupIds && numberOfArticlesAlreadyInGroup > 0 && to.index > 0) { + const otherGroup = to.groupIds.filter((groupId) => groupId !== to.id)[0]; + + const amendedTo = { + index: 0, + id: otherGroup, + type: 'group', + groupIds: to.groupIds, + }; + events.dropArticle(this.props.id, dropSource); + this.props.insertCardFromDropEvent(e, amendedTo, 'collection'); + return; + } }; public render() { diff --git a/fronts-client/src/components/clipboard/CardLevel.tsx b/fronts-client/src/components/clipboard/CardLevel.tsx index ba643d96ebe..d8e275c8a56 100644 --- a/fronts-client/src/components/clipboard/CardLevel.tsx +++ b/fronts-client/src/components/clipboard/CardLevel.tsx @@ -20,6 +20,8 @@ interface OuterProps { isUneditable?: boolean; dropMessage?: string; cardTypeAllowList?: CardTypes[]; + groupName?: string; + groupIds?: string[]; } interface InnerProps { @@ -49,11 +51,15 @@ const CardLevel = ({ isUneditable, dropMessage, cardTypeAllowList, + groupName, + groupIds, }: Props) => ( ; onDrop: DropHandler; isUneditable?: boolean; + groupName: string; + groupIds: string[]; } interface InnerProps { @@ -60,11 +62,15 @@ const GroupLevel = ({ onMove, onDrop, isUneditable, + groupName, + groupIds, }: Props) => ( @@ -56,6 +59,8 @@ export interface LevelProps { children: LevelChild; parentId: string; parentType: string; + groupName?: string; + groupIds?: string[]; type: string; getDropType?: (item: T) => string; dragImageOffsetX?: number; @@ -178,6 +183,9 @@ class Level extends React.Component, State> { index: i, type: this.props.parentType, id: this.props.parentId, + groupName: this.props.groupName, + groupIds: this.props.groupIds, + cards: this.props.arr, }; if (!af) { diff --git a/fronts-client/src/lib/dnd/__tests__/dnd.spec.tsx b/fronts-client/src/lib/dnd/__tests__/dnd.spec.tsx index 019c3333e41..074ea1a4715 100644 --- a/fronts-client/src/lib/dnd/__tests__/dnd.spec.tsx +++ b/fronts-client/src/lib/dnd/__tests__/dnd.spec.tsx @@ -110,7 +110,14 @@ describe('Curation', () => { expect(edit).toEqual({ data: { id: '1' }, from: { type: 'b', id: '0', index: 0 }, - to: { type: 'a', id: '2', index: 1 }, + to: { + cards: [{ id: '3' }, { id: '4' }], + groupName: undefined, + groupIds: undefined, + id: '2', + index: 1, + type: 'a', + }, }); }); @@ -164,7 +171,14 @@ describe('Curation', () => { expect(edit).toEqual({ data: { id: '1' }, from: { type: 'b', id: '0', index: 0 }, - to: { type: 'a', id: '2', index: 0 }, + to: { + cards: [{ id: '3' }, { id: '4' }], + groupName: undefined, + groupIds: undefined, + id: '2', + index: 0, + type: 'a', + }, }); runDrag(nodeProps)(dropProps, false); @@ -273,7 +287,14 @@ describe('Curation', () => { })(dropProps); expect(JSON.parse(event.dataTransfer.getData('text'))).toEqual(data); - expect(to).toEqual({ id: '2', index: 1, type: 'a' }); + expect(to).toEqual({ + cards: [{ id: '1' }], + groupName: undefined, + groupIds: undefined, + id: '2', + index: 1, + type: 'a', + }); }); it('does not allow moves of a node to a subPath of that node', () => {