diff --git a/src/app/components/book-tile/book-tile-display.tsx b/src/app/components/book-tile/book-tile-display.tsx new file mode 100644 index 000000000..344799135 --- /dev/null +++ b/src/app/components/book-tile/book-tile-display.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import PromoteBadge from '~/components/promote-badge/promote-badge'; +import type {Item} from '~/models/book-titles'; +import {Book as BookInfo} from '~/pages/subjects/new/specific/context'; +import GetTheBookDropdown from './dropdown-menu'; +import cn from 'classnames'; +import './book-tile.scss'; + +type AdditionalFields = Partial<{ + promoteSnippet: Item['promote_snippet']; + bookState: string; +}> + +export default function BookTile({book}: {book: BookInfo & AdditionalFields}) { + const {coverUrl, title, slug} = book; + const comingSoon = book.bookState === 'coming_soon'; + const snippets = book.promoteSnippet?.filter((s) => s.value.image); + const promoteSnippet = snippets?.find((s) => s.value.image); + const classes = cn({ + 'book-tile': true, + 'coming-soon': comingSoon, + promote: Boolean(promoteSnippet) + }); + + return ( +
+ + + {promoteSnippet?.value.image && ( + + )} +
+ {title} +
+
+ {comingSoon ? ( +
+ +
+ ) : ( + + )} +
+ ); +} diff --git a/src/app/components/book-tile/book-tile.tsx b/src/app/components/book-tile/book-tile.tsx index b471f2515..0dca94486 100644 --- a/src/app/components/book-tile/book-tile.tsx +++ b/src/app/components/book-tile/book-tile.tsx @@ -1,54 +1,19 @@ import React from 'react'; -import PromoteBadge from '~/components/promote-badge/promote-badge'; import bookPromise, {Item} from '~/models/book-titles'; import {Book as BookInfo} from '~/pages/subjects/new/specific/context'; -import GetTheBookDropdown from './dropdown-menu'; -import cn from 'classnames'; import './book-tile.scss'; +import BookTileDisplay from './book-tile-display'; -// eslint-disable-next-line complexity export default function BookTile({book: [book]}: {book: [BookInfo]}) { - const {coverUrl, title, slug} = book; const info = useBookInfo(book.id); - const comingSoon = info?.book_state === 'coming_soon'; - const snippets = info?.promote_snippet.filter((s) => s.value.image); - const promoteSnippet = snippets?.find((s) => s.value.image); - const classes = cn({ - 'book-tile': true, - 'coming-soon': comingSoon, - promote: Boolean(promoteSnippet) - }); - return ( -
- - - {promoteSnippet?.value.image && ( - - )} -
- {title} -
-
- {comingSoon ? ( -
- -
- ) : ( - - )} -
- ); + return ; } function useBookInfo(id: number) { diff --git a/src/app/pages/flex-page/blocks/BookListBlock.scss b/src/app/pages/flex-page/blocks/BookListBlock.scss new file mode 100644 index 000000000..6cb6e2b22 --- /dev/null +++ b/src/app/pages/flex-page/blocks/BookListBlock.scss @@ -0,0 +1,16 @@ +@import 'pattern-library/core/pattern-library/headers'; + +.flex-page.page div.content-block-book-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); + margin: $normal-margin 0; + width: 100%; + + @include width-up-to($phone-max) { + grid-gap: 1rem; + } + + @include wider-than($phone-max) { + grid-gap: 4rem 2rem; + } +} diff --git a/src/app/pages/flex-page/blocks/BookListBlock.tsx b/src/app/pages/flex-page/blocks/BookListBlock.tsx new file mode 100644 index 000000000..5dfb531ed --- /dev/null +++ b/src/app/pages/flex-page/blocks/BookListBlock.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import cn from 'classnames'; +import './BookListBlock.scss'; +import BookTile from '~/components/book-tile/book-tile-display'; + +/* + * the book data formatting in the CMS is currently fragemented, + * when it gets centralized we can centralize the types as well + */ +export type BookInfo = { + id: number; + slug: string; + title: string; + webviewRexLink: string; + webviewLink: string; + highResolutionPdfUrl: string; + lowResolutionPdfUrl: string; + coverUrl: string; + bookState: string; + promoteSnippet: { + value: { + id: number; + description: string; + image: string; + name: string; + }; + }[]; +}; + +export type BookListBlockConfig = { + id: string; + type: 'book_list'; + value: { + books: BookInfo[]; + }; +}; + +export function BookListBlock({data}: {data: BookListBlockConfig}) { + return ( +
+ {data.value.books.map((book) => )} +
+ ); +} diff --git a/src/app/pages/flex-page/blocks/ContentBlock.tsx b/src/app/pages/flex-page/blocks/ContentBlock.tsx index d1a6907a4..780f3f17f 100644 --- a/src/app/pages/flex-page/blocks/ContentBlock.tsx +++ b/src/app/pages/flex-page/blocks/ContentBlock.tsx @@ -9,6 +9,7 @@ import { HTMLBlockConfig, HTMLBlock } from './HTMLBlock'; import { LinksBlockConfig, LinksBlock } from './LinksBlock'; import { QuoteBlock, QuoteBlockConfig } from './QuoteBlock'; import { FAQBlockConfig, FAQBlock } from './FAQBlock'; +import { BookListBlockConfig, BookListBlock } from './BookListBlock'; export type ContentBlockConfig = LinksBlockConfig | @@ -20,6 +21,7 @@ export type ContentBlockConfig = RichTextBlockConfig | QuoteBlockConfig | FAQBlockConfig | + BookListBlockConfig | CardsBlockConfig; export function ContentBlocks({data}: {data: ContentBlockConfig[]}) { @@ -51,6 +53,8 @@ export function ContentBlock({data}: {data: ContentBlockConfig}) { return ; case 'faq': return ; + case 'book_list': + return ; default: return
{JSON.stringify(data, null, 2)}
; } diff --git a/src/app/pages/flex-page/blocks/RichTextBlock.scss b/src/app/pages/flex-page/blocks/RichTextBlock.scss index df0810030..891972590 100644 --- a/src/app/pages/flex-page/blocks/RichTextBlock.scss +++ b/src/app/pages/flex-page/blocks/RichTextBlock.scss @@ -9,12 +9,12 @@ width: 100%; height: auto; } - + img.richtext-image.left { float: left; margin-right: $normal-margin; } - + img.richtext-image.right { float: right; margin-left: $normal-margin; @@ -23,7 +23,7 @@ > *:first-child { margin-top: 0; } - > *:last-child { + > *:not(h1, h2, h3, h4, h5, h6):last-child { margin-bottom: 0; } diff --git a/src/app/pages/flex-page/flex-page.scss b/src/app/pages/flex-page/flex-page.scss new file mode 100644 index 000000000..936bf92dd --- /dev/null +++ b/src/app/pages/flex-page/flex-page.scss @@ -0,0 +1,4 @@ + +.page.flex-page { + overflow: visible; +} diff --git a/src/app/pages/flex-page/flex-page.tsx b/src/app/pages/flex-page/flex-page.tsx index 765809fba..737cb5579 100644 --- a/src/app/pages/flex-page/flex-page.tsx +++ b/src/app/pages/flex-page/flex-page.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { LoadedPage } from '~/components/jsx-helpers/loader-page'; import { ContentBlocks, ContentBlockConfig } from './blocks/ContentBlock'; +import './flex-page.scss'; type Data = { body: ContentBlockConfig[]; diff --git a/test/src/pages/flex-page.test.tsx b/test/src/pages/flex-page.test.tsx index 131ac72c3..e838c2973 100644 --- a/test/src/pages/flex-page.test.tsx +++ b/test/src/pages/flex-page.test.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import ShellContextProvider from '~/../../test/helpers/shell-context'; import {render, screen} from '@testing-library/preact'; import {describe, it} from '@jest/globals'; import userEvent from '@testing-library/user-event'; @@ -38,9 +39,11 @@ let body: Data['body']; function Component() { return ( - - - + + + + + ); } @@ -156,6 +159,11 @@ describe('flex-page', () => { expect(screen.getAllByText('Some text with')).toHaveLength(1); expect(screen.getAllByText('formatting')).toHaveLength(1); }); + it('renders bookListBlock', () => { + body = [bookListBlock()]; + render(); + expect(screen.getAllByText('book title')).toHaveLength(1); + }); }); function imageBlock(name: string) { @@ -338,3 +346,26 @@ function sectionBlock(): ContentBlockConfig { } }; } + +function bookListBlock(): ContentBlockConfig { + return { + id: 'book-list-id', + type: 'book_list', + value: { + books: [ + { + id: 1, + slug: 'book-slug', + title: 'book title', + webviewRexLink: 'webview-rex-link', + webviewLink: 'webview-link', + highResolutionPdfUrl: 'high-res-url', + lowResolutionPdfUrl: 'low-res-url', + coverUrl: 'cover-url', + bookState: 'book-state', + promoteSnippet: [], + } + ] + } + }; +}