@comet/mail-react 9.0.0-beta.1 → 9.0.0-beta.3
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/lib/__stories__/examples/CustomBackgroundForFooter.stories.d.ts +5 -0
- package/lib/__stories__/examples/CustomBackgroundForFooter.stories.js +32 -0
- package/lib/__stories__/examples/CustomNestedFooterTheme.stories.d.ts +5 -0
- package/lib/__stories__/examples/CustomNestedFooterTheme.stories.js +32 -0
- package/lib/__stories__/examples/NotificationEmail.stories.d.ts +5 -0
- package/lib/__stories__/examples/NotificationEmail.stories.js +25 -0
- package/lib/__stories__/layout-patterns/AsymmetricTwoColumnLayout.stories.d.ts +6 -0
- package/lib/__stories__/layout-patterns/AsymmetricTwoColumnLayout.stories.js +107 -0
- package/lib/__stories__/layout-patterns/SymmetricFourColumnLayout.stories.d.ts +4 -0
- package/lib/__stories__/layout-patterns/SymmetricFourColumnLayout.stories.js +58 -0
- package/lib/__stories__/layout-patterns/SymmetricThreeColumnLayout.stories.d.ts +5 -0
- package/lib/__stories__/layout-patterns/SymmetricThreeColumnLayout.stories.js +104 -0
- package/lib/__stories__/layout-patterns/SymmetricTwoColumnLayout.stories.d.ts +4 -0
- package/lib/__stories__/layout-patterns/SymmetricTwoColumnLayout.stories.js +47 -0
- package/lib/blocks/factories/BlocksBlock.d.ts +1 -1
- package/lib/blocks/factories/OneOfBlock.d.ts +2 -2
- package/lib/blocks/factories/OptionalBlock.d.ts +1 -1
- package/lib/blocks/factories/types.d.ts +1 -1
- package/lib/client/renderMailHtml.d.ts +1 -1
- package/lib/components/inlineLink/HtmlInlineLink.d.ts +10 -0
- package/lib/components/inlineLink/HtmlInlineLink.js +35 -0
- package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.d.ts +12 -0
- package/lib/components/inlineLink/__stories__/HtmlInlineLink.stories.js +56 -0
- package/lib/components/mailRoot/MjmlMailRoot.js +1 -1
- package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.d.ts +1 -0
- package/lib/components/mailRoot/__stories__/MjmlMailRoot.stories.js +6 -1
- package/lib/components/section/MjmlSection.d.ts +2 -2
- package/lib/components/section/MjmlSection.js +10 -5
- package/lib/components/section/__stories__/MjmlSection.stories.d.ts +1 -0
- package/lib/components/section/__stories__/MjmlSection.stories.js +6 -0
- package/lib/components/text/HtmlText.d.ts +48 -0
- package/lib/components/text/HtmlText.js +47 -0
- package/lib/components/text/MjmlText.d.ts +37 -0
- package/lib/components/text/MjmlText.js +65 -0
- package/lib/components/text/OutlookTextStyleContext.d.ts +9 -0
- package/lib/components/text/OutlookTextStyleContext.js +10 -0
- package/lib/components/text/__stories__/HtmlText.stories.d.ts +12 -0
- package/lib/components/text/__stories__/HtmlText.stories.js +77 -0
- package/lib/components/text/__stories__/MjmlText.stories.d.ts +10 -0
- package/lib/components/text/__stories__/MjmlText.stories.js +71 -0
- package/lib/components/text/__tests__/HtmlText.test.d.ts +1 -0
- package/lib/components/text/__tests__/HtmlText.test.js +157 -0
- package/lib/components/text/__tests__/MjmlText.test.d.ts +1 -0
- package/lib/components/text/__tests__/MjmlText.test.js +112 -0
- package/lib/components/text/textStyles.d.ts +12 -0
- package/lib/components/text/textStyles.js +74 -0
- package/lib/components/wrapper/InsideMjmlWrapperContext.d.ts +3 -0
- package/lib/components/wrapper/InsideMjmlWrapperContext.js +6 -0
- package/lib/components/wrapper/MjmlWrapper.d.ts +7 -0
- package/lib/components/wrapper/MjmlWrapper.js +12 -0
- package/lib/components/wrapper/__stories__/MjmlWrapper.stories.d.ts +10 -0
- package/lib/components/wrapper/__stories__/MjmlWrapper.stories.js +36 -0
- package/lib/index.d.ts +10 -2
- package/lib/index.js +5 -1
- package/lib/server/renderMailHtml.d.ts +1 -1
- package/lib/storybook/MailRendererDecorator.d.ts +7 -1
- package/lib/storybook/MailRendererDecorator.js +2 -2
- package/lib/storybook/preview.d.ts +34 -0
- package/lib/storybook/preview.js +22 -0
- package/lib/theme/__stories__/ThemeProvider.stories.js +2 -2
- package/lib/theme/createTheme.d.ts +5 -1
- package/lib/theme/createTheme.js +6 -0
- package/lib/theme/createTheme.test.js +32 -0
- package/lib/theme/defaultTheme.js +12 -0
- package/lib/theme/responsiveValue.d.ts +6 -0
- package/lib/theme/responsiveValue.js +11 -0
- package/lib/theme/responsiveValue.test.js +18 -1
- package/lib/theme/themeTypes.d.ts +59 -0
- package/package.json +11 -8
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
4
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
5
|
+
import { MjmlText } from "../../text/MjmlText.js";
|
|
6
|
+
import { HtmlInlineLink } from "../HtmlInlineLink.js";
|
|
7
|
+
const config = {
|
|
8
|
+
title: "Components/HtmlInlineLink",
|
|
9
|
+
component: HtmlInlineLink,
|
|
10
|
+
tags: ["autodocs"],
|
|
11
|
+
};
|
|
12
|
+
export default config;
|
|
13
|
+
export const InText = {
|
|
14
|
+
parameters: {
|
|
15
|
+
theme: createTheme({
|
|
16
|
+
text: {
|
|
17
|
+
fontFamily: "Arial, sans-serif",
|
|
18
|
+
fontSize: "16px",
|
|
19
|
+
lineHeight: "24px",
|
|
20
|
+
color: "#333333",
|
|
21
|
+
},
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsxs(MjmlText, { children: ["Visit our ", _jsx(HtmlInlineLink, { href: "https://example.com", children: "website" }), " for more information."] }) }) })),
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Using `!important` overrides the component's responsive `inherit !important`
|
|
28
|
+
* reset, ensuring the custom color persists on both desktop and mobile.
|
|
29
|
+
*/
|
|
30
|
+
export const CustomColorOverride = {
|
|
31
|
+
parameters: {
|
|
32
|
+
theme: createTheme({
|
|
33
|
+
text: {
|
|
34
|
+
fontFamily: "Arial, sans-serif",
|
|
35
|
+
fontSize: "16px",
|
|
36
|
+
color: "#333333",
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
},
|
|
40
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsxs(MjmlText, { children: ["Click", " ", _jsx(HtmlInlineLink, { href: "https://example.com", style: { color: "#0066cc !important" }, children: "here" }), " ", "to continue."] }) }) })),
|
|
41
|
+
};
|
|
42
|
+
export const WithTextVariants = {
|
|
43
|
+
parameters: {
|
|
44
|
+
theme: createTheme({
|
|
45
|
+
text: {
|
|
46
|
+
fontFamily: "Arial, sans-serif",
|
|
47
|
+
color: "#333333",
|
|
48
|
+
variants: {
|
|
49
|
+
heading: { fontSize: "32px", fontWeight: 700, lineHeight: "40px" },
|
|
50
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
},
|
|
55
|
+
render: () => (_jsx(MjmlSection, { children: _jsxs(MjmlColumn, { children: [_jsxs(MjmlText, { variant: "heading", bottomSpacing: true, children: ["Welcome to ", _jsx(HtmlInlineLink, { href: "https://example.com", children: "our platform" })] }), _jsxs(MjmlText, { variant: "body", children: ["Explore our ", _jsx(HtmlInlineLink, { href: "https://example.com/features", children: "features" }), " and start building today."] })] }) })),
|
|
56
|
+
};
|
|
@@ -16,5 +16,5 @@ import { ThemeProvider } from "../../theme/ThemeProvider.js";
|
|
|
16
16
|
*/
|
|
17
17
|
export function MjmlMailRoot({ theme: themeProp, children }) {
|
|
18
18
|
const theme = themeProp ?? createTheme();
|
|
19
|
-
return (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Mjml, { children: [_jsxs(MjmlHead, { children: [_jsx(MjmlAttributes, { children: _jsx(MjmlAll, { padding: "0" }) }), _jsx(MjmlBreakpoint, { width: `${theme.breakpoints.mobile.value}px` }), _jsx(Styles, {})] }), _jsx(MjmlBody, { width: theme.sizes.bodyWidth, children: children })] }) }));
|
|
19
|
+
return (_jsx(ThemeProvider, { theme: theme, children: _jsxs(Mjml, { children: [_jsxs(MjmlHead, { children: [_jsx(MjmlAttributes, { children: _jsx(MjmlAll, { padding: "0", fontFamily: theme.text.fontFamily }) }), _jsx(MjmlBreakpoint, { width: `${theme.breakpoints.mobile.value}px` }), _jsx(Styles, {})] }), _jsx(MjmlBody, { width: theme.sizes.bodyWidth, backgroundColor: theme.colors.background.body, children: children })] }) }));
|
|
20
20
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { MjmlColumn, MjmlText } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
3
4
|
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
4
5
|
import { MjmlMailRoot } from "../MjmlMailRoot.js";
|
|
5
6
|
const config = {
|
|
6
7
|
title: "Components/MjmlMailRoot",
|
|
7
8
|
component: MjmlMailRoot,
|
|
8
9
|
tags: ["autodocs"],
|
|
10
|
+
parameters: { mailRoot: false },
|
|
9
11
|
};
|
|
10
12
|
export default config;
|
|
11
13
|
export const Basic = {
|
|
12
|
-
render: () => (_jsx(MjmlMailRoot, { children: _jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Hello from MjmlMailRoot" }) }) }) })),
|
|
14
|
+
render: () => (_jsx(MjmlMailRoot, { children: _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Hello from MjmlMailRoot" }) }) }) })),
|
|
15
|
+
};
|
|
16
|
+
export const CustomBodyBackground = {
|
|
17
|
+
render: () => (_jsx(MjmlMailRoot, { theme: createTheme({ colors: { background: { body: "#EAEAEA" } } }), children: _jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Custom body background color" }) }) }) })),
|
|
13
18
|
};
|
|
@@ -11,5 +11,5 @@ export type MjmlSectionProps = IMjmlSectionProps & {
|
|
|
11
11
|
group?: Partial<IMjmlGroupProps>;
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
|
-
/** A section wrapper for email layouts. Must be a direct child of `MjmlBody`. */
|
|
15
|
-
export declare function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...
|
|
14
|
+
/** A section wrapper for email layouts. Must be a direct child of `MjmlBody` or `MjmlWrapper`. */
|
|
15
|
+
export declare function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }: MjmlSectionProps): ReactNode;
|
|
@@ -5,12 +5,15 @@ import { registerStyles } from "../../styles/registerStyles.js";
|
|
|
5
5
|
import { getDefaultFromResponsiveValue, getResponsiveOverrides } from "../../theme/responsiveValue.js";
|
|
6
6
|
import { useOptionalTheme } from "../../theme/ThemeProvider.js";
|
|
7
7
|
import { css } from "../../utils/css.js";
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
import { useIsInsideMjmlWrapper } from "../wrapper/InsideMjmlWrapperContext.js";
|
|
9
|
+
/** A section wrapper for email layouts. Must be a direct child of `MjmlBody` or `MjmlWrapper`. */
|
|
10
|
+
export function MjmlSection({ children, indent, disableResponsiveBehavior, slotProps, className, ...restProps }) {
|
|
10
11
|
const theme = useOptionalTheme();
|
|
12
|
+
const isInsideWrapper = useIsInsideMjmlWrapper();
|
|
11
13
|
const indentProps = indent ? getIndentProps(theme) : {};
|
|
12
14
|
const resolvedClassName = clsx("mjmlSection", indent && "mjmlSection--indented", className);
|
|
13
|
-
|
|
15
|
+
const themeBackgroundProps = theme && !isInsideWrapper ? { backgroundColor: theme.colors.background.content } : {};
|
|
16
|
+
return (_jsx(BaseMjmlSection, { className: resolvedClassName, ...themeBackgroundProps, ...indentProps, ...restProps, children: disableResponsiveBehavior ? _jsx(MjmlGroup, { ...slotProps?.group, children: children }) : _jsx(_Fragment, { children: children }) }));
|
|
14
17
|
}
|
|
15
18
|
function getIndentProps(theme) {
|
|
16
19
|
if (theme === null) {
|
|
@@ -23,13 +26,15 @@ function getIndentProps(theme) {
|
|
|
23
26
|
}
|
|
24
27
|
registerStyles((theme) => {
|
|
25
28
|
const overrides = getResponsiveOverrides(theme.sizes.contentIndentation);
|
|
26
|
-
if (overrides.length === 0)
|
|
29
|
+
if (overrides.length === 0) {
|
|
27
30
|
return css ``;
|
|
31
|
+
}
|
|
28
32
|
return overrides
|
|
29
33
|
.map((override) => {
|
|
30
34
|
const breakpoint = theme.breakpoints[override.breakpointKey];
|
|
31
|
-
if (!breakpoint)
|
|
35
|
+
if (!breakpoint) {
|
|
32
36
|
return "";
|
|
37
|
+
}
|
|
33
38
|
return css `
|
|
34
39
|
${breakpoint.belowMediaQuery} {
|
|
35
40
|
.mjmlSection--indented > table > tbody > tr > td {
|
|
@@ -16,6 +16,12 @@ export const Indented = {
|
|
|
16
16
|
},
|
|
17
17
|
render: (args) => (_jsx(MjmlSection, { ...args, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Indented section content" }) }) })),
|
|
18
18
|
};
|
|
19
|
+
export const ExplicitBackgroundColor = {
|
|
20
|
+
args: {
|
|
21
|
+
backgroundColor: "#FF0000",
|
|
22
|
+
},
|
|
23
|
+
render: (args) => (_jsx(MjmlSection, { ...args, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Explicit backgroundColor overrides theme default" }) }) })),
|
|
24
|
+
};
|
|
19
25
|
export const DisabledResponsiveBehavior = {
|
|
20
26
|
args: {
|
|
21
27
|
disableResponsiveBehavior: true,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, JSX, ReactNode, TdHTMLAttributes } from "react";
|
|
2
|
+
import type { VariantName } from "../../theme/themeTypes.js";
|
|
3
|
+
interface HtmlTextOwnProps {
|
|
4
|
+
/**
|
|
5
|
+
* The component's variant to apply, as defined in the theme.
|
|
6
|
+
*
|
|
7
|
+
* Custom variants should be defined in the theme through module augmentation:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* declare module "@comet/mail-react" {
|
|
11
|
+
* interface TextVariants { heading: true; body: true }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const theme = createTheme({
|
|
17
|
+
* text: {
|
|
18
|
+
* variants: {
|
|
19
|
+
* heading: { fontSize: "24px" },
|
|
20
|
+
* body: { fontSize: "16px" },
|
|
21
|
+
* },
|
|
22
|
+
* },
|
|
23
|
+
* });
|
|
24
|
+
*/
|
|
25
|
+
variant?: VariantName;
|
|
26
|
+
/** When true, applies spacing below the text. */
|
|
27
|
+
bottomSpacing?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export type HtmlTextProps<E extends keyof JSX.IntrinsicElements = "td"> = HtmlTextOwnProps & {
|
|
30
|
+
/**
|
|
31
|
+
* The HTML element to render instead of the default `<td>`.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <HtmlText element="div">Rendered as a div</HtmlText>
|
|
36
|
+
* <HtmlText element="a" href="/link">Rendered as an anchor</HtmlText>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
element?: E;
|
|
40
|
+
} & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">;
|
|
41
|
+
/**
|
|
42
|
+
* Themed text component for use inside MJML ending tags or outside of the MJML context.
|
|
43
|
+
*/
|
|
44
|
+
export declare function HtmlText<E extends keyof JSX.IntrinsicElements>(props: HtmlTextOwnProps & {
|
|
45
|
+
element: E;
|
|
46
|
+
} & Omit<ComponentPropsWithoutRef<E>, keyof HtmlTextOwnProps | "element">): ReactNode;
|
|
47
|
+
export declare function HtmlText(props: HtmlTextOwnProps & Omit<TdHTMLAttributes<HTMLTableCellElement>, keyof HtmlTextOwnProps>): ReactNode;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import clsx from "clsx";
|
|
3
|
+
import { registerStyles } from "../../styles/registerStyles.js";
|
|
4
|
+
import { getDefaultOrUndefined } from "../../theme/responsiveValue.js";
|
|
5
|
+
import { useTheme } from "../../theme/ThemeProvider.js";
|
|
6
|
+
import { OutlookTextStyleProvider } from "./OutlookTextStyleContext.js";
|
|
7
|
+
import { generateResponsiveTextCss } from "./textStyles.js";
|
|
8
|
+
export function HtmlText({ element: Element = "td", variant: variantProp, bottomSpacing, className, style, children, ...restProps }) {
|
|
9
|
+
const theme = useTheme();
|
|
10
|
+
const { defaultVariant, variants, ...baseStyles } = theme.text;
|
|
11
|
+
const activeVariant = variantProp ?? defaultVariant;
|
|
12
|
+
const variantStyles = activeVariant ? variants?.[activeVariant] : undefined;
|
|
13
|
+
const mergedStyles = variantStyles ? { ...baseStyles, ...variantStyles } : baseStyles;
|
|
14
|
+
const themeStyle = {
|
|
15
|
+
fontFamily: getDefaultOrUndefined(mergedStyles.fontFamily),
|
|
16
|
+
fontSize: getDefaultOrUndefined(mergedStyles.fontSize),
|
|
17
|
+
fontWeight: getDefaultOrUndefined(mergedStyles.fontWeight),
|
|
18
|
+
fontStyle: getDefaultOrUndefined(mergedStyles.fontStyle),
|
|
19
|
+
lineHeight: getDefaultOrUndefined(mergedStyles.lineHeight),
|
|
20
|
+
letterSpacing: getDefaultOrUndefined(mergedStyles.letterSpacing),
|
|
21
|
+
textDecoration: getDefaultOrUndefined(mergedStyles.textDecoration),
|
|
22
|
+
textTransform: getDefaultOrUndefined(mergedStyles.textTransform),
|
|
23
|
+
color: getDefaultOrUndefined(mergedStyles.color),
|
|
24
|
+
...(getDefaultOrUndefined(mergedStyles.lineHeight) !== undefined && { msoLineHeightRule: "exactly" }),
|
|
25
|
+
...(bottomSpacing && { paddingBottom: getDefaultOrUndefined(mergedStyles.bottomSpacing) }),
|
|
26
|
+
};
|
|
27
|
+
const outlookTextStyleValues = {
|
|
28
|
+
fontFamily: themeStyle.fontFamily,
|
|
29
|
+
fontSize: themeStyle.fontSize,
|
|
30
|
+
lineHeight: themeStyle.lineHeight,
|
|
31
|
+
fontWeight: themeStyle.fontWeight,
|
|
32
|
+
color: themeStyle.color,
|
|
33
|
+
...(style?.fontFamily !== undefined && { fontFamily: style.fontFamily }),
|
|
34
|
+
...(style?.fontSize !== undefined && { fontSize: style.fontSize }),
|
|
35
|
+
...(style?.lineHeight !== undefined && { lineHeight: style.lineHeight }),
|
|
36
|
+
...(style?.fontWeight !== undefined && { fontWeight: style.fontWeight }),
|
|
37
|
+
...(style?.color !== undefined && { color: style.color }),
|
|
38
|
+
};
|
|
39
|
+
return (_jsx(Element, { ...restProps, className: clsx("htmlText", activeVariant && `htmlText--${activeVariant}`, bottomSpacing && "htmlText--bottomSpacing", className), style: { ...themeStyle, ...style }, children: _jsx(OutlookTextStyleProvider, { value: outlookTextStyleValues, children: children }) }));
|
|
40
|
+
}
|
|
41
|
+
function generateHtmlTextStyles(theme) {
|
|
42
|
+
return generateResponsiveTextCss(theme, {
|
|
43
|
+
styleSelector: (variantName) => `.htmlText--${variantName}`,
|
|
44
|
+
spacingSelector: (variantName) => `.htmlText--bottomSpacing.htmlText--${variantName}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
registerStyles(generateHtmlTextStyles);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type IMjmlTextProps } from "@faire/mjml-react";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { Theme, VariantName } from "../../theme/themeTypes.js";
|
|
4
|
+
export type MjmlTextProps = IMjmlTextProps & {
|
|
5
|
+
/**
|
|
6
|
+
* The component's variant to apply, as defined in the theme.
|
|
7
|
+
*
|
|
8
|
+
* Custom variants should be defined in the theme, through module augmentation.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* declare module "@comet/mail-react" {
|
|
12
|
+
* interface TextVariants { heading: true; body: true }
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* ```ts
|
|
17
|
+
* const theme = createTheme({
|
|
18
|
+
* text: {
|
|
19
|
+
* variants: {
|
|
20
|
+
* heading: { fontSize: "24px" },
|
|
21
|
+
* body: { fontSize: "16px" },
|
|
22
|
+
* },
|
|
23
|
+
* },
|
|
24
|
+
* });
|
|
25
|
+
*/
|
|
26
|
+
variant?: VariantName;
|
|
27
|
+
/** When true, applies spacing below the text. */
|
|
28
|
+
bottomSpacing?: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Text component that can be styled using the theme, optionally using a variant.
|
|
32
|
+
*
|
|
33
|
+
* Works without a `ThemeProvider` as a plain pass-through to the base MJML text component.
|
|
34
|
+
* The `variant` and `bottomSpacing` props require a `ThemeProvider` (or `MjmlMailRoot`).
|
|
35
|
+
*/
|
|
36
|
+
export declare function MjmlText({ variant: variantProp, bottomSpacing, className, children, ...restProps }: MjmlTextProps): ReactNode;
|
|
37
|
+
export declare function generateTextStyles(theme: Theme): string;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlText as BaseMjmlText } from "@faire/mjml-react";
|
|
3
|
+
import clsx from "clsx";
|
|
4
|
+
import { registerStyles } from "../../styles/registerStyles.js";
|
|
5
|
+
import { getDefaultOrUndefined } from "../../theme/responsiveValue.js";
|
|
6
|
+
import { useOptionalTheme } from "../../theme/ThemeProvider.js";
|
|
7
|
+
import { OutlookTextStyleProvider } from "./OutlookTextStyleContext.js";
|
|
8
|
+
import { generateResponsiveTextCss } from "./textStyles.js";
|
|
9
|
+
/**
|
|
10
|
+
* Text component that can be styled using the theme, optionally using a variant.
|
|
11
|
+
*
|
|
12
|
+
* Works without a `ThemeProvider` as a plain pass-through to the base MJML text component.
|
|
13
|
+
* The `variant` and `bottomSpacing` props require a `ThemeProvider` (or `MjmlMailRoot`).
|
|
14
|
+
*/
|
|
15
|
+
export function MjmlText({ variant: variantProp, bottomSpacing, className, children, ...restProps }) {
|
|
16
|
+
const theme = useOptionalTheme();
|
|
17
|
+
const themedProps = getThemedProps(theme, variantProp, bottomSpacing, restProps);
|
|
18
|
+
const resolvedClassName = clsx("mjmlText", themedProps.activeVariant && `mjmlText--${themedProps.activeVariant}`, bottomSpacing && "mjmlText--bottomSpacing", className);
|
|
19
|
+
return (_jsx(BaseMjmlText, { ...themedProps.baseProps, className: resolvedClassName, ...restProps, children: themedProps.outlookTextStyleValues !== null ? (_jsx(OutlookTextStyleProvider, { value: themedProps.outlookTextStyleValues, children: children })) : (children) }));
|
|
20
|
+
}
|
|
21
|
+
function getThemedProps(theme, variantProp, bottomSpacing, explicitProps) {
|
|
22
|
+
if (theme === null) {
|
|
23
|
+
if (variantProp !== undefined) {
|
|
24
|
+
throw new Error("The `variant` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
|
|
25
|
+
}
|
|
26
|
+
if (bottomSpacing) {
|
|
27
|
+
throw new Error("The `bottomSpacing` prop requires being wrapped in a ThemeProvider or MjmlMailRoot.");
|
|
28
|
+
}
|
|
29
|
+
return { activeVariant: undefined, baseProps: {}, outlookTextStyleValues: null };
|
|
30
|
+
}
|
|
31
|
+
const { defaultVariant, variants, ...baseStyles } = theme.text;
|
|
32
|
+
const activeVariant = variantProp ?? defaultVariant;
|
|
33
|
+
const variantStyles = activeVariant ? variants?.[activeVariant] : undefined;
|
|
34
|
+
const mergedStyles = variantStyles ? { ...baseStyles, ...variantStyles } : baseStyles;
|
|
35
|
+
const fontWeightDefault = getDefaultOrUndefined(mergedStyles.fontWeight);
|
|
36
|
+
return {
|
|
37
|
+
activeVariant,
|
|
38
|
+
baseProps: {
|
|
39
|
+
fontFamily: getDefaultOrUndefined(mergedStyles.fontFamily),
|
|
40
|
+
fontSize: getDefaultOrUndefined(mergedStyles.fontSize),
|
|
41
|
+
fontWeight: fontWeightDefault !== undefined ? String(fontWeightDefault) : undefined,
|
|
42
|
+
fontStyle: getDefaultOrUndefined(mergedStyles.fontStyle),
|
|
43
|
+
lineHeight: getDefaultOrUndefined(mergedStyles.lineHeight),
|
|
44
|
+
letterSpacing: getDefaultOrUndefined(mergedStyles.letterSpacing),
|
|
45
|
+
textDecoration: getDefaultOrUndefined(mergedStyles.textDecoration),
|
|
46
|
+
textTransform: getDefaultOrUndefined(mergedStyles.textTransform),
|
|
47
|
+
color: getDefaultOrUndefined(mergedStyles.color),
|
|
48
|
+
paddingBottom: bottomSpacing ? getDefaultOrUndefined(mergedStyles.bottomSpacing) : undefined,
|
|
49
|
+
},
|
|
50
|
+
outlookTextStyleValues: {
|
|
51
|
+
fontFamily: explicitProps.fontFamily ?? getDefaultOrUndefined(mergedStyles.fontFamily),
|
|
52
|
+
fontSize: explicitProps.fontSize ?? getDefaultOrUndefined(mergedStyles.fontSize),
|
|
53
|
+
fontWeight: explicitProps.fontWeight ?? fontWeightDefault,
|
|
54
|
+
lineHeight: explicitProps.lineHeight ?? getDefaultOrUndefined(mergedStyles.lineHeight),
|
|
55
|
+
color: explicitProps.color ?? getDefaultOrUndefined(mergedStyles.color),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export function generateTextStyles(theme) {
|
|
60
|
+
return generateResponsiveTextCss(theme, {
|
|
61
|
+
styleSelector: (variantName) => `.mjmlText--${variantName} > div`,
|
|
62
|
+
spacingSelector: (variantName) => `.mjmlText--bottomSpacing.mjmlText--${variantName}`,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
registerStyles(generateTextStyles);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from "react";
|
|
2
|
+
type OutlookTextStyleValues = Pick<CSSProperties, "fontFamily" | "fontSize" | "lineHeight" | "fontWeight" | "color">;
|
|
3
|
+
declare function OutlookTextStyleProvider({ value, children }: {
|
|
4
|
+
value: OutlookTextStyleValues;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
}): ReactNode;
|
|
7
|
+
declare function useOutlookTextStyle(): OutlookTextStyleValues | null;
|
|
8
|
+
export { OutlookTextStyleProvider, useOutlookTextStyle };
|
|
9
|
+
export type { OutlookTextStyleValues };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
const OutlookTextStyleContext = createContext(null);
|
|
4
|
+
function OutlookTextStyleProvider({ value, children }) {
|
|
5
|
+
return _jsx(OutlookTextStyleContext, { value: value, children: children });
|
|
6
|
+
}
|
|
7
|
+
function useOutlookTextStyle() {
|
|
8
|
+
return useContext(OutlookTextStyleContext);
|
|
9
|
+
}
|
|
10
|
+
export { OutlookTextStyleProvider, useOutlookTextStyle };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { HtmlText } from "../HtmlText.js";
|
|
3
|
+
type Story = StoryObj<typeof HtmlText>;
|
|
4
|
+
declare const config: Meta<typeof HtmlText>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const WithVariants: Story;
|
|
8
|
+
export declare const ResponsiveVariants: Story;
|
|
9
|
+
export declare const BottomSpacing: Story;
|
|
10
|
+
export declare const DefaultVariant: Story;
|
|
11
|
+
export declare const ElementPropAsDiv: Story;
|
|
12
|
+
export declare const ElementPropAsAnchor: Story;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn, MjmlRaw } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
4
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
5
|
+
import { HtmlText } from "../HtmlText.js";
|
|
6
|
+
const config = {
|
|
7
|
+
title: "Components/HtmlText",
|
|
8
|
+
component: HtmlText,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
};
|
|
11
|
+
export default config;
|
|
12
|
+
export const Default = {
|
|
13
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx("table", { children: _jsx("tr", { children: _jsx(HtmlText, { children: "Default text with base theme styles" }) }) }) }) }) })),
|
|
14
|
+
};
|
|
15
|
+
export const WithVariants = {
|
|
16
|
+
parameters: {
|
|
17
|
+
theme: createTheme({
|
|
18
|
+
text: {
|
|
19
|
+
variants: {
|
|
20
|
+
heading: { fontSize: "32px", fontWeight: 700, lineHeight: "40px" },
|
|
21
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
22
|
+
caption: { fontSize: "12px", lineHeight: "16px", color: "#666666" },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsxs("table", { children: [_jsx("tr", { children: _jsx(HtmlText, { variant: "heading", children: "Heading variant" }) }), _jsx("tr", { children: _jsx(HtmlText, { variant: "body", children: "Body variant" }) }), _jsx("tr", { children: _jsx(HtmlText, { variant: "caption", children: "Caption variant" }) })] }) }) }) })),
|
|
28
|
+
};
|
|
29
|
+
export const ResponsiveVariants = {
|
|
30
|
+
parameters: {
|
|
31
|
+
theme: createTheme({
|
|
32
|
+
text: {
|
|
33
|
+
variants: {
|
|
34
|
+
heading: {
|
|
35
|
+
fontSize: { default: "32px", mobile: "24px" },
|
|
36
|
+
lineHeight: { default: "40px", mobile: "30px" },
|
|
37
|
+
fontWeight: 700,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx("table", { children: _jsx("tr", { children: _jsx(HtmlText, { variant: "heading", children: "Responsive heading \u2014 shrinks on mobile" }) }) }) }) }) })),
|
|
44
|
+
};
|
|
45
|
+
export const BottomSpacing = {
|
|
46
|
+
parameters: {
|
|
47
|
+
theme: createTheme({
|
|
48
|
+
text: {
|
|
49
|
+
bottomSpacing: "20px",
|
|
50
|
+
variants: {
|
|
51
|
+
heading: { fontSize: "32px", fontWeight: 700, bottomSpacing: { default: "24px", mobile: "16px" } },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsxs("table", { children: [_jsx("tr", { children: _jsx(HtmlText, { variant: "heading", bottomSpacing: true, children: "Heading with bottom spacing" }) }), _jsx("tr", { children: _jsx(HtmlText, { bottomSpacing: true, children: "Base text with bottom spacing" }) }), _jsx("tr", { children: _jsx(HtmlText, { children: "Text without bottom spacing" }) })] }) }) }) })),
|
|
57
|
+
};
|
|
58
|
+
export const DefaultVariant = {
|
|
59
|
+
parameters: {
|
|
60
|
+
theme: createTheme({
|
|
61
|
+
text: {
|
|
62
|
+
defaultVariant: "body",
|
|
63
|
+
variants: {
|
|
64
|
+
body: { fontSize: "14px", lineHeight: "22px" },
|
|
65
|
+
heading: { fontSize: "28px", fontWeight: 700 },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsxs("table", { children: [_jsx("tr", { children: _jsx(HtmlText, { children: "Uses the default \"body\" variant automatically" }) }), _jsx("tr", { children: _jsx(HtmlText, { variant: "heading", children: "Explicit heading variant" }) })] }) }) }) })),
|
|
71
|
+
};
|
|
72
|
+
export const ElementPropAsDiv = {
|
|
73
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx(HtmlText, { element: "div", children: "Rendered as a div with theme styles" }) }) }) })),
|
|
74
|
+
};
|
|
75
|
+
export const ElementPropAsAnchor = {
|
|
76
|
+
render: () => (_jsx(MjmlSection, { children: _jsx(MjmlColumn, { children: _jsx(MjmlRaw, { children: _jsx(HtmlText, { element: "a", href: "https://example.com", style: { textDecoration: "underline" }, children: "Rendered as an anchor with href" }) }) }) })),
|
|
77
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { MjmlText } from "../MjmlText.js";
|
|
3
|
+
type Story = StoryObj<typeof MjmlText>;
|
|
4
|
+
declare const config: Meta<typeof MjmlText>;
|
|
5
|
+
export default config;
|
|
6
|
+
export declare const Default: Story;
|
|
7
|
+
export declare const WithVariants: Story;
|
|
8
|
+
export declare const ResponsiveVariants: Story;
|
|
9
|
+
export declare const BottomSpacing: Story;
|
|
10
|
+
export declare const DefaultVariant: Story;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { MjmlColumn } from "@faire/mjml-react";
|
|
3
|
+
import { createTheme } from "../../../theme/createTheme.js";
|
|
4
|
+
import { MjmlSection } from "../../section/MjmlSection.js";
|
|
5
|
+
import { MjmlText } from "../MjmlText.js";
|
|
6
|
+
const config = {
|
|
7
|
+
title: "Components/MjmlText",
|
|
8
|
+
component: MjmlText,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
};
|
|
11
|
+
export default config;
|
|
12
|
+
export const Default = {
|
|
13
|
+
render: () => (_jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { children: "Default text with base theme styles" }) }) })),
|
|
14
|
+
};
|
|
15
|
+
export const WithVariants = {
|
|
16
|
+
parameters: {
|
|
17
|
+
theme: createTheme({
|
|
18
|
+
text: {
|
|
19
|
+
variants: {
|
|
20
|
+
heading: { fontSize: "32px", fontWeight: 700, lineHeight: "40px" },
|
|
21
|
+
body: { fontSize: "16px", lineHeight: "24px" },
|
|
22
|
+
caption: { fontSize: "12px", lineHeight: "16px", color: "#666666" },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
render: () => (_jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlText, { variant: "heading", children: "Heading variant" }), _jsx(MjmlText, { variant: "body", children: "Body variant" }), _jsx(MjmlText, { variant: "caption", children: "Caption variant" })] }) })),
|
|
28
|
+
};
|
|
29
|
+
export const ResponsiveVariants = {
|
|
30
|
+
parameters: {
|
|
31
|
+
theme: createTheme({
|
|
32
|
+
text: {
|
|
33
|
+
variants: {
|
|
34
|
+
heading: {
|
|
35
|
+
fontSize: { default: "32px", mobile: "24px" },
|
|
36
|
+
lineHeight: { default: "40px", mobile: "30px" },
|
|
37
|
+
fontWeight: 700,
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
render: () => (_jsx(MjmlSection, { indent: true, children: _jsx(MjmlColumn, { children: _jsx(MjmlText, { variant: "heading", children: "Responsive heading \u2014 shrinks on mobile" }) }) })),
|
|
44
|
+
};
|
|
45
|
+
export const BottomSpacing = {
|
|
46
|
+
parameters: {
|
|
47
|
+
theme: createTheme({
|
|
48
|
+
text: {
|
|
49
|
+
bottomSpacing: "20px",
|
|
50
|
+
variants: {
|
|
51
|
+
heading: { fontSize: "32px", fontWeight: 700, bottomSpacing: { default: "24px", mobile: "16px" } },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}),
|
|
55
|
+
},
|
|
56
|
+
render: () => (_jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlText, { variant: "heading", bottomSpacing: true, children: "Heading with bottom spacing" }), _jsx(MjmlText, { bottomSpacing: true, children: "Base text with bottom spacing" }), _jsx(MjmlText, { children: "Text without bottom spacing" })] }) })),
|
|
57
|
+
};
|
|
58
|
+
export const DefaultVariant = {
|
|
59
|
+
parameters: {
|
|
60
|
+
theme: createTheme({
|
|
61
|
+
text: {
|
|
62
|
+
defaultVariant: "body",
|
|
63
|
+
variants: {
|
|
64
|
+
body: { fontSize: "14px", lineHeight: "22px" },
|
|
65
|
+
heading: { fontSize: "28px", fontWeight: 700 },
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
render: () => (_jsx(MjmlSection, { indent: true, children: _jsxs(MjmlColumn, { children: [_jsx(MjmlText, { children: "Uses the default \"body\" variant automatically" }), _jsx(MjmlText, { variant: "heading", children: "Explicit heading variant" })] }) })),
|
|
71
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|