From 3302b7dc8c694a8146146574101fc3cb479905a8 Mon Sep 17 00:00:00 2001 From: Kevin Koech Date: Fri, 14 Jun 2024 17:19:38 +0300 Subject: [PATCH 1/4] Embed Featured Videos --- .../src/payload/blocks/EmbeddedPlaylist.js | 231 ++++-------------- .../src/payload/blocks/FeaturedVideos.js | 21 ++ .../src/payload/collections/Pages.js | 112 ++++----- .../youtubeSelect/youtubePlaylistSelect.js | 139 +++++++++++ .../fields/youtubeSelect/youtubeSelect.js | 38 ++- .../src/payload/utils/embeddedPlaylist.js | 49 ++-- 6 files changed, 304 insertions(+), 286 deletions(-) create mode 100644 apps/charterafrica/src/payload/blocks/FeaturedVideos.js create mode 100644 apps/charterafrica/src/payload/fields/youtubeSelect/youtubePlaylistSelect.js diff --git a/apps/charterafrica/src/payload/blocks/EmbeddedPlaylist.js b/apps/charterafrica/src/payload/blocks/EmbeddedPlaylist.js index c9845401a..452aeb078 100644 --- a/apps/charterafrica/src/payload/blocks/EmbeddedPlaylist.js +++ b/apps/charterafrica/src/payload/blocks/EmbeddedPlaylist.js @@ -1,200 +1,61 @@ import { slateEditor } from "@payloadcms/richtext-slate"; -import { select } from "payload/dist/fields/validations"; import richText from "../fields/richText"; -import YouTubeSelect from "../fields/youtubeSelect/youtubeSelect"; -import { - BLOCK_SLUG, - getEmbeddedPlaylist, - mapPlaylistLinkToId, -} from "../utils/embeddedPlaylist"; - -async function validateYouTubeSelect( - value, - { data: document, hasMany, required, t }, -) { - let options = []; - const { playlistId, queryString } = getEmbeddedPlaylist(document); - if (playlistId) { - const response = await fetch( - `${process.env.PAYLOAD_PUBLIC_APP_URL}/api/v1/opportunities/consultation/multimedia?${queryString}`, - ); - const data = await response.json(); - options = - data?.items?.map((item) => ({ - label: item?.snippet?.title, - value: item?.snippet?.resourceId?.videoId, - })) || []; - } - return select(value, { hasMany, options, required, t }); -} +import youtubePlaylistSelect from "../fields/youtubeSelect/youtubePlaylistSelect"; +import { BLOCK_SLUG } from "../utils/embeddedPlaylist"; const EmbeddedPlaylist = { - slug: BLOCK_SLUG, - labels: { - singular: { - en: "Embedded Playlist", - fr: "Playlist Intégrée", - pt: "Playlists incorporada", - }, - plural: { - en: "Embedded Playlist", - fr: "Playlist Intégrée", - pt: "Playlists incorporada", - }, - }, - fields: [ - { - type: "collapsible", - label: { - en: "Title & Description", - fr: "Titre & description", - pt: "Titulo & descrição", - }, - fields: [ - { - name: "title", - label: { - en: "Title", - fr: "Titre", - pt: "Título", - }, - type: "text", - localized: true, + slug: BLOCK_SLUG, + labels: { + singular: { + en: "Embedded Playlist", + fr: "Playlist Intégrée", + pt: "Playlists incorporada" }, - richText({ - name: "description", - label: { - en: "Description", - fr: "La description", - pt: "Descrição", - }, - localized: true, - editor: slateEditor({ - admin: { - elements: ["h3", "h4", "h5", "h6", "link", "ol", "ul", "indent"], - leaves: ["bold", "code", "italic", "underline"], - }, - }), - }), - ], + plural: { + en: "Embedded Playlist", + fr: "Playlist Intégrée", + pt: "Playlists incorporada" + } }, - { - type: "collapsible", - label: { - en: "Playlist", - }, - fields: [ + fields: [ { - type: "group", - name: "playlist", - fields: [ - { - name: "title", - label: { - en: "Title", - fr: "Titre", - pt: "Título", - }, - admin: { - description: () => "e.g. Previous Consultation", - }, - type: "text", - localized: true, + type: "collapsible", + label: { + en: "Title & Description", + fr: "Titre & description", + pt: "Titulo & descrição" }, - { - name: "link", - label: { - en: "URL", - }, - type: "text", - admin: { - description: () => - "YouTube playlist URL e.g https://www.youtube.com/watch?list= or https://www.youtube.com/playlist?list=", - }, - required: true, - }, - { - name: "playlistId", - admin: { - hidden: true, - }, - required: true, - hooks: { - beforeValidate: [mapPlaylistLinkToId], - }, - type: "text", - }, - ], - }, - { - name: "featured", - type: "group", - fields: [ - { - name: "featuredType", - label: { - en: "Show", - ft: "Afficher", - pt: "Mostrar", - }, - type: "radio", - options: [ + fields: [ { - label: { - en: "Most recent in the Playlist", - fr: "Le plus récent dans la playlist", - pt: "Mais recente na lista de reprodução", - }, - value: "latest", + name: "title", + label: { + en: "Title", + fr: "Titre", + pt: "Título" + }, + type: "text", + localized: true }, - { - label: { - en: "Custom selection", - fr: "Sélection personnalisée", - pt: "Seleção personalizada", - }, - value: "custom", - }, - { - label: { - en: "None", - ft: "Aucun", - pt: "Nenhum", - }, - value: "none", - }, - ], - defaultValue: "latest", - }, - { - name: "items", - label: { en: "Video(s)", fr: "Vidéo(s)", pt: "Vídeo(s)" }, - type: "select", - hasMany: true, - // These are just dummy options, YouTubeSelect loads (id, title) - // pairs from the playlist link provided above. - options: ["-"], - required: true, - validate: validateYouTubeSelect, - admin: { - description: () => - "Enter playlist URL above to select an audio/video", - components: { - Field: YouTubeSelect, - }, - condition: (_, siblingData) => - siblingData?.featuredType === "custom", - }, - }, - ], - admin: { - hideGutter: true, - }, + richText({ + name: "description", + label: { + en: "Description", + fr: "La description", + pt: "Descrição" + }, + localized: true, + editor: slateEditor({ + admin: { + elements: ["h3", "h4", "h5", "h6", "link", "ol", "ul", "indent"], + leaves: ["bold", "code", "italic", "underline"] + } + }) + }) + ] }, - ], - }, - ], + youtubePlaylistSelect(BLOCK_SLUG) + ] }; export default EmbeddedPlaylist; diff --git a/apps/charterafrica/src/payload/blocks/FeaturedVideos.js b/apps/charterafrica/src/payload/blocks/FeaturedVideos.js new file mode 100644 index 000000000..95d5084e7 --- /dev/null +++ b/apps/charterafrica/src/payload/blocks/FeaturedVideos.js @@ -0,0 +1,21 @@ +import youtubePlaylistSelect from "../fields/youtubeSelect/youtubePlaylistSelect"; + +const slug = "featured-videos"; +const FeaturedVideos = { + slug, + labels: { + singular: { + en: "Featured Video", + fr: "Vidéo en vedette", + pt: "Vídeo em destaque" + }, + plural: { + en: "Featured Videos", + fr: "vidéos liées", + pt: "Vídeos em Destaque" + } + }, + fields: [youtubePlaylistSelect(slug)] +}; + +export default FeaturedVideos; diff --git a/apps/charterafrica/src/payload/collections/Pages.js b/apps/charterafrica/src/payload/collections/Pages.js index 7bbd786de..3cecb8a45 100644 --- a/apps/charterafrica/src/payload/collections/Pages.js +++ b/apps/charterafrica/src/payload/collections/Pages.js @@ -10,6 +10,7 @@ import EmbeddedPlaylist from "../blocks/EmbeddedPlaylist"; import Error from "../blocks/Error"; import FAQ from "../blocks/FAQ"; import FeaturedPost from "../blocks/FeaturedPost"; +import FeaturedVideos from "../blocks/FeaturedVideos"; import FlourishChart from "../blocks/FlourishChart"; import Global from "../blocks/Global"; import Grantees from "../blocks/Grantees"; @@ -32,63 +33,64 @@ import slug from "../fields/slug"; import formatDraftUrl from "../utils/formatDraftUrl"; const Pages = { - slug: "pages", - admin: { - defaultColumns: ["fullTitle", "updatedAt"], - preview: (doc, options) => formatDraftUrl("pages", doc, options), - useAsTitle: "title", - }, - access: { - read: () => true, // Everyone can read Pages - }, - fields: [ - { - name: "title", - type: "text", - localized: true, - required: true, + slug: "pages", + admin: { + defaultColumns: ["fullTitle", "updatedAt"], + preview: (doc, options) => formatDraftUrl("pages", doc, options), + useAsTitle: "title" }, - fullTitle(), - slug(), - { - name: "blocks", - type: "blocks", - blocks: [ - AgaInfographic, - CommunityPlatforms, - Contributors, - Datasets, - DemocracyHelpdeskContent, - Documents, - Ecosystem, - EmbeddedDocuments, - EmbeddedPlaylist, - Error, - FAQ, - FeaturedPost, - FlourishChart, - Global, - GuidingPrincipals, - Grantees, - Hero, - Impressum, - LongForm, - Mooc, - Opportunities, - Organisations, - PageDescription, - PageHeader, - PageInfo, - Partners, - Resources, - Spotlight, - Tools, - ], - admin: { - initCollapsed: true, - }, + access: { + read: () => true // Everyone can read Pages }, - ], + fields: [ + { + name: "title", + type: "text", + localized: true, + required: true + }, + fullTitle(), + slug(), + { + name: "blocks", + type: "blocks", + blocks: [ + AgaInfographic, + CommunityPlatforms, + Contributors, + Datasets, + DemocracyHelpdeskContent, + Documents, + Ecosystem, + EmbeddedDocuments, + EmbeddedPlaylist, + Error, + FAQ, + FeaturedPost, + FlourishChart, + Global, + GuidingPrincipals, + Grantees, + Hero, + Impressum, + LongForm, + Mooc, + Opportunities, + Organisations, + PageDescription, + PageHeader, + PageInfo, + Partners, + Resources, + Spotlight, + Tools, + FeaturedVideos + ], + admin: { + initCollapsed: true + } + } + ] }; export default Pages; diff --git a/apps/charterafrica/src/payload/fields/youtubeSelect/youtubePlaylistSelect.js b/apps/charterafrica/src/payload/fields/youtubeSelect/youtubePlaylistSelect.js new file mode 100644 index 000000000..46206568e --- /dev/null +++ b/apps/charterafrica/src/payload/fields/youtubeSelect/youtubePlaylistSelect.js @@ -0,0 +1,139 @@ +import { select } from "payload/dist/fields/validations"; +import { createElement } from "react"; + +import { getEmbeddedPlaylist, mapPlaylistLinkToId } from "../../utils/embeddedPlaylist"; + +import YouTubeSelect from "./youtubeSelect"; + +async function validateYouTubeSelect(value, { data: document, hasMany, required, t }, blockSlug) { + let options = []; + const { playlistId, queryString } = getEmbeddedPlaylist(document, blockSlug); + if (playlistId) { + const response = await fetch( + `${process.env.PAYLOAD_PUBLIC_APP_URL}/api/v1/opportunities/consultation/multimedia?${queryString}` + ); + const data = await response.json(); + options = + data?.items?.map((item) => ({ + label: item?.snippet?.title, + value: item?.snippet?.resourceId?.videoId + })) || []; + } + return select(value, { hasMany, options, required, t }); +} + +export default function youtubePlaylistSelect(slug) { + return { + type: "collapsible", + label: { + en: "Playlist" + }, + fields: [ + { + type: "group", + name: "playlist", + fields: [ + { + name: "title", + label: { + en: "Title", + fr: "Titre", + pt: "Título" + }, + admin: { + description: () => "e.g. Previous Consultation" + }, + type: "text", + localized: true + }, + { + name: "link", + label: { + en: "URL" + }, + type: "text", + admin: { + description: () => + "YouTube playlist URL e.g https://www.youtube.com/watch?list= or https://www.youtube.com/playlist?list=" + }, + required: true + }, + { + name: "playlistId", + admin: { + hidden: true + }, + required: true, + hooks: { + beforeValidate: [mapPlaylistLinkToId] + }, + type: "text" + } + ] + }, + { + name: "featured", + type: "group", + fields: [ + { + name: "featuredType", + label: { + en: "Show", + ft: "Afficher", + pt: "Mostrar" + }, + type: "radio", + options: [ + { + label: { + en: "Most recent in the Playlist", + fr: "Le plus récent dans la playlist", + pt: "Mais recente na lista de reprodução" + }, + value: "latest" + }, + { + label: { + en: "Custom selection", + fr: "Sélection personnalisée", + pt: "Seleção personalizada" + }, + value: "custom" + }, + { + label: { + en: "None", + ft: "Aucun", + pt: "Nenhum" + }, + value: "none" + } + ], + defaultValue: "latest" + }, + { + name: "items", + label: { en: "Video(s)", fr: "Vidéo(s)", pt: "Vídeo(s)" }, + type: "select", + hasMany: true, + // These are just dummy options, YouTubeSelect loads (id, title) + // pairs from the playlist link provided above. + options: ["-"], + required: true, + validate: (value, props) => validateYouTubeSelect(value, props, slug), + admin: { + description: () => "Enter playlist URL above to select an audio/video", + components: { + Field: (props) => createElement(YouTubeSelect, { slug, ...props }) + }, + condition: (_, siblingData) => siblingData?.featuredType === "custom" + } + } + ], + admin: { + hideGutter: true + } + } + ] + }; +} diff --git a/apps/charterafrica/src/payload/fields/youtubeSelect/youtubeSelect.js b/apps/charterafrica/src/payload/fields/youtubeSelect/youtubeSelect.js index 034949639..d22a248fc 100644 --- a/apps/charterafrica/src/payload/fields/youtubeSelect/youtubeSelect.js +++ b/apps/charterafrica/src/payload/fields/youtubeSelect/youtubeSelect.js @@ -1,8 +1,4 @@ -import { - useAllFormFields, - Select, - getSiblingData, -} from "payload/components/forms"; +import { getSiblingData, Select, useAllFormFields } from "payload/components/forms"; import { createElement, useMemo } from "react"; import useSWR from "swr"; @@ -10,24 +6,22 @@ import { getEmbeddedPlaylist } from "../../utils/embeddedPlaylist"; const fetcher = (url) => fetch(url).then((res) => res.json()); -function YouTubeSelect(props) { - const [fields] = useAllFormFields(); +function YouTubeSelect({ slug, ...props }) { + const [fields] = useAllFormFields(); - const document = getSiblingData(fields, "blocks"); - const { playlistId, queryString } = getEmbeddedPlaylist(document); - const { data } = useSWR( - playlistId - ? `/api/v1/opportunities/consultation/multimedia?${queryString}` - : null, - fetcher, - ); - const memoOptions = () => - data?.items?.map((video) => ({ - label: video?.snippet?.title, - value: video?.snippet?.resourceId?.videoId, - })) || []; - const options = useMemo(memoOptions, [data?.items]); - return createElement(Select, { ...props, options }); + const document = getSiblingData(fields, "blocks"); + const { playlistId, queryString } = getEmbeddedPlaylist(document, slug); + const { data } = useSWR( + playlistId ? `/api/v1/opportunities/consultation/multimedia?${queryString}` : null, + fetcher + ); + const memoOptions = () => + data?.items?.map((video) => ({ + label: video?.snippet?.title, + value: video?.snippet?.resourceId?.videoId + })) || []; + const options = useMemo(memoOptions, [data?.items]); + return createElement(Select, { ...props, options }); } export default YouTubeSelect; diff --git a/apps/charterafrica/src/payload/utils/embeddedPlaylist.js b/apps/charterafrica/src/payload/utils/embeddedPlaylist.js index 3eb22fcbe..a02e662d7 100644 --- a/apps/charterafrica/src/payload/utils/embeddedPlaylist.js +++ b/apps/charterafrica/src/payload/utils/embeddedPlaylist.js @@ -1,34 +1,35 @@ export const BLOCK_SLUG = "embedded-playlist"; export const mapPlaylistLinkToId = ({ siblingData }) => { - try { - const { link } = siblingData; - const url = new URL(link); - const playlistId = url.searchParams.get("list"); - return playlistId; - } catch (error) { - return ""; - } + try { + const { link } = siblingData; + const url = new URL(link); + const playlistId = url.searchParams.get("list"); + return playlistId; + } catch (error) { + return ""; + } }; function getEmbeddedPlaylistFromBlock(block) { - const link = block?.playlist?.link; - const playlistId = mapPlaylistLinkToId({ - siblingData: { link }, - }); - const params = { - maxResults: 100, - part: "snippet", - pathname: "/playlistItems", - playlistId, - }; - const queryString = new URLSearchParams(params).toString(); + const link = block?.playlist?.link; + const playlistId = mapPlaylistLinkToId({ + siblingData: { link } + }); + const params = { + maxResults: 100, + part: "snippet", + pathname: "/playlistItems", + playlistId + }; + const queryString = new URLSearchParams(params).toString(); - return { playlistId, queryString }; + return { playlistId, queryString }; } -export function getEmbeddedPlaylist(documents = {}) { - const { blocks } = documents; - const block = blocks?.find((b) => b?.blockType === BLOCK_SLUG); - return getEmbeddedPlaylistFromBlock(block); +export function getEmbeddedPlaylist(documents = {}, blockSlug = BLOCK_SLUG) { + const { blocks } = documents; + const block = blocks?.find((b) => b?.blockType === blockSlug); + console.log(blocks); + return getEmbeddedPlaylistFromBlock(block); } From 9c324f7d32e39474cbc3725dd0b878e7690b3fbe Mon Sep 17 00:00:00 2001 From: Kevin Koech Date: Tue, 18 Jun 2024 13:09:04 +0300 Subject: [PATCH 2/4] Add Featured videos tests --- .../FeaturedVideos/FeaturedVideo.snap.js | 1235 +++++++++++++++++ .../FeaturedVideos/FeaturedVideo.test.js | 28 + .../FeaturedVideos/FeaturedVideoCard.js | 109 ++ .../FeaturedVideos/FeaturedVideoCard.snap.js | 1214 ++++++++++++++++ .../FeaturedVideos/FeaturedVideoCard.test.js | 23 + .../FeaturedVideos/FeaturedVideos.js | 45 + .../src/components/FeaturedVideos/index.js | 3 + .../data/common/processBlockFeaturedVideos.js | 80 ++ .../src/lib/data/common/processPageAbout.js | 5 +- .../data/common/processPageConsultation.js | 2 +- .../src/lib/data/common/processPageIndex.js | 6 +- .../src/pages/[...slugs].page.js | 2 + .../src/payload/blocks/EmbeddedPlaylist.js | 96 +- .../src/payload/blocks/FeaturedVideos.js | 26 +- .../src/payload/collections/Pages.js | 112 +- .../youtubeSelect/youtubePlaylistSelect.js | 265 ++-- .../fields/youtubeSelect/youtubeSelect.js | 36 +- .../src/payload/utils/embeddedPlaylist.js | 47 +- 18 files changed, 3047 insertions(+), 287 deletions(-) create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideo.snap.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideo.test.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideoCard.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideoCard.snap.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideoCard.test.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/FeaturedVideos.js create mode 100644 apps/charterafrica/src/components/FeaturedVideos/index.js create mode 100644 apps/charterafrica/src/lib/data/common/processBlockFeaturedVideos.js diff --git a/apps/charterafrica/src/components/FeaturedVideos/FeaturedVideo.snap.js b/apps/charterafrica/src/components/FeaturedVideos/FeaturedVideo.snap.js new file mode 100644 index 000000000..1030884c7 --- /dev/null +++ b/apps/charterafrica/src/components/FeaturedVideos/FeaturedVideo.snap.js @@ -0,0 +1,1235 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FeaturedVideos should render 1`] = ` +
+
+
+

+ Featured Videos +

+
+
+
+
+
+