diff --git a/common/views/slices/ContentList/index.tsx b/common/views/slices/ContentList/index.tsx index f322ba0e6c..e9291af797 100644 --- a/common/views/slices/ContentList/index.tsx +++ b/common/views/slices/ContentList/index.tsx @@ -19,7 +19,8 @@ const ContentListSlice: FunctionComponent = ({ slice, context, }) => { - const options = { ...defaultContext, context }; + const options = { ...defaultContext, ...context }; + const transformedSlice = transformContentListSlice(slice); if (!options.isLanding) { return ( diff --git a/common/views/slices/EditorialImage/index.tsx b/common/views/slices/EditorialImage/index.tsx index 9b3e51eba8..6f57456d73 100644 --- a/common/views/slices/EditorialImage/index.tsx +++ b/common/views/slices/EditorialImage/index.tsx @@ -22,7 +22,7 @@ const EditorialImageSlice: FunctionComponent = ({ }) => { const transformedSlice = transformEditorialImageSlice(slice); - const options = { ...defaultContext, context }; + const options = { ...defaultContext, ...context }; // TODO: use one layout for all image weights if/when it's established // that width isn't an adequate means to illustrate a difference return ( diff --git a/content/webapp/components/Body/Body.tsx b/content/webapp/components/Body/Body.tsx index 1dc52aa42e..d178a74895 100644 --- a/content/webapp/components/Body/Body.tsx +++ b/content/webapp/components/Body/Body.tsx @@ -57,6 +57,7 @@ import TextAndImageOrIcons from '../TextAndImageOrIcons'; import { SliceZone } from '@prismicio/react'; import { components } from '@weco/common/views/slices'; import { useToggles } from '@weco/common/server-data/Context'; +import { Slice } from '@weco/content/types/body'; const BodyWrapper = styled.div<{ $splitBackground: boolean }>` ${props => @@ -171,14 +172,30 @@ const Body: FunctionComponent = ({ comicPreviousNext, contentType, }: Props) => { + const isFirstFeaturedTextSliceFromBody = (slice, i) => + i === 0 && slice.type === 'text' && slice.weight === 'featured'; + const isFirstFeaturedTextSliceFromUntransformedBody = (slice, i) => + i === 0 && slice.slice_type === 'text' && slice.slice_label === 'featured'; + const { sliceMachine } = useToggles(); + const featuredTextFromBody = body.find( + isFirstFeaturedTextSliceFromBody + ) as Slice<'text', prismic.RichTextField>; + const featuredTextFromUntransformedBody = untransformedBody.find( + isFirstFeaturedTextSliceFromUntransformedBody + ) as prismic.Slice<'text', { text: prismic.RichTextField }>; + const filteredBody = body + .filter((slice, i) => !isFirstFeaturedTextSliceFromBody(slice, i)) .filter(slice => !(slice.type === 'picture' && slice.weight === 'featured')) // The standfirst is now put into the header // and used exclusively by articles / article series .filter(slice => slice.type !== 'standfirst'); const filteredUntransformedBody = untransformedBody + .filter( + (slice, i) => !isFirstFeaturedTextSliceFromUntransformedBody(slice, i) + ) .filter( slice => !( @@ -221,122 +238,99 @@ const Body: FunctionComponent = ({ featuredCardText: 'black', }, ]; - const AdditionalContent = ({ - index, - sections = [], - isLanding = false, - staticContent, - }: { - index: number; + + const LandingPageSections: FunctionComponent<{ sections: ContentListSlice[]; - isLanding: boolean; - staticContent: ReactElement | null; - }): ReactElement | null => { - if (index === 0) { - return ( - <> - {staticContent} - {onThisPage && onThisPage.length > 2 && showOnThisPage && ( - - - - - - )} - {isLanding && - sections.map((section, index) => { - const isFirst = index === 0; - const isLast = index === sections.length - 1; - const sectionTheme = sectionThemes[index % sectionThemes.length]; - const hasFeatured = section.value.items.length === 1; - const firstItem = section.value.items?.[0]; - const isCardType = firstItem?.type === 'card'; - - const firstItemProps = - firstItem && - (isCardType - ? convertCardToFeaturedCardProps(firstItem) - : convertItemToFeaturedCardProps(firstItem)); - - const cardItems = hasFeatured - ? section.value.items.slice(1) - : section.value.items; - const featuredItem = - hasFeatured && firstItem ? ( - -

{firstItem.title}

- {isCardType && firstItem.description && ( -

{firstItem.description}

- )} - {'promo' in firstItem && firstItem.promo && ( -

- {firstItem.promo.caption} -

- )} -
- ) : null; - - const cards = cardItems.map((item, i) => { - const cardProps = - item.type === 'card' ? item : convertItemToCardProps(item); - return ; - }); - - return ( - - {!isFirst && ( - - )} - - {section.value.title && ( - - - - )} - {featuredItem && ( - - {featuredItem} - - )} - {cards.length > 0 && ( - - )} - - {!isLast && } - - ); - })} - - ); - } else { - return null; - } - }; + }> = ({ sections = [] }) => ( + <> + {sections.map((section, index) => { + const isFirst = index === 0; + const isLast = index === sections.length - 1; + const sectionTheme = sectionThemes[index % sectionThemes.length]; + const hasFeatured = section.value.items.length === 1; + const firstItem = section.value.items?.[0]; + const isCardType = firstItem?.type === 'card'; + + const firstItemProps = + firstItem && + (isCardType + ? convertCardToFeaturedCardProps(firstItem) + : convertItemToFeaturedCardProps(firstItem)); + + const cardItems = hasFeatured + ? section.value.items.slice(1) + : section.value.items; + const featuredItem = + hasFeatured && firstItem ? ( + +

{firstItem.title}

+ {isCardType && firstItem.description && ( +

{firstItem.description}

+ )} + {'promo' in firstItem && firstItem.promo && ( +

{firstItem.promo.caption}

+ )} +
+ ) : null; + + const cards = cardItems.map((item, i) => { + const cardProps = + item.type === 'card' ? item : convertItemToCardProps(item); + return ; + }); + + return ( + + {!isFirst && ( + + )} + + {section.value.title && ( + + + + )} + {featuredItem && ( + + {featuredItem} + + )} + {cards.length > 0 && ( + + )} + + {!isLast && } + + ); + })} + + ); + const isShortFilm = contentType === 'short-film'; const isVisualStory = contentType === 'visual-story'; @@ -345,15 +339,40 @@ const Body: FunctionComponent = ({ className={`content-type-${contentType}`} $splitBackground={isShortFilm} > - {filteredBody.length < 1 && ( - + {featuredTextFromBody && featuredTextFromUntransformedBody && ( + +
+ + + +
+
+ )} + + {staticContent} + + {onThisPage && onThisPage.length > 2 && showOnThisPage && ( + + + + + )} + {isLanding && } + {sliceMachine && ( = ({ {!sliceMachine && filteredBody.map((slice, i) => ( - {/* If the first slice is featured text we display it and any static content, i.e. */} - {i === 0 && - slice.type === 'text' && - slice.weight === 'featured' && ( - -
- - - -
-
- )} - - {!( - i === 0 && - slice.type === 'text' && - slice.weight === 'featured' - ) && ( - <> - {slice.type === 'text' && ( - - -
- {slice.weight !== 'featured' && - (firstTextSliceIndex === i && isDropCapped ? ( - <> - {/* + {slice.type === 'text' && ( + + +
+ {slice.weight !== 'featured' && + (firstTextSliceIndex === i && isDropCapped ? ( + <> + {/* The featured text slice can contain multiple paragraphs, e.g. https://wellcomecollection.org/articles/XcMBBREAACUAtBoV The drop cap serializer will see them as two separate paragraphs, so we have to split out the first paragraph here. */} - - - - ) : ( - - ))} -
-
-
- )} + + + + ) : ( + + ))} +
+
+
+ )} - {slice.type === 'textAndImage' && ( - - - - - - )} + {slice.type === 'textAndImage' && ( + + + + + + )} - {slice.type === 'textAndIcons' && ( - - - - - - )} + {slice.type === 'textAndIcons' && ( + + + + + + )} - {/* TODO: use one layout for all image weights if/when it's established + {/* TODO: use one layout for all image weights if/when it's established that width isn't an adequate means to illustrate a difference */} - {slice.type === 'picture' && slice.weight === 'default' && ( - - - - - - )} - {slice.type === 'picture' && slice.weight === 'standalone' && ( - - - - - - )} - {slice.type === 'picture' && slice.weight === 'supporting' && ( - - - - - - )} - {slice.type === 'imageGallery' && ( - - + + + + + )} + {slice.type === 'picture' && slice.weight === 'standalone' && ( + + + + + + )} + {slice.type === 'picture' && slice.weight === 'supporting' && ( + + + + + + )} + {slice.type === 'imageGallery' && ( + + + + )} + {slice.type === 'quote' && ( + + + + + + )} + {slice.type === 'titledTextList' && ( + + + + + + )} + {slice.type === 'contentList' && !isLanding && ( + + + {/* FIXME: this makes what-we-do contentLists synchronous, but it's hacky. */} + {pageId === prismicPageIds.whatWeDo ? ( + - - )} - {slice.type === 'quote' && ( - - - - - - )} - {slice.type === 'titledTextList' && ( - - - - - - )} - {slice.type === 'contentList' && !isLanding && ( - - - {/* FIXME: this makes what-we-do contentLists synchronous, but it's hacky. */} - {pageId === prismicPageIds.whatWeDo ? ( - - ) : ( - - 'id' in item ? `id:${item.id}` : undefined - ) - .filter(isNotUndefined) - .join(' ')} - /> - )} - - - )} - {slice.type === 'searchResults' && ( - - - - - - )} - {slice.type === 'videoEmbed' && ( - - - - - - )} - {slice.type === 'soundcloudEmbed' && ( - - - - - - )} - {slice.type === 'map' && ( - - - - - - )} - {slice.type === 'gifVideo' && ( - - - - - - )} - {slice.type === 'iframe' && ( - - -