Skip to content

Commit

Permalink
add phone and email link block
Browse files Browse the repository at this point in the history
  • Loading branch information
SebiVPS committed Jun 5, 2024
1 parent 3ee8c7a commit dd8545d
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 10 deletions.
8 changes: 8 additions & 0 deletions .changeset/fifty-lemons-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@comet/cms-admin": minor
"@comet/blocks-api": minor
"@comet/cms-site": minor
"@comet/cms-api": minor
---

Add PhoneLinkBlock and EmailLinkBlock
4 changes: 3 additions & 1 deletion demo/admin/src/common/blocks/LinkBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createLinkBlock, DamFileDownloadLinkBlock, ExternalLinkBlock, InternalLinkBlock } from "@comet/cms-admin";
import { createLinkBlock, DamFileDownloadLinkBlock, EmailLinkBlock, ExternalLinkBlock, InternalLinkBlock, PhoneLinkBlock } from "@comet/cms-admin";
import { NewsLinkBlock } from "@src/news/blocks/NewsLinkBlock";

export const LinkBlock = createLinkBlock({
Expand All @@ -7,5 +7,7 @@ export const LinkBlock = createLinkBlock({
external: ExternalLinkBlock,
news: NewsLinkBlock,
damFileDownload: DamFileDownloadLinkBlock,
email: EmailLinkBlock,
phone: PhoneLinkBlock,
},
});
44 changes: 41 additions & 3 deletions demo/api/block-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,23 @@
}
]
},
{
"name": "EmailLink",
"fields": [
{
"name": "email",
"kind": "String",
"nullable": true
}
],
"inputFields": [
{
"name": "email",
"kind": "String",
"nullable": true
}
]
},
{
"name": "ExternalLink",
"fields": [
Expand Down Expand Up @@ -796,7 +813,9 @@
"internal": "InternalLink",
"external": "ExternalLink",
"news": "NewsLink",
"damFileDownload": "DamFileDownloadLink"
"damFileDownload": "DamFileDownloadLink",
"phone": "PhoneLink",
"email": "EmailLink"
},
"nullable": false
}
Expand Down Expand Up @@ -826,7 +845,9 @@
"internal": "InternalLink",
"external": "ExternalLink",
"news": "NewsLink",
"damFileDownload": "DamFileDownloadLink"
"damFileDownload": "DamFileDownloadLink",
"phone": "PhoneLink",
"email": "EmailLink"
},
"nullable": false
}
Expand Down Expand Up @@ -1259,6 +1280,23 @@
}
]
},
{
"name": "PhoneLink",
"fields": [
{
"name": "phone",
"kind": "String",
"nullable": false
}
],
"inputFields": [
{
"name": "phone",
"kind": "String",
"nullable": false
}
]
},
{
"name": "PixelImage",
"fields": [
Expand Down Expand Up @@ -2148,7 +2186,7 @@
{
"name": "youtubeIdentifier",
"kind": "String",
"nullable": false
"nullable": true
},
{
"name": "aspectRatio",
Expand Down
11 changes: 9 additions & 2 deletions demo/api/src/common/blocks/linkBlock/link.block.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
import { ExternalLinkBlock } from "@comet/blocks-api";
import { EmailLinkBlock, ExternalLinkBlock, PhoneLinkBlock } from "@comet/blocks-api";
import { createLinkBlock, DamFileDownloadLinkBlock, InternalLinkBlock } from "@comet/cms-api";
import { NewsLinkBlock } from "@src/news/blocks/news-link.block";

export const LinkBlock = createLinkBlock({
supportedBlocks: { internal: InternalLinkBlock, external: ExternalLinkBlock, news: NewsLinkBlock, damFileDownload: DamFileDownloadLinkBlock },
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
news: NewsLinkBlock,
damFileDownload: DamFileDownloadLinkBlock,
phone: PhoneLinkBlock,
email: EmailLinkBlock,
},
});
12 changes: 12 additions & 0 deletions demo/site/src/blocks/LinkBlock.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import {
DamFileDownloadLinkBlock,
EmailLinkBlock,
ExternalLinkBlock,
InternalLinkBlock,
OneOfBlock,
PhoneLinkBlock,
PropsWithData,
SupportedBlocks,
withPreview,
Expand Down Expand Up @@ -32,6 +34,16 @@ const supportedBlocks: SupportedBlocks = {
{children}
</DamFileDownloadLinkBlock>
),
email: ({ children, title, ...props }) => (
<EmailLinkBlock data={props} title={title}>
{children}
</EmailLinkBlock>
),
phone: ({ children, title, ...props }) => (
<PhoneLinkBlock data={props} title={title}>
{children}
</PhoneLinkBlock>
),
};

interface LinkBlockProps extends PropsWithData<LinkBlockData> {
Expand Down
80 changes: 80 additions & 0 deletions packages/admin/cms-admin/src/blocks/EmailLinkBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { Field, FinalFormInput } from "@comet/admin";
import { BlockCategory, BlockInterface, BlocksFinalForm, createBlockSkeleton, SelectPreviewComponent } from "@comet/blocks-admin";
import * as React from "react";
import { FormattedMessage } from "react-intl";

interface EmailLinkBlockData {
email?: string;
}

interface EmailLinkBlockInput {
email?: string;
}

export const EmailLinkBlock: BlockInterface<EmailLinkBlockData, EmailLinkBlockData, EmailLinkBlockInput> = {
...createBlockSkeleton(),

name: "Email",

displayName: <FormattedMessage id="comet.blocks.link.email" defaultMessage="Email" />,

defaultValues: () => ({ email: undefined }),

category: BlockCategory.Navigation,

input2State: (state) => {
return state;
},

state2Output: (state) => {
return {
email: state.email,
};
},

output2State: async (output) => {
return {
email: output.email,
};
},

isValid: (state) => {
return state.email ? isEmail(state.email) : true;
},

AdminComponent: ({ state, updateState }) => {
return (
<SelectPreviewComponent>
<BlocksFinalForm
onSubmit={(newState: EmailLinkBlockData) => {
updateState((prevState) => ({ ...prevState, ...newState }));
}}
initialValues={state}
>
<Field
label={<FormattedMessage id="comet.blocks.link.email" defaultMessage="Email" />}
name="email"
component={FinalFormInput}
fullWidth
validate={(email: string) => {
if (email && !isEmail(email)) {
return <FormattedMessage id="comet.blocks.link.email.invalid" defaultMessage="Invalid e-mail address" />;
}
}}
/>
</BlocksFinalForm>
</SelectPreviewComponent>
);
},
previewContent: (state) => {
return state.email ? [{ type: "text", content: state.email }] : [];
},
};

const isEmail = (text: string) => {
return !!String(text)
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
);
};
76 changes: 76 additions & 0 deletions packages/admin/cms-admin/src/blocks/PhoneLinkBlock.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Field, FinalFormInput } from "@comet/admin";
import { BlockCategory, BlockInterface, BlocksFinalForm, createBlockSkeleton, SelectPreviewComponent } from "@comet/blocks-admin";
import * as React from "react";
import { FormattedMessage } from "react-intl";

interface PhoneLinkBlockData {
phone: string;
}

interface PhoneLinkBlockInput {
phone: string;
}

export const PhoneLinkBlock: BlockInterface<PhoneLinkBlockData, PhoneLinkBlockData, PhoneLinkBlockInput> = {
...createBlockSkeleton(),

name: "Phone",

displayName: <FormattedMessage id="comet.blocks.link.phone" defaultMessage="Phone Number" />,

defaultValues: () => ({ phone: "" }),

category: BlockCategory.Navigation,

input2State: (state) => {
return state;
},

state2Output: (state) => {
return {
phone: state.phone,
};
},

output2State: async (output) => {
return {
phone: output.phone,
};
},

isValid: (state) => {
return state.phone ? isPhone(state.phone) : true;
},

AdminComponent: ({ state, updateState }) => {
return (
<SelectPreviewComponent>
<BlocksFinalForm
onSubmit={(newState: PhoneLinkBlockData) => {
updateState((prevState) => ({ ...prevState, ...newState }));
}}
initialValues={state}
>
<Field
label={<FormattedMessage id="comet.blocks.link.phone" defaultMessage="Phone Number" />}
name="phone"
component={FinalFormInput}
fullWidth
validate={(phone: string) => {
if (phone && !isPhone(phone)) {
return <FormattedMessage id="comet.blocks.link.phone.invalid" defaultMessage="Invalid phone number" />;
}
}}
/>
</BlocksFinalForm>
</SelectPreviewComponent>
);
},
previewContent: (state) => {
return state.phone ? [{ type: "text", content: state.phone }] : [];
},
};

const isPhone = (text: string) => {
return !!String(text).match(/^[+]?[0-9]{4,20}$/im);
};
2 changes: 2 additions & 0 deletions packages/admin/cms-admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ export type { TextImageBlockFactoryOptions } from "./blocks/createTextImageBlock
export { createTextImageBlock } from "./blocks/createTextImageBlock";
export { createTextLinkBlock } from "./blocks/createTextLinkBlock";
export { DamVideoBlock } from "./blocks/DamVideoBlock";
export { EmailLinkBlock } from "./blocks/EmailLinkBlock";
export { ExternalLinkBlock } from "./blocks/ExternalLinkBlock";
export { EditImageDialog } from "./blocks/image/EditImageDialog";
export { InternalLinkBlock } from "./blocks/InternalLinkBlock";
export { PhoneLinkBlock } from "./blocks/PhoneLinkBlock";
export { PixelImageBlock } from "./blocks/PixelImageBlock";
export { SvgImageBlock } from "./blocks/SvgImageBlock";
export { useCmsBlockContext } from "./blocks/useCmsBlockContext";
Expand Down
34 changes: 34 additions & 0 deletions packages/api/blocks-api/block-meta.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
[
{
"name": "EmailLink",
"fields": [
{
"name": "email",
"kind": "String",
"nullable": true
}
],
"inputFields": [
{
"name": "email",
"kind": "String",
"nullable": true
}
]
},
{
"name": "ExternalLink",
"fields": [
Expand Down Expand Up @@ -26,6 +43,23 @@
}
]
},
{
"name": "PhoneLink",
"fields": [
{
"name": "phone",
"kind": "String",
"nullable": false
}
],
"inputFields": [
{
"name": "phone",
"kind": "String",
"nullable": false
}
]
},
{
"name": "Space",
"fields": [
Expand Down
22 changes: 22 additions & 0 deletions packages/api/blocks-api/src/blocks/email-link.block.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { IsEmail, IsOptional } from "class-validator";

import { BlockData, BlockInput, createBlock, inputToData } from "./block";
import { BlockField } from "./decorators/field";

class EmailLinkBlockData extends BlockData {
@BlockField({ nullable: true })
email?: string;
}

class EmailLinkBlockInput extends BlockInput {
@IsOptional()
@IsEmail()
@BlockField({ nullable: true })
email?: string;

transformToBlockData(): EmailLinkBlockData {
return inputToData(EmailLinkBlockData, this);
}
}

export const EmailLinkBlock = createBlock(EmailLinkBlockData, EmailLinkBlockInput, "EmailLink");
Loading

0 comments on commit dd8545d

Please sign in to comment.