@comet/mail-react 9.0.0-beta.3 → 9.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -15
- package/lib/__stories__/layout-patterns/AsymmetricTwoColumnLayout.stories.js +2 -1
- package/lib/blocks/pixelImage/HtmlPixelImageBlock.d.ts +11 -0
- package/lib/blocks/pixelImage/HtmlPixelImageBlock.js +18 -0
- package/lib/blocks/pixelImage/MjmlPixelImageBlock.d.ts +9 -0
- package/lib/blocks/pixelImage/MjmlPixelImageBlock.js +15 -0
- package/lib/blocks/pixelImage/__stories__/HtmlPixelImageBlock.stories.d.ts +7 -0
- package/lib/blocks/pixelImage/__stories__/HtmlPixelImageBlock.stories.js +54 -0
- package/lib/blocks/pixelImage/__stories__/MjmlPixelImageBlock.stories.d.ts +7 -0
- package/lib/blocks/pixelImage/__stories__/MjmlPixelImageBlock.stories.js +54 -0
- package/lib/blocks/pixelImage/__stories__/exampleBlockData.d.ts +2 -0
- package/lib/blocks/pixelImage/__stories__/exampleBlockData.js +19 -0
- package/lib/blocks/pixelImage/__tests__/usePixelImageBlockConfig.test.d.ts +1 -0
- package/lib/blocks/pixelImage/__tests__/usePixelImageBlockConfig.test.js +21 -0
- package/lib/blocks/pixelImage/__tests__/usePixelImageBlockData.test.d.ts +1 -0
- package/lib/blocks/pixelImage/__tests__/usePixelImageBlockData.test.js +205 -0
- package/lib/blocks/pixelImage/common.d.ts +18 -0
- package/lib/blocks/pixelImage/common.js +1 -0
- package/lib/blocks/pixelImage/usePixelImageBlockConfig.d.ts +5 -0
- package/lib/blocks/pixelImage/usePixelImageBlockConfig.js +11 -0
- package/lib/blocks/pixelImage/usePixelImageBlockData.d.ts +16 -0
- package/lib/blocks/pixelImage/usePixelImageBlockData.js +80 -0
- package/lib/components/divider/HtmlDivider.d.ts +9 -0
- package/lib/components/divider/HtmlDivider.js +43 -0
- package/lib/components/divider/MjmlDivider.d.ts +7 -0
- package/lib/components/divider/MjmlDivider.js +13 -0
- package/lib/components/divider/__stories__/HtmlDivider.stories.d.ts +10 -0
- package/lib/components/divider/__stories__/HtmlDivider.stories.js +60 -0
- package/lib/components/divider/__stories__/MjmlDivider.stories.d.ts +10 -0
- package/lib/components/divider/__stories__/MjmlDivider.stories.js +60 -0
- package/lib/components/divider/__tests__/HtmlDivider.test.d.ts +1 -0
- package/lib/components/divider/__tests__/HtmlDivider.test.js +144 -0
- package/lib/components/divider/__tests__/MjmlDivider.test.d.ts +1 -0
- package/lib/components/divider/__tests__/MjmlDivider.test.js +43 -0
- package/lib/components/divider/defaultDividerStyles.d.ts +2 -0
- package/lib/components/divider/defaultDividerStyles.js +4 -0
- package/lib/components/divider/dividerProps.d.ts +38 -0
- package/lib/components/divider/dividerProps.js +1 -0
- package/lib/components/divider/generateResponsiveDividerCss.d.ts +8 -0
- package/lib/components/divider/generateResponsiveDividerCss.js +55 -0
- package/lib/components/image/HtmlImage.d.ts +11 -0
- package/lib/components/image/HtmlImage.js +23 -0
- package/lib/components/image/MjmlImage.d.ts +10 -0
- package/lib/components/image/MjmlImage.js +22 -0
- package/lib/components/image/__stories__/HtmlImage.stories.d.ts +7 -0
- package/lib/components/image/__stories__/HtmlImage.stories.js +32 -0
- package/lib/components/image/__stories__/MjmlImage.stories.d.ts +7 -0
- package/lib/components/image/__stories__/MjmlImage.stories.js +32 -0
- package/lib/components/mailRoot/MjmlMailRoot.d.ts +13 -4
- package/lib/components/mailRoot/MjmlMailRoot.js +10 -5
- package/lib/components/text/textStyles.d.ts +1 -3
- package/lib/components/text/textStyles.js +1 -1
- package/lib/config/ConfigProvider.d.ts +43 -0
- package/lib/config/ConfigProvider.js +16 -0
- package/lib/config/ConfigProvider.test.d.ts +8 -0
- package/lib/config/ConfigProvider.test.js +30 -0
- package/lib/index.d.ts +15 -2
- package/lib/index.js +8 -1
- package/lib/server/renderMailHtml.test.js +10 -1
- package/lib/theme/createTheme.d.ts +2 -1
- package/lib/theme/createTheme.js +1 -0
- package/lib/theme/defaultTheme.js +2 -0
- package/lib/theme/themeTypes.d.ts +41 -0
- package/package.json +13 -9
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type IMjmlImageProps } from "@faire/mjml-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
export type MjmlImageProps = IMjmlImageProps;
|
|
4
|
+
/**
|
|
5
|
+
* Renders an MJML image that adapts to the viewport width below the default breakpoint.
|
|
6
|
+
*
|
|
7
|
+
* Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
|
|
8
|
+
* use `HtmlImage` instead.
|
|
9
|
+
*/
|
|
10
|
+
export declare function MjmlImage({ className, ...restProps }: MjmlImageProps): ReactNode;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlImage as BaseMjmlImage } from "@faire/mjml-react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { registerStyles } from "../../styles/registerStyles.js";
|
|
5
|
+
import { css } from "../../utils/css.js";
|
|
6
|
+
/**
|
|
7
|
+
* Renders an MJML image that adapts to the viewport width below the default breakpoint.
|
|
8
|
+
*
|
|
9
|
+
* Must be placed within an `MjmlColumn`. For raw HTML context (e.g. inside `MjmlRaw`),
|
|
10
|
+
* use `HtmlImage` instead.
|
|
11
|
+
*/
|
|
12
|
+
export function MjmlImage({ className, ...restProps }) {
|
|
13
|
+
return _jsx(BaseMjmlImage, { className: clsx("mjmlImage", className), ...restProps });
|
|
14
|
+
}
|
|
15
|
+
// MJML inlines a fixed `height` on the inner <img>; !important overrides it for responsive scaling.
|
|
16
|
+
registerStyles((theme) => css `
|
|
17
|
+
${theme.breakpoints.default.belowMediaQuery} {
|
|
18
|
+
.mjmlImage img {
|
|
19
|
+
height: auto !important;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
`);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { HtmlImage } from "../HtmlImage.js";
|
|
3
|
+
type Story = StoryObj<typeof HtmlImage>;
|
|
4
|
+
declare const config: Meta<typeof HtmlImage>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const FullWidth: Story;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlRaw } from "@faire/mjml-react";
|
|
3
|
+
import { defaultTheme } from "../../../theme/defaultTheme.js";
|
|
4
|
+
import { getDefaultFromResponsiveValue } from "../../../theme/responsiveValue.js";
|
|
5
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
6
|
+
import { HtmlImage } from "../HtmlImage.js";
|
|
7
|
+
const sectionIndent = getDefaultFromResponsiveValue(defaultTheme.sizes.contentIndentation);
|
|
8
|
+
const sectionInnerWidth = defaultTheme.sizes.bodyWidth - 2 * sectionIndent;
|
|
9
|
+
const config = {
|
|
10
|
+
title: "Components/HtmlImage",
|
|
11
|
+
component: HtmlImage,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
args: {
|
|
14
|
+
width: sectionInnerWidth,
|
|
15
|
+
height: 268,
|
|
16
|
+
alt: "Placeholder image",
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
src: { control: false },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export default config;
|
|
23
|
+
export const Default = {
|
|
24
|
+
render: (args) => (_jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx(HtmlImage, { ...args, src: `https://picsum.photos/seed/html-image/${args.width}/${args.height}` }) }) }) })),
|
|
25
|
+
};
|
|
26
|
+
export const FullWidth = {
|
|
27
|
+
args: {
|
|
28
|
+
width: defaultTheme.sizes.bodyWidth,
|
|
29
|
+
height: 300,
|
|
30
|
+
},
|
|
31
|
+
render: (args) => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx(HtmlImage, { ...args, src: `https://picsum.photos/seed/html-image-full-width/${args.width}/${args.height}` }) }) }) })),
|
|
32
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { MjmlImage } from "../MjmlImage.js";
|
|
3
|
+
type Story = StoryObj<typeof MjmlImage>;
|
|
4
|
+
declare const config: Meta<typeof MjmlImage>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const FullWidth: Story;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn } from "@faire/mjml-react";
|
|
3
|
+
import { defaultTheme } from "../../../theme/defaultTheme.js";
|
|
4
|
+
import { getDefaultFromResponsiveValue } from "../../../theme/responsiveValue.js";
|
|
5
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
6
|
+
import { MjmlImage } from "../MjmlImage.js";
|
|
7
|
+
const sectionIndent = getDefaultFromResponsiveValue(defaultTheme.sizes.contentIndentation);
|
|
8
|
+
const sectionInnerWidth = defaultTheme.sizes.bodyWidth - 2 * sectionIndent;
|
|
9
|
+
const config = {
|
|
10
|
+
title: "Components/MjmlImage",
|
|
11
|
+
component: MjmlImage,
|
|
12
|
+
tags: ["autodocs"],
|
|
13
|
+
args: {
|
|
14
|
+
width: sectionInnerWidth,
|
|
15
|
+
height: 268,
|
|
16
|
+
alt: "Placeholder image",
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
src: { control: false },
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export default config;
|
|
23
|
+
export const Default = {
|
|
24
|
+
render: (args) => (_jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlImage, { ...args, src: `https://picsum.photos/seed/mjml-image/${args.width}/${args.height}` }) }) })),
|
|
25
|
+
};
|
|
26
|
+
export const FullWidth = {
|
|
27
|
+
args: {
|
|
28
|
+
width: defaultTheme.sizes.bodyWidth,
|
|
29
|
+
height: 300,
|
|
30
|
+
},
|
|
31
|
+
render: (args) => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlImage, { ...args, src: `https://picsum.photos/seed/mjml-image-full-width/${args.width}/${args.height}` }) }) })),
|
|
32
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PropsWithChildren, ReactNode } from "react";
|
|
2
|
+
import { type Config } from "../../config/ConfigProvider.js";
|
|
2
3
|
import type { Theme } from "../../theme/themeTypes.js";
|
|
3
4
|
type MjmlMailRootProps = PropsWithChildren<{
|
|
4
5
|
/**
|
|
@@ -6,17 +7,25 @@ type MjmlMailRootProps = PropsWithChildren<{
|
|
|
6
7
|
* (equivalent to `createTheme()`) is used.
|
|
7
8
|
*/
|
|
8
9
|
theme?: Theme;
|
|
10
|
+
/** Extra content appended inside the built-in `<MjmlAttributes>`, after the default `<MjmlAll>`. */
|
|
11
|
+
attributes?: ReactNode;
|
|
12
|
+
/** Extra content appended inside `<MjmlHead>`, after the registered styles block. */
|
|
13
|
+
head?: ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Configuration to make available to descendants via `useConfig`.
|
|
16
|
+
*/
|
|
17
|
+
config?: Config;
|
|
9
18
|
}>;
|
|
10
19
|
/**
|
|
11
20
|
* The root element for email templates. Renders the standard MJML email skeleton
|
|
12
21
|
* (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
|
|
13
22
|
* default attribute so all components start with zero padding.
|
|
14
23
|
*
|
|
15
|
-
* Accepts an optional `theme` prop that controls the body width and responsive
|
|
16
|
-
*
|
|
17
|
-
* `
|
|
24
|
+
* Accepts an optional `theme` prop that controls the body width and responsive breakpoints. The theme is made available to all descendant components via `useTheme()`.
|
|
25
|
+
*
|
|
26
|
+
* Accepts an optional `config` prop containing configuration. When provided, the value is made available to descendants via `useConfig()`.
|
|
18
27
|
*
|
|
19
28
|
* Direct children should be section-level components (e.g. `MjmlSection`).
|
|
20
29
|
*/
|
|
21
|
-
export declare function MjmlMailRoot({ theme: themeProp, children }: MjmlMailRootProps): ReactNode;
|
|
30
|
+
export declare function MjmlMailRoot({ theme: themeProp, attributes, head, config, children }: MjmlMailRootProps): ReactNode;
|
|
22
31
|
export {};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Mjml, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlHead } from "@faire/mjml-react";
|
|
3
|
+
import { ConfigProvider } from "../../config/ConfigProvider.js";
|
|
3
4
|
import { Styles } from "../../styles/Styles.js";
|
|
4
5
|
import { createTheme } from "../../theme/createTheme.js";
|
|
5
6
|
import { ThemeProvider } from "../../theme/ThemeProvider.js";
|
|
@@ -8,13 +9,17 @@ import { ThemeProvider } from "../../theme/ThemeProvider.js";
|
|
|
8
9
|
* (`<Mjml>`, `<MjmlHead>`, `<MjmlBody>`) with `<MjmlAll padding={0} />` as the
|
|
9
10
|
* default attribute so all components start with zero padding.
|
|
10
11
|
*
|
|
11
|
-
* Accepts an optional `theme` prop that controls the body width and responsive
|
|
12
|
-
*
|
|
13
|
-
* `
|
|
12
|
+
* Accepts an optional `theme` prop that controls the body width and responsive breakpoints. The theme is made available to all descendant components via `useTheme()`.
|
|
13
|
+
*
|
|
14
|
+
* Accepts an optional `config` prop containing configuration. When provided, the value is made available to descendants via `useConfig()`.
|
|
14
15
|
*
|
|
15
16
|
* Direct children should be section-level components (e.g. `MjmlSection`).
|
|
16
17
|
*/
|
|
17
|
-
export function MjmlMailRoot({ theme: themeProp, children }) {
|
|
18
|
+
export function MjmlMailRoot({ theme: themeProp, attributes, head, config, children }) {
|
|
18
19
|
const theme = themeProp ?? createTheme();
|
|
19
|
-
|
|
20
|
+
const content = (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Mjml, { children: [_jsxs(MjmlHead, { children: [_jsxs(MjmlAttributes, { children: [_jsx(MjmlAll, { padding: "0", fontFamily: theme.text.fontFamily }), attributes] }), _jsx(MjmlBreakpoint, { width: `${theme.breakpoints.mobile.value}px` }), _jsx(Styles, {}), head] }), _jsx(MjmlBody, { width: theme.sizes.bodyWidth, backgroundColor: theme.colors.background.body, children: children })] }) }));
|
|
21
|
+
if (config) {
|
|
22
|
+
return _jsx(ConfigProvider, { config: config, children: content });
|
|
23
|
+
}
|
|
24
|
+
return content;
|
|
20
25
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
type StylePropertyKey = Exclude<keyof TextVariantStyles, "bottomSpacing">;
|
|
3
|
-
export declare const textStyleCssProperties: ReadonlyArray<[StylePropertyKey, string]>;
|
|
1
|
+
import type { Theme } from "../../theme/themeTypes.js";
|
|
4
2
|
interface GenerateResponsiveTextCssOptions {
|
|
5
3
|
/** Selector for text style overrides, given a variant name. */
|
|
6
4
|
styleSelector: (variantName: string) => string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getResponsiveOverrides } from "../../theme/responsiveValue.js";
|
|
2
2
|
import { css } from "../../utils/css.js";
|
|
3
|
-
|
|
3
|
+
const textStyleCssProperties = [
|
|
4
4
|
["fontFamily", "font-family"],
|
|
5
5
|
["fontSize", "font-size"],
|
|
6
6
|
["fontWeight", "font-weight"],
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type PropsWithChildren, type ReactNode } from "react";
|
|
2
|
+
export interface PixelImageBlockConfig {
|
|
3
|
+
/**
|
|
4
|
+
* Image widths supported by the API, used to pick a render width.
|
|
5
|
+
* Generally derived from `cometConfig.images.imageSizes` and `cometConfig.images.deviceSizes`.
|
|
6
|
+
*/
|
|
7
|
+
validSizes: number[];
|
|
8
|
+
/**
|
|
9
|
+
* Origin to prefix relative image URLs with, e.g. `http://localhost:3000`.
|
|
10
|
+
*/
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Configuration context for mails.
|
|
15
|
+
*
|
|
16
|
+
* Add custom keys via TypeScript interface declaration merging:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* declare module "@comet/mail-react" {
|
|
20
|
+
* interface Config {
|
|
21
|
+
* myKey?: { foo: string };
|
|
22
|
+
* }
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export interface Config {
|
|
27
|
+
/**
|
|
28
|
+
* Configuration consumed by `HtmlPixelImageBlock` and `MjmlPixelImageBlock`.
|
|
29
|
+
* Required when those components are rendered.
|
|
30
|
+
*/
|
|
31
|
+
pixelImageBlock?: PixelImageBlockConfig;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Places a `Config` value into a React context, making it available to all
|
|
35
|
+
* descendants via `useConfig`.
|
|
36
|
+
*/
|
|
37
|
+
export declare function ConfigProvider({ config, children }: PropsWithChildren<{
|
|
38
|
+
config: Config;
|
|
39
|
+
}>): ReactNode;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the nearest `Config` from context, defined by `ConfigProvider` or by the `config` prop on `MjmlMailRoot`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function useConfig(): Config;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const ConfigContext = createContext({});
|
|
4
|
+
/**
|
|
5
|
+
* Places a `Config` value into a React context, making it available to all
|
|
6
|
+
* descendants via `useConfig`.
|
|
7
|
+
*/
|
|
8
|
+
export function ConfigProvider({ config, children }) {
|
|
9
|
+
return _jsx(ConfigContext.Provider, { value: config, children: children });
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns the nearest `Config` from context, defined by `ConfigProvider` or by the `config` prop on `MjmlMailRoot`.
|
|
13
|
+
*/
|
|
14
|
+
export function useConfig() {
|
|
15
|
+
return useContext(ConfigContext);
|
|
16
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { MjmlMailRoot } from "../components/mailRoot/MjmlMailRoot.js";
|
|
6
|
+
import { MjmlSection } from "../components/section/MjmlSection.js";
|
|
7
|
+
import { renderMailHtml } from "../server/renderMailHtml.js";
|
|
8
|
+
import { ConfigProvider, useConfig } from "./ConfigProvider.js";
|
|
9
|
+
function Probe() {
|
|
10
|
+
const config = useConfig();
|
|
11
|
+
return _jsx("span", { "data-config-value": config.testKey?.value ?? "no-config" });
|
|
12
|
+
}
|
|
13
|
+
describe("useConfig", () => {
|
|
14
|
+
it("returns an empty Config when no ConfigProvider is mounted", () => {
|
|
15
|
+
const html = renderToStaticMarkup(_jsx(Probe, {}));
|
|
16
|
+
expect(html).toContain('data-config-value="no-config"');
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe("MjmlMailRoot config integration", () => {
|
|
20
|
+
it("makes the config value available to descendants via useConfig", () => {
|
|
21
|
+
const { html, mjmlWarnings } = renderMailHtml(_jsx(MjmlMailRoot, { config: { testKey: { value: "from-root" } }, children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: _jsx(Probe, {}) }) }) }) }));
|
|
22
|
+
expect(mjmlWarnings).toEqual([]);
|
|
23
|
+
expect(html).toContain('data-config-value="from-root"');
|
|
24
|
+
});
|
|
25
|
+
it("does not shadow an outer ConfigProvider when no config prop is passed", () => {
|
|
26
|
+
const { html, mjmlWarnings } = renderMailHtml(_jsx(ConfigProvider, { config: { testKey: { value: "from-outer" } }, children: _jsx(MjmlMailRoot, { children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: _jsx(Probe, {}) }) }) }) }) }));
|
|
27
|
+
expect(mjmlWarnings).toEqual([]);
|
|
28
|
+
expect(html).toContain('data-config-value="from-outer"');
|
|
29
|
+
});
|
|
30
|
+
});
|
package/lib/index.d.ts
CHANGED
|
@@ -4,6 +4,18 @@ export { OneOfBlock } from "./blocks/factories/OneOfBlock.js";
|
|
|
4
4
|
export { OptionalBlock } from "./blocks/factories/OptionalBlock.js";
|
|
5
5
|
export type { SupportedBlocks } from "./blocks/factories/types.js";
|
|
6
6
|
export type { PropsWithData } from "./blocks/helpers/PropsWithData.js";
|
|
7
|
+
export type { HtmlPixelImageBlockProps } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
|
|
8
|
+
export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
|
|
9
|
+
export type { MjmlPixelImageBlockProps } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
|
|
10
|
+
export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
|
|
11
|
+
export type { HtmlDividerProps } from "./components/divider/HtmlDivider.js";
|
|
12
|
+
export { HtmlDivider } from "./components/divider/HtmlDivider.js";
|
|
13
|
+
export type { MjmlDividerProps } from "./components/divider/MjmlDivider.js";
|
|
14
|
+
export { MjmlDivider } from "./components/divider/MjmlDivider.js";
|
|
15
|
+
export type { HtmlImageProps } from "./components/image/HtmlImage.js";
|
|
16
|
+
export { HtmlImage } from "./components/image/HtmlImage.js";
|
|
17
|
+
export type { MjmlImageProps } from "./components/image/MjmlImage.js";
|
|
18
|
+
export { MjmlImage } from "./components/image/MjmlImage.js";
|
|
7
19
|
export type { HtmlInlineLinkProps } from "./components/inlineLink/HtmlInlineLink.js";
|
|
8
20
|
export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
|
|
9
21
|
export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
|
|
@@ -15,14 +27,15 @@ export type { MjmlTextProps } from "./components/text/MjmlText.js";
|
|
|
15
27
|
export { MjmlText } from "./components/text/MjmlText.js";
|
|
16
28
|
export type { MjmlWrapperProps } from "./components/wrapper/MjmlWrapper.js";
|
|
17
29
|
export { MjmlWrapper } from "./components/wrapper/MjmlWrapper.js";
|
|
30
|
+
export { type Config, ConfigProvider, type PixelImageBlockConfig, useConfig } from "./config/ConfigProvider.js";
|
|
18
31
|
export { registerStyles } from "./styles/registerStyles.js";
|
|
19
32
|
export { createBreakpoint } from "./theme/createBreakpoint.js";
|
|
20
33
|
export { createTheme } from "./theme/createTheme.js";
|
|
21
34
|
export type { ResponsiveValue } from "./theme/responsiveValue.js";
|
|
22
35
|
export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
|
|
23
36
|
export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
|
|
24
|
-
export type { TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeColors, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
|
|
37
|
+
export type { DividerStyles, DividerVariants, DividerVariantStyles, TextStyles, TextVariants, TextVariantStyles, Theme, ThemeBackgroundColors, ThemeBreakpoint, ThemeBreakpoints, ThemeColors, ThemeDivider, ThemeSizes, ThemeText, } from "./theme/themeTypes.js";
|
|
25
38
|
export { css } from "./utils/css.js";
|
|
26
|
-
export { Mjml, MjmlAccordion, MjmlAccordionElement, type IMjmlAccordionElementProps as MjmlAccordionElementProps, type IMjmlAccordionProps as MjmlAccordionProps, MjmlAccordionText, type IMjmlAccordionTextProps as MjmlAccordionTextProps, MjmlAccordionTitle, type IMjmlAccordionTitleProps as MjmlAccordionTitleProps, MjmlAll, type IMjmlAllProps as MjmlAllProps, MjmlAttributes, type IMjmlAttributesProps as MjmlAttributesProps, MjmlBody, type IMjmlBodyProps as MjmlBodyProps, MjmlBreakpoint, type IMjmlBreakpointProps as MjmlBreakpointProps, MjmlButton, type IMjmlButtonProps as MjmlButtonProps, MjmlCarousel, MjmlCarouselImage, type IMjmlCarouselImageProps as MjmlCarouselImageProps, type IMjmlCarouselProps as MjmlCarouselProps, MjmlClass, type IMjmlClassProps as MjmlClassProps, MjmlColumn, type IMjmlColumnProps as MjmlColumnProps,
|
|
39
|
+
export { Mjml, MjmlAccordion, MjmlAccordionElement, type IMjmlAccordionElementProps as MjmlAccordionElementProps, type IMjmlAccordionProps as MjmlAccordionProps, MjmlAccordionText, type IMjmlAccordionTextProps as MjmlAccordionTextProps, MjmlAccordionTitle, type IMjmlAccordionTitleProps as MjmlAccordionTitleProps, MjmlAll, type IMjmlAllProps as MjmlAllProps, MjmlAttributes, type IMjmlAttributesProps as MjmlAttributesProps, MjmlBody, type IMjmlBodyProps as MjmlBodyProps, MjmlBreakpoint, type IMjmlBreakpointProps as MjmlBreakpointProps, MjmlButton, type IMjmlButtonProps as MjmlButtonProps, MjmlCarousel, MjmlCarouselImage, type IMjmlCarouselImageProps as MjmlCarouselImageProps, type IMjmlCarouselProps as MjmlCarouselProps, MjmlClass, type IMjmlClassProps as MjmlClassProps, MjmlColumn, type IMjmlColumnProps as MjmlColumnProps, MjmlFont, type IMjmlFontProps as MjmlFontProps, MjmlGroup, type IMjmlGroupProps as MjmlGroupProps, MjmlHead, type IMjmlHeadProps as MjmlHeadProps, MjmlHero, type IMjmlHeroProps as MjmlHeroProps, MjmlHtmlAttribute, type IMjmlHtmlAttributeProps as MjmlHtmlAttributeProps, MjmlHtmlAttributes, type IMjmlHtmlAttributesProps as MjmlHtmlAttributesProps, MjmlInclude, type IMjmlIncludeProps as MjmlIncludeProps, MjmlNavbar, MjmlNavbarLink, type IMjmlNavbarLinkProps as MjmlNavbarLinkProps, type IMjmlNavbarProps as MjmlNavbarProps, MjmlPreview, type IMjmlPreviewProps as MjmlPreviewProps, type IMjmlProps as MjmlProps, MjmlRaw, type IMjmlRawProps as MjmlRawProps, MjmlSelector, type IMjmlSelectorProps as MjmlSelectorProps, MjmlSocial, MjmlSocialElement, type IMjmlSocialElementProps as MjmlSocialElementProps, type IMjmlSocialProps as MjmlSocialProps, MjmlSpacer, type IMjmlSpacerProps as MjmlSpacerProps, MjmlStyle, type IMjmlStyleProps as MjmlStyleProps, MjmlTable, type IMjmlTableProps as MjmlTableProps, MjmlTitle, type IMjmlTitleProps as MjmlTitleProps, } from "@faire/mjml-react";
|
|
27
40
|
export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
|
|
28
41
|
export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
|
package/lib/index.js
CHANGED
|
@@ -2,18 +2,25 @@ export { BlocksBlock } from "./blocks/factories/BlocksBlock.js";
|
|
|
2
2
|
export { ListBlock } from "./blocks/factories/ListBlock.js";
|
|
3
3
|
export { OneOfBlock } from "./blocks/factories/OneOfBlock.js";
|
|
4
4
|
export { OptionalBlock } from "./blocks/factories/OptionalBlock.js";
|
|
5
|
+
export { HtmlPixelImageBlock } from "./blocks/pixelImage/HtmlPixelImageBlock.js";
|
|
6
|
+
export { MjmlPixelImageBlock } from "./blocks/pixelImage/MjmlPixelImageBlock.js";
|
|
7
|
+
export { HtmlDivider } from "./components/divider/HtmlDivider.js";
|
|
8
|
+
export { MjmlDivider } from "./components/divider/MjmlDivider.js";
|
|
9
|
+
export { HtmlImage } from "./components/image/HtmlImage.js";
|
|
10
|
+
export { MjmlImage } from "./components/image/MjmlImage.js";
|
|
5
11
|
export { HtmlInlineLink } from "./components/inlineLink/HtmlInlineLink.js";
|
|
6
12
|
export { MjmlMailRoot } from "./components/mailRoot/MjmlMailRoot.js";
|
|
7
13
|
export { MjmlSection } from "./components/section/MjmlSection.js";
|
|
8
14
|
export { HtmlText } from "./components/text/HtmlText.js";
|
|
9
15
|
export { MjmlText } from "./components/text/MjmlText.js";
|
|
10
16
|
export { MjmlWrapper } from "./components/wrapper/MjmlWrapper.js";
|
|
17
|
+
export { ConfigProvider, useConfig } from "./config/ConfigProvider.js";
|
|
11
18
|
export { registerStyles } from "./styles/registerStyles.js";
|
|
12
19
|
export { createBreakpoint } from "./theme/createBreakpoint.js";
|
|
13
20
|
export { createTheme } from "./theme/createTheme.js";
|
|
14
21
|
export { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./theme/responsiveValue.js";
|
|
15
22
|
export { ThemeProvider, useTheme } from "./theme/ThemeProvider.js";
|
|
16
23
|
export { css } from "./utils/css.js";
|
|
17
|
-
export { Mjml, MjmlAccordion, MjmlAccordionElement, MjmlAccordionText, MjmlAccordionTitle, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlButton, MjmlCarousel, MjmlCarouselImage, MjmlClass, MjmlColumn,
|
|
24
|
+
export { Mjml, MjmlAccordion, MjmlAccordionElement, MjmlAccordionText, MjmlAccordionTitle, MjmlAll, MjmlAttributes, MjmlBody, MjmlBreakpoint, MjmlButton, MjmlCarousel, MjmlCarouselImage, MjmlClass, MjmlColumn, MjmlFont, MjmlGroup, MjmlHead, MjmlHero, MjmlHtmlAttribute, MjmlHtmlAttributes, MjmlInclude, MjmlNavbar, MjmlNavbarLink, MjmlPreview, MjmlRaw, MjmlSelector, MjmlSocial, MjmlSocialElement, MjmlSpacer, MjmlStyle, MjmlTable, MjmlTitle, } from "@faire/mjml-react";
|
|
18
25
|
export { MjmlComment, MjmlConditionalComment, MjmlHtml, MjmlTrackingPixel, MjmlYahooStyle } from "@faire/mjml-react/extensions/index.js";
|
|
19
26
|
export { renderToMjml } from "@faire/mjml-react/utils/renderToMjml.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
|
|
2
|
+
import { MjmlClass, MjmlColumn, MjmlText, MjmlTitle } from "@faire/mjml-react";
|
|
3
3
|
import { describe, expect, it } from "vitest";
|
|
4
4
|
import { MjmlMailRoot } from "../components/mailRoot/MjmlMailRoot.js";
|
|
5
5
|
import { MjmlSection } from "../components/section/MjmlSection.js";
|
|
@@ -36,6 +36,15 @@ describe("server/renderMailHtml", () => {
|
|
|
36
36
|
expect(html).toContain(".themed");
|
|
37
37
|
expect(html).toContain("width: 600px");
|
|
38
38
|
});
|
|
39
|
+
it("renders the head slot content inside <mj-head>", () => {
|
|
40
|
+
const titleText = "Welcome to Comet";
|
|
41
|
+
const { html } = renderMailHtml(_jsx(MjmlMailRoot, { head: _jsx(MjmlTitle, { children: titleText }), children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Hello" }) }) }) }));
|
|
42
|
+
expect(html).toContain(`<title>${titleText}</title>`);
|
|
43
|
+
});
|
|
44
|
+
it("renders the attributes slot content inside <mj-attributes>", () => {
|
|
45
|
+
const { html } = renderMailHtml(_jsx(MjmlMailRoot, { attributes: _jsx(MjmlClass, { name: "cta", color: "#FF0000" }), children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { mjmlClass: "cta", children: "Hello" }) }) }) }));
|
|
46
|
+
expect(html).toContain("color:#FF0000");
|
|
47
|
+
});
|
|
39
48
|
it("produces no MJML warnings for a valid component tree", () => {
|
|
40
49
|
const { mjmlWarnings } = renderMailHtml(_jsx(MjmlMailRoot, { children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "No warnings expected" }) }) }) }));
|
|
41
50
|
expect(mjmlWarnings).toEqual([]);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeColors, ThemeSizes, ThemeText } from "./themeTypes.js";
|
|
1
|
+
import type { Theme, ThemeBackgroundColors, ThemeBreakpoints, ThemeColors, ThemeDivider, ThemeSizes, ThemeText } from "./themeTypes.js";
|
|
2
2
|
type CreateThemeOverrides = {
|
|
3
3
|
sizes?: Partial<ThemeSizes>;
|
|
4
4
|
breakpoints?: Partial<ThemeBreakpoints>;
|
|
5
5
|
text?: Partial<ThemeText>;
|
|
6
|
+
divider?: Partial<ThemeDivider>;
|
|
6
7
|
colors?: {
|
|
7
8
|
background?: Partial<ThemeBackgroundColors>;
|
|
8
9
|
} & Partial<Omit<ThemeColors, "background">>;
|
package/lib/theme/createTheme.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { defaultDividerStyles } from "../components/divider/defaultDividerStyles.js";
|
|
1
2
|
import { createBreakpoint } from "./createBreakpoint.js";
|
|
2
3
|
export const defaultTheme = {
|
|
3
4
|
sizes: {
|
|
@@ -14,6 +15,7 @@ export const defaultTheme = {
|
|
|
14
15
|
lineHeight: "20px",
|
|
15
16
|
bottomSpacing: "16px",
|
|
16
17
|
},
|
|
18
|
+
divider: defaultDividerStyles,
|
|
17
19
|
colors: {
|
|
18
20
|
background: {
|
|
19
21
|
body: "#F2F2F2",
|
|
@@ -46,6 +46,46 @@ export interface ThemeText extends TextStyles {
|
|
|
46
46
|
defaultVariant?: VariantName;
|
|
47
47
|
variants?: VariantsRecord;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Single source of truth for divider style property names and their value types.
|
|
51
|
+
* Both `DividerStyles` and `DividerVariantStyles` are derived from this interface.
|
|
52
|
+
*/
|
|
53
|
+
interface DividerStyleMap {
|
|
54
|
+
height: number;
|
|
55
|
+
backgroundColor: string;
|
|
56
|
+
backgroundImage: string;
|
|
57
|
+
}
|
|
58
|
+
/** Base divider styles where each property holds a plain value. */
|
|
59
|
+
export type DividerStyles = {
|
|
60
|
+
[K in keyof DividerStyleMap]?: DividerStyleMap[K];
|
|
61
|
+
};
|
|
62
|
+
/** Variant divider styles where each property supports responsive values. */
|
|
63
|
+
export type DividerVariantStyles = {
|
|
64
|
+
[K in keyof DividerStyleMap]?: ResponsiveValue<DividerStyleMap[K]>;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Defines the variants available on the `MjmlDivider` and `HtmlDivider` components.
|
|
68
|
+
*
|
|
69
|
+
* ```ts
|
|
70
|
+
* declare module "@comet/mail-react" {
|
|
71
|
+
* interface DividerVariants {
|
|
72
|
+
* thin: true;
|
|
73
|
+
* thick: true;
|
|
74
|
+
* }
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export interface DividerVariants {
|
|
79
|
+
}
|
|
80
|
+
type DividerVariantsRecord = keyof DividerVariants extends never ? Record<string, DividerVariantStyles> : {
|
|
81
|
+
[K in keyof DividerVariants]?: DividerVariantStyles;
|
|
82
|
+
};
|
|
83
|
+
export type DividerVariantName = keyof DividerVariants extends never ? string : keyof DividerVariants;
|
|
84
|
+
/** Theme configuration for divider styles, variants, and default variant. */
|
|
85
|
+
export interface ThemeDivider extends DividerStyles {
|
|
86
|
+
defaultVariant?: DividerVariantName;
|
|
87
|
+
variants?: DividerVariantsRecord;
|
|
88
|
+
}
|
|
49
89
|
/**
|
|
50
90
|
* A resolved breakpoint with its numeric value and a ready-to-use media query
|
|
51
91
|
* string that targets viewports narrower than this breakpoint.
|
|
@@ -101,6 +141,7 @@ export interface Theme {
|
|
|
101
141
|
sizes: ThemeSizes;
|
|
102
142
|
breakpoints: ThemeBreakpoints;
|
|
103
143
|
text: ThemeText;
|
|
144
|
+
divider: ThemeDivider;
|
|
104
145
|
colors: ThemeColors;
|
|
105
146
|
}
|
|
106
147
|
export {};
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/mail-react",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.5",
|
|
4
4
|
"description": "Utilities for building HTML emails with React",
|
|
5
5
|
"license": "BSD-2-Clause",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"directory": "packages/mail-react",
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/vivid-planet/comet"
|
|
11
|
+
},
|
|
7
12
|
"exports": {
|
|
8
13
|
".": "./lib/index.js",
|
|
9
14
|
"./client": "./lib/client/index.js",
|
|
10
15
|
"./server": "./lib/server/index.js",
|
|
11
16
|
"./storybook": "./lib/storybook/index.js"
|
|
12
17
|
},
|
|
18
|
+
"sideEffects": false,
|
|
13
19
|
"files": [
|
|
14
20
|
"lib/*"
|
|
15
21
|
],
|
|
@@ -20,25 +26,24 @@
|
|
|
20
26
|
"mjml-browser": "^4.18.0"
|
|
21
27
|
},
|
|
22
28
|
"devDependencies": {
|
|
23
|
-
"@fission-ai/openspec": "^1.2.0",
|
|
24
29
|
"@storybook/addon-docs": "^10.3.5",
|
|
25
30
|
"@storybook/react-vite": "^10.3.5",
|
|
26
31
|
"@types/mjml": "^4.7.4",
|
|
27
32
|
"@types/mjml-browser": "^4.15.0",
|
|
28
|
-
"@types/react": "^19.2.
|
|
33
|
+
"@types/react": "^19.2.15",
|
|
29
34
|
"@types/react-dom": "^19.2.3",
|
|
30
35
|
"chokidar-cli": "^3.0.0",
|
|
31
|
-
"eslint": "^9.39.
|
|
36
|
+
"eslint": "^9.39.4",
|
|
32
37
|
"npm-run-all2": "^8.0.4",
|
|
33
38
|
"prettier": "^3.6.2",
|
|
34
|
-
"react": "^19.2.
|
|
35
|
-
"react-dom": "^19.2.
|
|
39
|
+
"react": "^19.2.6",
|
|
40
|
+
"react-dom": "^19.2.6",
|
|
36
41
|
"rimraf": "^6.1.2",
|
|
37
42
|
"storybook": "^10.3.5",
|
|
38
43
|
"typescript": "^5.9.3",
|
|
39
44
|
"vitest": "^4.0.16",
|
|
40
|
-
"@comet/
|
|
41
|
-
"@comet/
|
|
45
|
+
"@comet/eslint-config": "9.0.0-beta.5",
|
|
46
|
+
"@comet/cli": "9.0.0-beta.5"
|
|
42
47
|
},
|
|
43
48
|
"peerDependencies": {
|
|
44
49
|
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
@@ -71,7 +76,6 @@
|
|
|
71
76
|
"lint:fix:prettier": "pnpm run lint:prettier --write",
|
|
72
77
|
"lint:prettier": "pnpm exec prettier --check '*.{ts,js,json,md,yml,yaml}'",
|
|
73
78
|
"lint:tsc": "tsc",
|
|
74
|
-
"openspec:install-skills": "openspec init --tools github-copilot,cursor,claude",
|
|
75
79
|
"storybook": "storybook dev -p 6066 --no-open",
|
|
76
80
|
"test": "vitest --run",
|
|
77
81
|
"test:watch": "vitest --watch"
|