From e5dbbfab858e1529b61389b95ea2ac026fb1063a Mon Sep 17 00:00:00 2001 From: raphaelblum <44967610+raphaelblum@users.noreply.github.com> Date: Mon, 26 Aug 2024 13:07:29 +0200 Subject: [PATCH] Add internationalization for newsletter preview and add salutation block for testing (#73) * Add internationalization for newsletter preview and add salutation block for testing * Add changeset * Adapt changeset - specify that previewUrl was removed --- .changeset/lemon-vans-burn.md | 13 +++++++++++++ demo/admin/src/App.tsx | 9 ++++++++- demo/admin/src/Routes.tsx | 2 -- .../blocks/EmailCampaignContentBlock.tsx | 2 ++ .../blocks/EmailCampaignSalutationBlock.tsx | 10 ++++++++++ demo/api/block-meta.json | 7 +++++++ .../blocks/email-campaign-content.block.ts | 2 ++ .../blocks/email-campaign-salutation.block.ts | 15 +++++++++++++++ demo/campaign/.prettierignore | 1 + demo/campaign/intl-update.sh | 2 +- demo/campaign/package.json | 1 + demo/campaign/src/blocks/ContentBlock.tsx | 2 ++ demo/campaign/src/blocks/SalutationBlock.tsx | 18 ++++++++++++++++++ demo/campaign/src/lang.ts | 4 ++-- .../{ => [domain]/[language]}/index.page.tsx | 5 ++--- .../admin/src/common/BrevoConfigProvider.tsx | 16 ++++++++++++++-- .../src/emailCampaigns/EmailCampaignsPage.tsx | 18 ++++-------------- .../emailCampaigns/form/EmailCampaignForm.tsx | 5 +++-- .../emailCampaigns/view/EmailCampaignView.tsx | 5 +++-- 19 files changed, 108 insertions(+), 29 deletions(-) create mode 100644 .changeset/lemon-vans-burn.md create mode 100644 demo/admin/src/emailCampaigns/blocks/EmailCampaignSalutationBlock.tsx create mode 100644 demo/api/src/email-campaign/blocks/email-campaign-salutation.block.ts create mode 100644 demo/campaign/src/blocks/SalutationBlock.tsx rename demo/campaign/src/pages/preview/{ => [domain]/[language]}/index.page.tsx (85%) diff --git a/.changeset/lemon-vans-burn.md b/.changeset/lemon-vans-burn.md new file mode 100644 index 00000000..11620072 --- /dev/null +++ b/.changeset/lemon-vans-burn.md @@ -0,0 +1,13 @@ +--- +"@comet/brevo-admin": major +--- + +Add `resolvePreviewUrlForScope` in `BrevoConfigProvider` to be able to provide internationalization for the preview. The previous `previewUrl` option has been removed and must be defined via the `resolvePreviewUrlForScope`. + +**The project can then call different urls based on the scope like that:** + +```typescript + resolvePreviewUrlForScope: (scope: ContentScope) => { + return `${config.campaignUrl}/preview/${scope.domain}/${scope.language}`; + }, +``` diff --git a/demo/admin/src/App.tsx b/demo/admin/src/App.tsx index 3dad7569..94bca084 100644 --- a/demo/admin/src/App.tsx +++ b/demo/admin/src/App.tsx @@ -78,7 +78,14 @@ export function App() { - + { + return `${config.campaignUrl}/preview/${scope.domain}/${scope.language}`; + }, + }} + > { const EmailCampaignsPage = createEmailCampaignsPage({ scopeParts: ["domain", "language"], EmailCampaignContentBlock: EmailCampaignContentBlock, - previewUrl: `${config.campaignUrl}/preview`, }); return ( diff --git a/demo/admin/src/emailCampaigns/blocks/EmailCampaignContentBlock.tsx b/demo/admin/src/emailCampaigns/blocks/EmailCampaignContentBlock.tsx index 146031f5..0c3b9473 100644 --- a/demo/admin/src/emailCampaigns/blocks/EmailCampaignContentBlock.tsx +++ b/demo/admin/src/emailCampaigns/blocks/EmailCampaignContentBlock.tsx @@ -3,12 +3,14 @@ import { PixelImageBlock } from "@comet/cms-admin"; import { RichTextBlock } from "@src/common/blocks/RichTextBlock"; import { DividerBlock } from "./DividerBlock"; +import { EmailCampaignSalutationBlock } from "./EmailCampaignSalutationBlock"; export const EmailCampaignContentBlock = createBlocksBlock({ name: "EmailCampaignContent", supportedBlocks: { divider: DividerBlock, text: RichTextBlock, + salutation: EmailCampaignSalutationBlock, image: PixelImageBlock, }, }); diff --git a/demo/admin/src/emailCampaigns/blocks/EmailCampaignSalutationBlock.tsx b/demo/admin/src/emailCampaigns/blocks/EmailCampaignSalutationBlock.tsx new file mode 100644 index 00000000..0e99dffb --- /dev/null +++ b/demo/admin/src/emailCampaigns/blocks/EmailCampaignSalutationBlock.tsx @@ -0,0 +1,10 @@ +import { BlockCategory, createCompositeBlock } from "@comet/blocks-admin"; +import * as React from "react"; +import { FormattedMessage } from "react-intl"; + +export const EmailCampaignSalutationBlock = createCompositeBlock({ + name: "EmailCampaignSalutation", + displayName: , + category: BlockCategory.TextAndContent, + blocks: {}, +}); diff --git a/demo/api/block-meta.json b/demo/api/block-meta.json index 62f6f02f..40aa326b 100644 --- a/demo/api/block-meta.json +++ b/demo/api/block-meta.json @@ -209,6 +209,7 @@ "blocks": { "text": "RichText", "divider": "EmailCampaignDivider", + "salutation": "EmailCampaignSalutation", "image": "PixelImage" }, "nullable": false @@ -245,6 +246,7 @@ "blocks": { "text": "RichText", "divider": "EmailCampaignDivider", + "salutation": "EmailCampaignSalutation", "image": "PixelImage" }, "nullable": false @@ -321,6 +323,11 @@ } ] }, + { + "name": "EmailCampaignSalutation", + "fields": [], + "inputFields": [] + }, { "name": "ExternalLink", "fields": [ diff --git a/demo/api/src/email-campaign/blocks/email-campaign-content.block.ts b/demo/api/src/email-campaign/blocks/email-campaign-content.block.ts index 509c548c..d1ca132a 100644 --- a/demo/api/src/email-campaign/blocks/email-campaign-content.block.ts +++ b/demo/api/src/email-campaign/blocks/email-campaign-content.block.ts @@ -3,12 +3,14 @@ import { PixelImageBlock } from "@comet/cms-api"; import { EmailCampaignDividerBlock } from "./email-campaign-divider.block"; import { EmailCampaignRichTextBlock } from "./email-campaign-rich-text.block"; +import { EmailCampaignSalutationBlock } from "./email-campaign-salutation.block"; export const EmailCampaignContentBlock = createBlocksBlock( { supportedBlocks: { text: EmailCampaignRichTextBlock, divider: EmailCampaignDividerBlock, + salutation: EmailCampaignSalutationBlock, image: PixelImageBlock, }, }, diff --git a/demo/api/src/email-campaign/blocks/email-campaign-salutation.block.ts b/demo/api/src/email-campaign/blocks/email-campaign-salutation.block.ts new file mode 100644 index 00000000..d4e0d4cf --- /dev/null +++ b/demo/api/src/email-campaign/blocks/email-campaign-salutation.block.ts @@ -0,0 +1,15 @@ +import { BlockData, BlockInput, createBlock, inputToData } from "@comet/blocks-api"; + +class EmailCampaignSalutationBlockData extends BlockData {} + +class EmailCampaignSalutationBlockInput extends BlockInput { + transformToBlockData(): EmailCampaignSalutationBlockData { + return inputToData(EmailCampaignSalutationBlockData, this); + } +} + +export const EmailCampaignSalutationBlock = createBlock( + EmailCampaignSalutationBlockData, + EmailCampaignSalutationBlockInput, + "EmailCampaignSalutation", +); diff --git a/demo/campaign/.prettierignore b/demo/campaign/.prettierignore index 6f2ceda0..ff28c373 100644 --- a/demo/campaign/.prettierignore +++ b/demo/campaign/.prettierignore @@ -1,5 +1,6 @@ .next/ block-meta.json lang-compiled/ +lang-extracted/ lang/ preBuild/ \ No newline at end of file diff --git a/demo/campaign/intl-update.sh b/demo/campaign/intl-update.sh index 40c33278..b161ef24 100755 --- a/demo/campaign/intl-update.sh +++ b/demo/campaign/intl-update.sh @@ -5,4 +5,4 @@ cd "$(dirname "$0")" || exit rm -rf ./lang/ mkdir -p ./lang -git clone https://github.com/vivid-planet/comet-starter-lang.git lang/comet-brevo-module-demo-lang +git clone https://github.com/vivid-planet/comet-brevo-module-lang.git lang/comet-brevo-module-demo-lang diff --git a/demo/campaign/package.json b/demo/campaign/package.json index bff676ae..c77e25ae 100644 --- a/demo/campaign/package.json +++ b/demo/campaign/package.json @@ -9,6 +9,7 @@ "export": "next export", "gql:types": "graphql-codegen", "gql:watch": "graphql-codegen --watch", + "intl:extract": "formatjs extract \"src/**/*.ts*\" --ignore ./**.d.ts --out-file lang-extracted/en.json --format simple", "lint": "run-p gql:types generate-block-types && run-p lint:prettier lint:eslint lint:tsc", "lint:prettier": "npx prettier --check './**/*.{js,json,md,yml,yaml}'", "lint:eslint": "eslint --max-warnings 0 --config ./.eslintrc.cli.js --ext .ts,.tsx,.js,.jsx,.json,.md src/ package.json", diff --git a/demo/campaign/src/blocks/ContentBlock.tsx b/demo/campaign/src/blocks/ContentBlock.tsx index c29a7213..f840d18e 100644 --- a/demo/campaign/src/blocks/ContentBlock.tsx +++ b/demo/campaign/src/blocks/ContentBlock.tsx @@ -5,10 +5,12 @@ import { DividerBlock } from "@src/blocks/DividerBlock"; import React from "react"; import { ImageBlock } from "./ImageBlock"; +import { SalutationBlock } from "./SalutationBlock"; const supportedBlocks: SupportedBlocks = { divider: (data) => , text: (data) => , + salutation: (data) => , image: (data) => , }; diff --git a/demo/campaign/src/blocks/SalutationBlock.tsx b/demo/campaign/src/blocks/SalutationBlock.tsx new file mode 100644 index 00000000..8164cf1c --- /dev/null +++ b/demo/campaign/src/blocks/SalutationBlock.tsx @@ -0,0 +1,18 @@ +import { PropsWithData } from "@comet/cms-site"; +import { MjmlColumn, MjmlText } from "@luma-team/mjml-react"; +import { RichTextBlockData } from "@src/blocks.generated"; +import { IndentedSectionGroup } from "@src/components/IndentedSectionGroup"; +import * as React from "react"; +import { FormattedMessage } from "react-intl"; + +export const SalutationBlock: React.FC> = ({ data }) => { + return ( + + + + + + + + ); +}; diff --git a/demo/campaign/src/lang.ts b/demo/campaign/src/lang.ts index 5a577054..573f9b93 100644 --- a/demo/campaign/src/lang.ts +++ b/demo/campaign/src/lang.ts @@ -2,7 +2,7 @@ import { IntlConfig } from "react-intl"; export function getMessages(language: string): Promise { if (language === "en") { - return require("../lang/comet-brevo-module-demo-lang/site/en.json"); + return require("../lang/comet-brevo-module-demo-lang/campaign/en.json"); } - return require("../lang/comet-brevo-module-demo-lang/site/de.json"); + return require("../lang/comet-brevo-module-demo-lang/campaign/de.json"); } diff --git a/demo/campaign/src/pages/preview/index.page.tsx b/demo/campaign/src/pages/preview/[domain]/[language]/index.page.tsx similarity index 85% rename from demo/campaign/src/pages/preview/index.page.tsx rename to demo/campaign/src/pages/preview/[domain]/[language]/index.page.tsx index 533b1493..f9368305 100644 --- a/demo/campaign/src/pages/preview/index.page.tsx +++ b/demo/campaign/src/pages/preview/[domain]/[language]/index.page.tsx @@ -31,10 +31,9 @@ const MailPreviewPage: React.FC = (props) => ( export default MailPreviewPage; -export async function getServerSideProps({ locale: localeFromContext }: GetServerSidePropsContext): Promise<{ props: Props } | undefined> { - const locale = localeFromContext ?? defaultLanguage; +export async function getServerSideProps({ params }: GetServerSidePropsContext): Promise<{ props: Props } | undefined> { + const locale = typeof params?.language === "string" ? params.language : defaultLanguage; const [messages] = await Promise.all([getMessages(locale)]); - return { props: { intlProviderValues: { diff --git a/packages/admin/src/common/BrevoConfigProvider.tsx b/packages/admin/src/common/BrevoConfigProvider.tsx index d295aead..bce4926c 100644 --- a/packages/admin/src/common/BrevoConfigProvider.tsx +++ b/packages/admin/src/common/BrevoConfigProvider.tsx @@ -1,7 +1,9 @@ +import { ContentScopeInterface, useContentScope } from "@comet/cms-admin"; import React from "react"; export interface BrevoConfig { apiUrl: string; + resolvePreviewUrlForScope: (scope: ContentScopeInterface) => string; } const BrevoConfigContext = React.createContext(undefined); @@ -14,10 +16,20 @@ export const BrevoConfigProvider = ({ children, value }: React.PropsWithChildren return {children}; }; -export const useBrevoConfig = (): BrevoConfig => { +interface UseBrevoConfigReturn { + apiUrl: string; + previewUrl: string; +} + +export const useBrevoConfig = (): UseBrevoConfigReturn => { + const { scope } = useContentScope(); + const context = React.useContext(BrevoConfigContext); if (context === undefined) { throw new Error("useBrevoConfig must be used within a BrevoConfigProvider"); } - return context; + + const previewUrl = context.resolvePreviewUrlForScope(scope); + + return { ...context, previewUrl }; }; diff --git a/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx b/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx index ea868cf1..d4cad598 100644 --- a/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx +++ b/packages/admin/src/emailCampaigns/EmailCampaignsPage.tsx @@ -12,10 +12,9 @@ import { EmailCampaignView } from "./view/EmailCampaignView"; interface CreateEmailCampaignsPageOptions { scopeParts: string[]; EmailCampaignContentBlock: BlockInterface; - previewUrl: string; } -export function createEmailCampaignsPage({ scopeParts, EmailCampaignContentBlock, previewUrl }: CreateEmailCampaignsPageOptions) { +export function createEmailCampaignsPage({ scopeParts, EmailCampaignContentBlock }: CreateEmailCampaignsPageOptions) { function EmailCampaignsPage(): JSX.Element { const { scope: completeScope } = useContentScope(); const intl = useIntl(); @@ -33,29 +32,20 @@ export function createEmailCampaignsPage({ scopeParts, EmailCampaignContentBlock {(selectedId) => } - {(selectedId) => ( - - )} + {(selectedId) => } - {(selectedId) => ( - - )} + {(selectedId) => } - + diff --git a/packages/admin/src/emailCampaigns/form/EmailCampaignForm.tsx b/packages/admin/src/emailCampaigns/form/EmailCampaignForm.tsx index 222e4593..72a219ae 100644 --- a/packages/admin/src/emailCampaigns/form/EmailCampaignForm.tsx +++ b/packages/admin/src/emailCampaigns/form/EmailCampaignForm.tsx @@ -40,6 +40,7 @@ import React, { useMemo } from "react"; import { FormattedMessage } from "react-intl"; import { useRouteMatch } from "react-router"; +import { useBrevoConfig } from "../../common/BrevoConfigProvider"; import { GQLEmailCampaignInput } from "../../graphql.generated"; import { ConfigFields } from "./ConfigFields"; import { createEmailCampaignMutation, emailCampaignFormQuery, updateEmailCampaignMutation } from "./EmailCampaignForm.gql"; @@ -59,10 +60,9 @@ interface FormProps { id?: string; EmailCampaignContentBlock: BlockInterface; scope: ContentScopeInterface; - previewUrl: string; } -export function EmailCampaignForm({ id, EmailCampaignContentBlock, scope, previewUrl }: FormProps): React.ReactElement { +export function EmailCampaignForm({ id, EmailCampaignContentBlock, scope }: FormProps): React.ReactElement { const rootBlocks = { content: EmailCampaignContentBlock, }; @@ -71,6 +71,7 @@ export function EmailCampaignForm({ id, EmailCampaignContentBlock, scope, previe [key in keyof typeof rootBlocks]: BlockState<(typeof rootBlocks)[key]>; }; + const { previewUrl } = useBrevoConfig(); const stackApi = useStackApi(); const stackSwitchApi = useStackSwitchApi(); const client = useApolloClient(); diff --git a/packages/admin/src/emailCampaigns/view/EmailCampaignView.tsx b/packages/admin/src/emailCampaigns/view/EmailCampaignView.tsx index a458ffd0..a9e006a9 100644 --- a/packages/admin/src/emailCampaigns/view/EmailCampaignView.tsx +++ b/packages/admin/src/emailCampaigns/view/EmailCampaignView.tsx @@ -8,21 +8,22 @@ import React from "react"; import { FormattedMessage } from "react-intl"; import { useRouteMatch } from "react-router"; +import { useBrevoConfig } from "../../common/BrevoConfigProvider"; import { emailCampaignViewQuery } from "./EmailCampaignView.gql"; import { GQLEmailCampaignViewQuery, GQLEmailCampaignViewQueryVariables } from "./EmailCampaignView.gql.generated"; interface EmailCampaignViewProps { id: string; EmailCampaignContentBlock: BlockInterface; - previewUrl: string; } -export function EmailCampaignView({ id, EmailCampaignContentBlock, previewUrl }: EmailCampaignViewProps): React.ReactElement { +export function EmailCampaignView({ id, EmailCampaignContentBlock }: EmailCampaignViewProps): React.ReactElement { const stackApi = useStackApi(); const previewApi = useBlockPreview(); const blockContext = useCmsBlockContext(); const match = useRouteMatch(); const { scope } = useContentScope(); + const { previewUrl } = useBrevoConfig(); const { data, error, loading } = useQuery(emailCampaignViewQuery, { variables: { id },