From e0e4472a081b97453500930d265c6c3930000fd8 Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Tue, 30 Apr 2024 17:02:36 +0100 Subject: [PATCH 1/9] Add cardType property to Card type --- fronts-client/src/selectors/cardSelectors.ts | 12 +++++++++--- fronts-client/src/types/Collection.ts | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/fronts-client/src/selectors/cardSelectors.ts b/fronts-client/src/selectors/cardSelectors.ts index b9b7bd87f5c..1c2863b72be 100644 --- a/fronts-client/src/selectors/cardSelectors.ts +++ b/fronts-client/src/selectors/cardSelectors.ts @@ -6,9 +6,15 @@ import { getContributorImage } from 'util/CAPIUtils'; const createSelectCardType = () => createSelector(selectCard, (card) => { - return card && validateId(card.id) - ? CardTypesMap.SNAP_LINK - : CardTypesMap.ARTICLE; + if (!card) { + return undefined; + } + + if (card.cardType) { + return card.cardType; + } + + return validateId(card.id) ? CardTypesMap.SNAP_LINK : CardTypesMap.ARTICLE; }); const createSelectCutoutUrl = () => diff --git a/fronts-client/src/types/Collection.ts b/fronts-client/src/types/Collection.ts index b5df7b0fa13..1323da09135 100644 --- a/fronts-client/src/types/Collection.ts +++ b/fronts-client/src/types/Collection.ts @@ -1,6 +1,7 @@ import { CapiArticle } from 'types/Capi'; import { Diff } from 'utility-types'; import type { FrontsToolSettings } from 'types/FaciaApi'; +import { CardTypes } from 'constants/cardTypes'; interface CollectionArticles { draft: CapiArticle[]; @@ -28,6 +29,7 @@ type CardSizes = 'wide' | 'default' | 'medium' | 'small'; interface NestedCardRootFields { id: string; + cardType?: CardTypes; frontPublicationDate: number; publishedBy?: string; } From f4dcfec34ffe041f0a2c95a9e88c97ad2aaa6b9d Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Tue, 30 Apr 2024 17:29:53 +0100 Subject: [PATCH 2/9] Move article and snaplink cards into cards folder, and add new RecipeCard --- .../FrontsEdit/CollectionComponents/Card.tsx | 23 +++++- .../{ => card}/article/ArticleBody.tsx | 30 ++++---- .../article/ArticleCard.tsx} | 18 ++--- .../{ => card}/article/ArticleGraph.tsx | 4 +- .../DraggableArticleImageContainer.tsx | 0 .../article/__tests__/Article.spec.tsx | 22 +++--- .../src/components/card/recipe/RecipeCard.tsx | 77 +++++++++++++++++++ .../snapLink/SnapLinkCard.tsx} | 20 ++--- 8 files changed, 145 insertions(+), 49 deletions(-) rename fronts-client/src/components/{ => card}/article/ArticleBody.tsx (92%) rename fronts-client/src/components/{article/Article.tsx => card/article/ArticleCard.tsx} (91%) rename fronts-client/src/components/{ => card}/article/ArticleGraph.tsx (91%) rename fronts-client/src/components/{ => card}/article/DraggableArticleImageContainer.tsx (100%) rename fronts-client/src/components/{ => card}/article/__tests__/Article.spec.tsx (94%) create mode 100644 fronts-client/src/components/card/recipe/RecipeCard.tsx rename fronts-client/src/components/{snapLink/SnapLink.tsx => card/snapLink/SnapLinkCard.tsx} (93%) diff --git a/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx b/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx index 96689e0c0ba..85401bf4463 100644 --- a/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx +++ b/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx @@ -1,7 +1,7 @@ import { Dispatch } from 'types/Store'; import React from 'react'; import { connect } from 'react-redux'; -import Article from 'components/article/Article'; +import Article from 'components/card/article/ArticleCard'; import type { State } from 'types/State'; import { createSelectCardType } from 'selectors/cardSelectors'; import { @@ -9,7 +9,7 @@ import { selectSupportingArticleCount, } from 'selectors/shared'; import { CardSizes, CardMeta } from 'types/Collection'; -import SnapLink from 'components/snapLink/SnapLink'; +import SnapLink from 'components/card/snapLink/SnapLinkCard'; import { copyCardImageMetaWithPersist, addImageToCard, @@ -43,6 +43,7 @@ import { isLive as isArticleLive } from 'util/CAPIUtils'; import { DefaultDropIndicator } from 'components/DropZone'; import DragIntentContainer from 'components/DragIntentContainer'; import { CardTypes, CardTypesMap } from 'constants/cardTypes'; +import { RecipeCard } from 'components/card/recipe/RecipeCard'; export const createCardId = (id: string) => `collection-item-${id}`; @@ -203,6 +204,24 @@ class Card extends React.Component { : this.state.showCardSublinks && children} ); + case CardTypesMap.RECIPE: + return ( + <> + onSelect(uuid)} + size={size} + textSize={textSize} + /> + {getSublinks} + + ); default: return (

diff --git a/fronts-client/src/components/article/ArticleBody.tsx b/fronts-client/src/components/card/article/ArticleBody.tsx similarity index 92% rename from fronts-client/src/components/article/ArticleBody.tsx rename to fronts-client/src/components/card/article/ArticleBody.tsx index eb41f5266cb..9c66dd2e877 100644 --- a/fronts-client/src/components/article/ArticleBody.tsx +++ b/fronts-client/src/components/card/article/ArticleBody.tsx @@ -2,36 +2,36 @@ import React from 'react'; import { styled, theme } from 'constants/theme'; import startCase from 'lodash/startCase'; import distanceInWordsStrict from 'date-fns/distance_in_words_strict'; -import CardHeading from '../card/CardHeading'; -import BasePlaceholder from '../BasePlaceholder'; +import CardHeading from '../CardHeading'; +import BasePlaceholder from '../../BasePlaceholder'; import { getPillarColor } from 'util/getPillarColor'; -import CardMetaContainer from '../card/CardMetaContainer'; -import CardContent from '../card/CardContent'; +import CardMetaContainer from '../CardMetaContainer'; +import CardContent from '../CardContent'; import { notLiveLabels, liveBlogTones } from 'constants/fronts'; import TextPlaceholder from 'components/TextPlaceholder'; import { ThumbnailSmall, ThumbnailCutout } from 'components/image/Thumbnail'; -import CardMetaHeading from '../card/CardMetaHeading'; -import { HoverActionsButtonWrapper } from '../inputs/HoverActionButtonWrapper'; +import CardMetaHeading from '../CardMetaHeading'; +import { HoverActionsButtonWrapper } from '../../inputs/HoverActionButtonWrapper'; import { HoverViewButton, HoverOphanButton, HoverDeleteButton, HoverAddToClipboardButton, -} from '../inputs/HoverActionButtons'; -import { HoverActionsAreaOverlay } from '../CollectionHoverItems'; +} from '../../inputs/HoverActionButtons'; +import { HoverActionsAreaOverlay } from '../../CollectionHoverItems'; import { CardSizes } from 'types/Collection'; -import CardMetaContent from '../card/CardMetaContent'; -import CardDraftMetaContent from '../card/CardDraftMetaContent'; +import CardMetaContent from '../CardMetaContent'; +import CardDraftMetaContent from '../CardDraftMetaContent'; import DraggableArticleImageContainer from './DraggableArticleImageContainer'; import { media } from 'util/mediaQueries'; import ArticleGraph from './ArticleGraph'; -import { VideoIcon } from '../icons/Icons'; -import CardHeadingContainer from '../card/CardHeadingContainer'; -import CardSettingsDisplay from '../card/CardSettingsDisplay'; -import CircularIconContainer from '../icons/CircularIconContainer'; +import { VideoIcon } from '../../icons/Icons'; +import CardHeadingContainer from '../CardHeadingContainer'; +import CardSettingsDisplay from '../CardSettingsDisplay'; +import CircularIconContainer from '../../icons/CircularIconContainer'; import { ImageMetadataContainer } from 'components/image/ImageMetaDataContainer'; import EditModeVisibility from 'components/util/EditModeVisibility'; -import PageViewDataWrapper from '../PageViewDataWrapper'; +import PageViewDataWrapper from '../../PageViewDataWrapper'; import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper'; const ThumbnailPlaceholder = styled(BasePlaceholder)` diff --git a/fronts-client/src/components/article/Article.tsx b/fronts-client/src/components/card/article/ArticleCard.tsx similarity index 91% rename from fronts-client/src/components/article/Article.tsx rename to fronts-client/src/components/card/article/ArticleCard.tsx index 1416d7a22ba..613d0f16b2d 100644 --- a/fronts-client/src/components/article/Article.tsx +++ b/fronts-client/src/components/card/article/ArticleCard.tsx @@ -6,16 +6,16 @@ import noop from 'lodash/noop'; import { createSelectArticleFromCard, selectCard, -} from '../../selectors/shared'; +} from '../../../selectors/shared'; import { selectors } from 'bundles/externalArticlesBundle'; import type { State } from 'types/State'; -import { DerivedArticle } from '../../types/Article'; -import CardBody from '../card/CardBody'; -import CardContainer from '../card/CardContainer'; -import CardMetaHeading from '../card/CardMetaHeading'; +import { DerivedArticle } from '../../../types/Article'; +import CardBody from '../CardBody'; +import CardContainer from '../CardContainer'; +import CardMetaHeading from '../CardMetaHeading'; import ArticleBody from './ArticleBody'; import { CardSizes } from 'types/Collection'; -import DragIntentContainer from '../DragIntentContainer'; +import DragIntentContainer from '../../DragIntentContainer'; import { selectFeatureValue } from 'selectors/featureSwitchesSelectors'; import { theme } from 'constants/theme'; import { getPillarColor } from 'util/getPillarColor'; @@ -72,7 +72,7 @@ interface ComponentState { isDraggingImageOver: boolean; } -class ArticleComponent extends React.Component { +class ArticleCard extends React.Component { public state = { isDraggingImageOver: false, }; @@ -195,6 +195,6 @@ const createMapStateToProps = () => { }; }; -export { ArticleComponentProps, ArticleComponent }; +export { ArticleComponentProps, ArticleCard as ArticleComponent }; -export default connect(createMapStateToProps)(ArticleComponent); +export default connect(createMapStateToProps)(ArticleCard); diff --git a/fronts-client/src/components/article/ArticleGraph.tsx b/fronts-client/src/components/card/article/ArticleGraph.tsx similarity index 91% rename from fronts-client/src/components/article/ArticleGraph.tsx rename to fronts-client/src/components/card/article/ArticleGraph.tsx index a5b66a20b2c..2a9b2213ad5 100644 --- a/fronts-client/src/components/article/ArticleGraph.tsx +++ b/fronts-client/src/components/card/article/ArticleGraph.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { AreaChart, Area, XAxis, YAxis } from 'recharts'; import { PageViewStory } from 'types/PageViewData'; -import { theme } from '../../constants/theme'; +import { theme } from '../../../constants/theme'; import type { State } from 'types/State'; import { connect } from 'react-redux'; -import { selectDataForArticle } from '../../selectors/pageViewDataSelectors'; +import { selectDataForArticle } from '../../../selectors/pageViewDataSelectors'; interface ArticleGraphContainerProps { articleId: string; diff --git a/fronts-client/src/components/article/DraggableArticleImageContainer.tsx b/fronts-client/src/components/card/article/DraggableArticleImageContainer.tsx similarity index 100% rename from fronts-client/src/components/article/DraggableArticleImageContainer.tsx rename to fronts-client/src/components/card/article/DraggableArticleImageContainer.tsx diff --git a/fronts-client/src/components/article/__tests__/Article.spec.tsx b/fronts-client/src/components/card/article/__tests__/Article.spec.tsx similarity index 94% rename from fronts-client/src/components/article/__tests__/Article.spec.tsx rename to fronts-client/src/components/card/article/__tests__/Article.spec.tsx index 3245d450983..5e74993c8ce 100644 --- a/fronts-client/src/components/article/__tests__/Article.spec.tsx +++ b/fronts-client/src/components/card/article/__tests__/Article.spec.tsx @@ -1,10 +1,10 @@ import React from 'react'; import { render, cleanup } from 'react-testing-library'; -import { ArticleComponent } from '../Article'; +import { ArticleCard } from '../ArticleCard'; import '@testing-library/jest-dom/extend-expect'; import derivedArticle from 'fixtures/derivedArticle'; import { ThemeProvider } from 'styled-components'; -import { theme } from '../../../constants/theme'; +import { theme } from '../../../../constants/theme'; import { Provider } from 'react-redux'; import configureStore from 'util/configureStore'; @@ -28,7 +28,7 @@ describe('Article component ', () => { const { getByTestId } = render( - } article={derivedArticle} @@ -49,7 +49,7 @@ describe('Article component ', () => { const { getByTestId } = render( - } article={draftArticle} @@ -68,7 +68,7 @@ describe('Article component ', () => { const { getByTestId } = render( - } article={takenDownArticle} @@ -87,7 +87,7 @@ describe('Article component ', () => { const { getByTestId } = render( - } article={undefined} @@ -106,7 +106,7 @@ describe('Article component ', () => { const { getByTestId } = render( - } article={takenDownArticle} @@ -125,7 +125,7 @@ describe('Article component ', () => { let renderResult = render( - } article={takenDownArticle} @@ -143,7 +143,7 @@ describe('Article component ', () => { renderResult = render( - } article={takenDownArticle} @@ -164,7 +164,7 @@ it('should show the page view data graph if 3 conditions are true: canShowPageVi const { getByTestId } = render( - } @@ -183,7 +183,7 @@ it('should NOT show the page view data graph if any of these conditions are fals const { container } = render( - } diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx new file mode 100644 index 00000000000..aebade27247 --- /dev/null +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Card, CardSizes } from 'types/Collection'; +import CardContainer from '../CardContainer'; +import CardContent from '../CardContent'; +import CardSettingsDisplay from '../CardSettingsDisplay'; +import CardHeadingContainer from '../CardHeadingContainer'; +import CardMetaHeading from '../CardMetaHeading'; +import CardHeading from '../CardHeading'; +import { selectCard } from 'selectors/shared'; +import { connect } from 'react-redux'; +import { State } from 'types/State'; + +interface ContainerProps { + onDragStart?: (d: React.DragEvent) => void; + onDrop?: (d: React.DragEvent) => void; + onDelete?: (uuid: string) => void; + onAddToClipboard?: (uuid: string) => void; + onClick?: () => void; + id: string; + collectionId?: string; + frontId: string; + draggable?: boolean; + size?: CardSizes; + textSize?: CardSizes; + fade?: boolean; + children?: React.ReactNode; + isUneditable?: boolean; +} + +interface RecipeProps extends ContainerProps { + card: Card; + featureFlagPageViewData: boolean; +} + +const RecipeCardComponent = ({ + id, + fade, + size = 'default', + textSize = 'default', + onDelete, + onAddToClipboard, + children, + card, + isUneditable, + collectionId, + frontId, + featureFlagPageViewData, + ...rest +}: RecipeProps) => { + return ( + + + + + Recipe + + Recipe heading + + + + + ); +}; + +const mapStateToProps = (state: State, props: ContainerProps) => { + return { + card: selectCard(state, props.id), + }; +}; + +export const RecipeCard = connect(mapStateToProps)(RecipeCardComponent); diff --git a/fronts-client/src/components/snapLink/SnapLink.tsx b/fronts-client/src/components/card/snapLink/SnapLinkCard.tsx similarity index 93% rename from fronts-client/src/components/snapLink/SnapLink.tsx rename to fronts-client/src/components/card/snapLink/SnapLinkCard.tsx index 9b33d984fd7..1a24e8cda48 100644 --- a/fronts-client/src/components/snapLink/SnapLink.tsx +++ b/fronts-client/src/components/card/snapLink/SnapLinkCard.tsx @@ -13,21 +13,21 @@ import { HoverAddToClipboardButton, HoverViewButton, HoverOphanButton, -} from '../inputs/HoverActionButtons'; -import { HoverActionsAreaOverlay } from '../CollectionHoverItems'; +} from '../../inputs/HoverActionButtons'; +import { HoverActionsAreaOverlay } from '../../CollectionHoverItems'; import { Card, CardSizes } from 'types/Collection'; import { selectCard, createSelectArticleFromCard, -} from '../../selectors/shared'; +} from '../../../selectors/shared'; import type { State } from 'types/State'; -import CardHeading from '../card/CardHeading'; -import CardContent from '../card/CardContent'; +import CardHeading from '../CardHeading'; +import CardContent from '../CardContent'; import CardBody from 'components/card/CardBody'; -import CardMetaContent from '../card/CardMetaContent'; +import CardMetaContent from '../CardMetaContent'; import url from 'constants/url'; -import CardHeadingContainer from '../card/CardHeadingContainer'; -import CardSettingsDisplay from '../card/CardSettingsDisplay'; +import CardHeadingContainer from '../CardHeadingContainer'; +import CardSettingsDisplay from '../CardSettingsDisplay'; import { distanceInWordsStrict } from 'date-fns'; import { DerivedArticle } from 'types/Article'; import { ImageMetadataContainer } from 'components/image/ImageMetaDataContainer'; @@ -76,7 +76,7 @@ interface SnapLinkProps extends ContainerProps { featureFlagPageViewData: boolean; } -const SnapLink = ({ +const SnapLinkCard = ({ id, fade, size = 'default', @@ -233,4 +233,4 @@ const mapStateToProps = () => { }; }; -export default connect(mapStateToProps)(SnapLink); +export default connect(mapStateToProps)(SnapLinkCard); From c0f3dcb612f0a8ee43c17e97aed2dce16efce9cd Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Tue, 30 Apr 2024 17:59:50 +0100 Subject: [PATCH 3/9] Wire recipe cards to recipe data --- app/model/editions/EditionsClientCollection.scala | 4 ++-- fronts-client/src/bundles/recipesBundle.ts | 7 +++++-- .../src/components/card/recipe/RecipeCard.tsx | 13 +++++++++---- .../src/components/feed/RecipeSearchContainer.tsx | 4 ++-- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/app/model/editions/EditionsClientCollection.scala b/app/model/editions/EditionsClientCollection.scala index 19ca9ee5cf6..c55b19f4dd3 100644 --- a/app/model/editions/EditionsClientCollection.scala +++ b/app/model/editions/EditionsClientCollection.scala @@ -11,7 +11,7 @@ case class EditionsClientCard(id: String, cardType: Option[CardType], frontPubli object EditionsClientCard { def fromCard(card: EditionsCard): EditionsClientCard = { EditionsClientCard( - "internal-code/page/" + card.id, + card.id, Some(card.cardType), card.addedOn, card.metadata.map(ClientCardMetadata.fromCardMetadata) @@ -19,7 +19,7 @@ object EditionsClientCard { } def toCard(card: EditionsClientCard): EditionsCard = { EditionsCard( - card.id.split("/").last, + card.id, card.cardType.getOrElse(CardType.Article), card.frontPublicationDate, card.meta.map(_.toCardMetadata) diff --git a/fronts-client/src/bundles/recipesBundle.ts b/fronts-client/src/bundles/recipesBundle.ts index 9ad72570d6f..62be0353418 100644 --- a/fronts-client/src/bundles/recipesBundle.ts +++ b/fronts-client/src/bundles/recipesBundle.ts @@ -3,11 +3,14 @@ import { Recipe } from 'types/Recipe'; import recipe1 from './fixtures/recipe1.json'; import recipe2 from './fixtures/recipe2.json'; -type RecipesState = Recipe[]; +type RecipesState = Record; export const { actions, reducer, selectors } = createAsyncResourceBundle('recipes', { indexById: true, // Add stub data in the absence of proper search data. - initialData: [recipe1, recipe2], + initialData: { + [recipe1.id]: recipe1, + [recipe2.id]: recipe2, + }, }); diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx index aebade27247..63276e8c0d4 100644 --- a/fronts-client/src/components/card/recipe/RecipeCard.tsx +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -9,6 +9,8 @@ import CardHeading from '../CardHeading'; import { selectCard } from 'selectors/shared'; import { connect } from 'react-redux'; import { State } from 'types/State'; +import { selectors as recipeSelectors } from 'bundles/recipesBundle'; +import { Recipe } from 'types/Recipe'; interface ContainerProps { onDragStart?: (d: React.DragEvent) => void; @@ -29,7 +31,7 @@ interface ContainerProps { interface RecipeProps extends ContainerProps { card: Card; - featureFlagPageViewData: boolean; + recipe: Recipe; } const RecipeCardComponent = ({ @@ -44,7 +46,7 @@ const RecipeCardComponent = ({ isUneditable, collectionId, frontId, - featureFlagPageViewData, + recipe, ...rest }: RecipeProps) => { return ( @@ -60,7 +62,7 @@ const RecipeCardComponent = ({ Recipe - Recipe heading + {recipe.title} @@ -69,8 +71,11 @@ const RecipeCardComponent = ({ }; const mapStateToProps = (state: State, props: ContainerProps) => { + const card = selectCard(state, props.id); + return { - card: selectCard(state, props.id), + card, + recipe: recipeSelectors.selectById(state, card.id), }; }; diff --git a/fronts-client/src/components/feed/RecipeSearchContainer.tsx b/fronts-client/src/components/feed/RecipeSearchContainer.tsx index 72ba2c29236..29ca71628c6 100644 --- a/fronts-client/src/components/feed/RecipeSearchContainer.tsx +++ b/fronts-client/src/components/feed/RecipeSearchContainer.tsx @@ -27,7 +27,7 @@ const FixedContentContainer = styled.div` interface Props { rightHandContainer?: React.ReactElement; - recipes: Recipe[]; + recipes: Record; } const FeastSearchContainerComponent = ({ @@ -48,7 +48,7 @@ const FeastSearchContainerComponent = ({ - {recipes.map((recipe) => ( + {Object.values(recipes).map((recipe) => ( ))} From 1cfebba20aa7d94b002ebb947707d5e6ed49207a Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Tue, 30 Apr 2024 18:16:27 +0100 Subject: [PATCH 4/9] Add recipe label to feed item --- .../src/components/feed/ArticleFeedItem.tsx | 12 +++--------- fronts-client/src/components/feed/RecipeFeedItem.tsx | 2 ++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/fronts-client/src/components/feed/ArticleFeedItem.tsx b/fronts-client/src/components/feed/ArticleFeedItem.tsx index 4869e9cac03..63dc1f54aed 100644 --- a/fronts-client/src/components/feed/ArticleFeedItem.tsx +++ b/fronts-client/src/components/feed/ArticleFeedItem.tsx @@ -19,13 +19,7 @@ import { Dispatch } from '../../types/Store'; import { insertCardWithCreate } from '../../actions/Cards'; import { connect } from 'react-redux'; import { FeedItem } from './FeedItem'; - -const TagInfo = styled.div` - padding-top: 2px; - font-size: 12px; - font-family: TS3TextSans; - font-weight: bold; -`; +import { ContentInfo } from './ContentInfo'; const Tone = styled.span` font-weight: normal; @@ -74,7 +68,7 @@ const ArticleFeedItemComponent = ({ handleDragStart={handleDragStart} metaContent={ <> - / {startCase(article.frontsMeta.tone)} )} - + } diff --git a/fronts-client/src/components/feed/RecipeFeedItem.tsx b/fronts-client/src/components/feed/RecipeFeedItem.tsx index a3bb9c65fe2..ee4c4eb95fa 100644 --- a/fronts-client/src/components/feed/RecipeFeedItem.tsx +++ b/fronts-client/src/components/feed/RecipeFeedItem.tsx @@ -9,6 +9,7 @@ import { import noop from 'lodash/noop'; import { selectFeatureValue } from '../../selectors/featureSwitchesSelectors'; import { connect } from 'react-redux'; +import { ContentInfo } from './ContentInfo'; interface ComponentProps { recipe: Recipe; @@ -40,6 +41,7 @@ export const RecipeFeedItemComponent = ({ handleDragStart={handleDragStart} onAddToClipboard={noop} shouldObscureFeed={shouldObscureFeed} + metaContent={Recipe} /> ); }; From b658fa804affdab5f702970622965bd04cfe8271 Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Wed, 1 May 2024 12:21:19 +0100 Subject: [PATCH 5/9] Add additional styling --- .../src/components/card/recipe/RecipeCard.tsx | 39 ++++++++++++------- .../src/components/feed/ContentInfo.tsx | 8 ++++ 2 files changed, 33 insertions(+), 14 deletions(-) create mode 100644 fronts-client/src/components/feed/ContentInfo.tsx diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx index 63276e8c0d4..f5955b7469f 100644 --- a/fronts-client/src/components/card/recipe/RecipeCard.tsx +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -11,6 +11,10 @@ import { connect } from 'react-redux'; import { State } from 'types/State'; import { selectors as recipeSelectors } from 'bundles/recipesBundle'; import { Recipe } from 'types/Recipe'; +import CardBody from '../CardBody'; +import CardMetaContainer from '../CardMetaContainer'; +import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper'; +import { ThumbnailSmall } from 'components/image/Thumbnail'; interface ContainerProps { onDragStart?: (d: React.DragEvent) => void; @@ -51,21 +55,28 @@ const RecipeCardComponent = ({ }: RecipeProps) => { return ( - - - + + Recipe - - {recipe.title} - - - + + + + + + {recipe.title} + + + + + + + ); }; diff --git a/fronts-client/src/components/feed/ContentInfo.tsx b/fronts-client/src/components/feed/ContentInfo.tsx new file mode 100644 index 00000000000..88e021191d5 --- /dev/null +++ b/fronts-client/src/components/feed/ContentInfo.tsx @@ -0,0 +1,8 @@ +import { styled } from 'constants/theme'; + +export const ContentInfo = styled.div` + padding-top: 2px; + font-size: 12px; + font-family: TS3TextSans; + font-weight: bold; +`; From 326990419fc7d3fd62805100976729281505e638 Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Wed, 1 May 2024 12:32:50 +0100 Subject: [PATCH 6/9] Add difficulty level to card --- fronts-client/src/bundles/fixtures/recipe1.json | 1 + fronts-client/src/components/card/recipe/RecipeCard.tsx | 5 +++++ fronts-client/src/types/Recipe.ts | 1 + 3 files changed, 7 insertions(+) diff --git a/fronts-client/src/bundles/fixtures/recipe1.json b/fronts-client/src/bundles/fixtures/recipe1.json index cb4bc311d4e..05c6646533e 100644 --- a/fronts-client/src/bundles/fixtures/recipe1.json +++ b/fronts-client/src/bundles/fixtures/recipe1.json @@ -8,6 +8,7 @@ ], "cuisineIds": [], "description": "The words “crab” and “butterscotch” aren’t often put together, but trust me on this one. Mixed with egg or mayonnaise, this also makes a great tart or sandwich filling, incidentally.", + "difficultyLevel": "easy", "featuredImage": { "url": "https://i.guim.co.uk/img/media/67f51e74f91032bd893349aa3a8c66ad55659bdd/30_111_2949_3201/master/2949.jpg?width=1600&dpr=1&s=none", "mediaId": "67f51e74f91032bd893349aa3a8c66ad55659bdd", diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx index f5955b7469f..2e55f527754 100644 --- a/fronts-client/src/components/card/recipe/RecipeCard.tsx +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -15,6 +15,8 @@ import CardBody from '../CardBody'; import CardMetaContainer from '../CardMetaContainer'; import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper'; import { ThumbnailSmall } from 'components/image/Thumbnail'; +import CardMetaContent from '../CardMetaContent'; +import { upperFirst } from 'lodash'; interface ContainerProps { onDragStart?: (d: React.DragEvent) => void; @@ -58,6 +60,9 @@ const RecipeCardComponent = ({ Recipe + + {upperFirst(recipe.difficultyLevel)} + Date: Wed, 1 May 2024 14:22:09 +0100 Subject: [PATCH 7/9] Use hooks, not connect, for redux state --- fronts-client/src/bundles/recipesBundle.ts | 4 +- .../src/components/card/recipe/RecipeCard.tsx | 52 ++++++++----------- .../lib/createAsyncResourceBundle/index.ts | 2 +- 3 files changed, 23 insertions(+), 35 deletions(-) diff --git a/fronts-client/src/bundles/recipesBundle.ts b/fronts-client/src/bundles/recipesBundle.ts index 62be0353418..396d7491714 100644 --- a/fronts-client/src/bundles/recipesBundle.ts +++ b/fronts-client/src/bundles/recipesBundle.ts @@ -3,10 +3,8 @@ import { Recipe } from 'types/Recipe'; import recipe1 from './fixtures/recipe1.json'; import recipe2 from './fixtures/recipe2.json'; -type RecipesState = Record; - export const { actions, reducer, selectors } = - createAsyncResourceBundle('recipes', { + createAsyncResourceBundle('recipes', { indexById: true, // Add stub data in the absence of proper search data. initialData: { diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx index 2e55f527754..48f1b0d416e 100644 --- a/fronts-client/src/components/card/recipe/RecipeCard.tsx +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -7,18 +7,17 @@ import CardHeadingContainer from '../CardHeadingContainer'; import CardMetaHeading from '../CardMetaHeading'; import CardHeading from '../CardHeading'; import { selectCard } from 'selectors/shared'; -import { connect } from 'react-redux'; import { State } from 'types/State'; import { selectors as recipeSelectors } from 'bundles/recipesBundle'; -import { Recipe } from 'types/Recipe'; import CardBody from '../CardBody'; import CardMetaContainer from '../CardMetaContainer'; import ImageAndGraphWrapper from 'components/image/ImageAndGraphWrapper'; import { ThumbnailSmall } from 'components/image/Thumbnail'; import CardMetaContent from '../CardMetaContent'; import { upperFirst } from 'lodash'; +import { useSelector } from 'react-redux'; -interface ContainerProps { +interface Props { onDragStart?: (d: React.DragEvent) => void; onDrop?: (d: React.DragEvent) => void; onDelete?: (uuid: string) => void; @@ -33,14 +32,10 @@ interface ContainerProps { fade?: boolean; children?: React.ReactNode; isUneditable?: boolean; + showMeta?: boolean; } -interface RecipeProps extends ContainerProps { - card: Card; - recipe: Recipe; -} - -const RecipeCardComponent = ({ +export const RecipeCard = ({ id, fade, size = 'default', @@ -48,22 +43,28 @@ const RecipeCardComponent = ({ onDelete, onAddToClipboard, children, - card, isUneditable, collectionId, frontId, - recipe, + showMeta, ...rest -}: RecipeProps) => { +}: Props) => { + const card = useSelector((state) => selectCard(state, id)); + const recipe = useSelector((state) => + recipeSelectors.selectById(state, card.id) + ); + return ( - - Recipe - - {upperFirst(recipe.difficultyLevel)} - - + {showMeta && ( + + Recipe + + {upperFirst(recipe?.difficultyLevel)} + + + )} - {recipe.title} + {recipe?.title ?? 'No recipe found'} - + ); }; - -const mapStateToProps = (state: State, props: ContainerProps) => { - const card = selectCard(state, props.id); - - return { - card, - recipe: recipeSelectors.selectById(state, card.id), - }; -}; - -export const RecipeCard = connect(mapStateToProps)(RecipeCardComponent); diff --git a/fronts-client/src/lib/createAsyncResourceBundle/index.ts b/fronts-client/src/lib/createAsyncResourceBundle/index.ts index 5b9ee66c95d..7e5de795ba2 100644 --- a/fronts-client/src/lib/createAsyncResourceBundle/index.ts +++ b/fronts-client/src/lib/createAsyncResourceBundle/index.ts @@ -208,7 +208,7 @@ function createAsyncResourceBundle( // e.g.the resource 'books' namespaced with 'shared' becomes SHARED/BOOKS namespace?: string; // The initial state of the reducer data. Defaults to an empty object. - initialData?: Resource; + initialData?: unknown; } = { indexById: false, } From 95115a4b0fb4be69099a9a539221ed7d3c441dcf Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Wed, 1 May 2024 16:05:11 +0100 Subject: [PATCH 8/9] Ensure recipe meta is not displayed in clipboard --- .../src/components/FrontsEdit/CollectionComponents/Card.tsx | 3 ++- fronts-client/src/components/card/article/ArticleCard.tsx | 2 +- fronts-client/src/components/card/recipe/RecipeCard.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx b/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx index 85401bf4463..a460e2b6d4c 100644 --- a/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx +++ b/fronts-client/src/components/FrontsEdit/CollectionComponents/Card.tsx @@ -96,7 +96,7 @@ type CardContainerProps = ContainerProps & { addImageToCard: (id: string, response: ValidationResponse) => void; updateCardMeta: (id: string, meta: CardMeta) => void; clearCardSelection: (id: string) => void; - type: CardTypes; + type?: CardTypes; isSelected: boolean; numSupportingArticles: number; editMode: EditMode; @@ -218,6 +218,7 @@ class Card extends React.Component { onClick={isUneditable ? undefined : () => onSelect(uuid)} size={size} textSize={textSize} + showMeta={showMeta} /> {getSublinks} diff --git a/fronts-client/src/components/card/article/ArticleCard.tsx b/fronts-client/src/components/card/article/ArticleCard.tsx index 613d0f16b2d..2e8a90d44cb 100644 --- a/fronts-client/src/components/card/article/ArticleCard.tsx +++ b/fronts-client/src/components/card/article/ArticleCard.tsx @@ -195,6 +195,6 @@ const createMapStateToProps = () => { }; }; -export { ArticleComponentProps, ArticleCard as ArticleComponent }; +export { ArticleComponentProps, ArticleCard }; export default connect(createMapStateToProps)(ArticleCard); diff --git a/fronts-client/src/components/card/recipe/RecipeCard.tsx b/fronts-client/src/components/card/recipe/RecipeCard.tsx index 48f1b0d416e..70bef5e7dbc 100644 --- a/fronts-client/src/components/card/recipe/RecipeCard.tsx +++ b/fronts-client/src/components/card/recipe/RecipeCard.tsx @@ -46,7 +46,7 @@ export const RecipeCard = ({ isUneditable, collectionId, frontId, - showMeta, + showMeta = true, ...rest }: Props) => { const card = useSelector((state) => selectCard(state, id)); From 4768accc373612922d8cc5b9d3eddb2c7e7e952a Mon Sep 17 00:00:00 2001 From: Jonathon Herbert Date: Thu, 9 May 2024 11:33:03 +0100 Subject: [PATCH 9/9] Conditionally apply article id logic to client card ids --- app/model/editions/EditionsClientCollection.scala | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/app/model/editions/EditionsClientCollection.scala b/app/model/editions/EditionsClientCollection.scala index c55b19f4dd3..25df85b1e32 100644 --- a/app/model/editions/EditionsClientCollection.scala +++ b/app/model/editions/EditionsClientCollection.scala @@ -10,16 +10,26 @@ case class EditionsClientCard(id: String, cardType: Option[CardType], frontPubli object EditionsClientCard { def fromCard(card: EditionsCard): EditionsClientCard = { + val id = card.cardType match { + case CardType.Article => "internal-code/page/" + card.id + case _ => card.id + } + EditionsClientCard( - card.id, + id, Some(card.cardType), card.addedOn, card.metadata.map(ClientCardMetadata.fromCardMetadata) ) } def toCard(card: EditionsClientCard): EditionsCard = { + val id = card.cardType match { + case Some(CardType.Article) | None => card.id.split("/").last + case _ => card.id + } + EditionsCard( - card.id, + id, card.cardType.getOrElse(CardType.Article), card.frontPublicationDate, card.meta.map(_.toCardMetadata)