@comet/mail-react 9.0.0-beta.1 → 9.0.0-beta.2
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/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__/examples/TextWithImageEmail.stories.d.ts +5 -0
- package/lib/__stories__/examples/TextWithImageEmail.stories.js +56 -0
- 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 +1 -1
- package/lib/components/section/MjmlSection.js +3 -2
- 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 +69 -0
- package/lib/index.d.ts +8 -2
- package/lib/index.js +4 -1
- package/lib/storybook/MailRendererDecorator.d.ts +7 -1
- package/lib/storybook/MailRendererDecorator.js +2 -2
- 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 +10 -0
- package/lib/theme/responsiveValue.test.js +18 -1
- package/lib/theme/themeTypes.d.ts +59 -0
- package/package.json +7 -4
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { createTheme } from "./createTheme.js";
|
|
3
3
|
import { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./responsiveValue.js";
|
|
4
|
+
describe("createTheme text", () => {
|
|
5
|
+
it("includes default text values when no overrides given", () => {
|
|
6
|
+
const theme = createTheme();
|
|
7
|
+
expect(theme.text.fontFamily).toBe("Arial, sans-serif");
|
|
8
|
+
expect(theme.text.fontSize).toBe("16px");
|
|
9
|
+
expect(theme.text.lineHeight).toBe("20px");
|
|
10
|
+
expect(theme.text.bottomSpacing).toBe("16px");
|
|
11
|
+
});
|
|
12
|
+
it("shallow-merges text overrides with defaults", () => {
|
|
13
|
+
const theme = createTheme({ text: { fontFamily: "Georgia, serif", fontSize: "18px" } });
|
|
14
|
+
expect(theme.text.fontFamily).toBe("Georgia, serif");
|
|
15
|
+
expect(theme.text.fontSize).toBe("18px");
|
|
16
|
+
expect(theme.text.lineHeight).toBe("20px");
|
|
17
|
+
expect(theme.text.bottomSpacing).toBe("16px");
|
|
18
|
+
});
|
|
19
|
+
it("passes through variant definitions", () => {
|
|
20
|
+
const theme = createTheme({
|
|
21
|
+
text: {
|
|
22
|
+
variants: {
|
|
23
|
+
heading: { fontSize: { default: "32px", mobile: "24px" } },
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
expect(theme.text.variants).toBeDefined();
|
|
28
|
+
expect(theme.text.variants?.heading).toEqual({ fontSize: { default: "32px", mobile: "24px" } });
|
|
29
|
+
});
|
|
30
|
+
it("does not set defaultVariant or variants by default", () => {
|
|
31
|
+
const theme = createTheme();
|
|
32
|
+
expect(theme.text.defaultVariant).toBeUndefined();
|
|
33
|
+
expect(theme.text.variants).toBeUndefined();
|
|
34
|
+
});
|
|
35
|
+
});
|
|
4
36
|
describe("createTheme contentIndentation", () => {
|
|
5
37
|
it("has default contentIndentation with responsive mobile value", () => {
|
|
6
38
|
const theme = createTheme();
|
|
@@ -8,4 +8,16 @@ export const defaultTheme = {
|
|
|
8
8
|
default: createBreakpoint(600),
|
|
9
9
|
mobile: createBreakpoint(420),
|
|
10
10
|
},
|
|
11
|
+
text: {
|
|
12
|
+
fontFamily: "Arial, sans-serif",
|
|
13
|
+
fontSize: "16px",
|
|
14
|
+
lineHeight: "20px",
|
|
15
|
+
bottomSpacing: "16px",
|
|
16
|
+
},
|
|
17
|
+
colors: {
|
|
18
|
+
background: {
|
|
19
|
+
body: "#F2F2F2",
|
|
20
|
+
content: "#FFFFFF",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
11
23
|
};
|
|
@@ -14,6 +14,12 @@ export type ResponsiveValue<T = number> = T | (Partial<Record<keyof ThemeBreakpo
|
|
|
14
14
|
* For a plain value, returns it directly. For an object, returns the `default` property.
|
|
15
15
|
*/
|
|
16
16
|
export declare function getDefaultFromResponsiveValue<T = number>(value: ResponsiveValue<T>): T;
|
|
17
|
+
/**
|
|
18
|
+
* Returns the default value from a `ResponsiveValue`, or `undefined` if the
|
|
19
|
+
* input is `undefined`. Useful for extracting inline prop values from optional
|
|
20
|
+
* theme properties.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getDefaultOrUndefined<T>(value: ResponsiveValue<T> | undefined): T | undefined;
|
|
17
23
|
/**
|
|
18
24
|
* Returns the non-default breakpoint overrides from a `ResponsiveValue`.
|
|
19
25
|
* For a plain value, returns an empty array. For an object, returns one
|
|
@@ -8,6 +8,16 @@ function isResponsiveObject(value) {
|
|
|
8
8
|
export function getDefaultFromResponsiveValue(value) {
|
|
9
9
|
return isResponsiveObject(value) ? value.default : value;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* Returns the default value from a `ResponsiveValue`, or `undefined` if the
|
|
13
|
+
* input is `undefined`. Useful for extracting inline prop values from optional
|
|
14
|
+
* theme properties.
|
|
15
|
+
*/
|
|
16
|
+
export function getDefaultOrUndefined(value) {
|
|
17
|
+
if (value === undefined)
|
|
18
|
+
return undefined;
|
|
19
|
+
return getDefaultFromResponsiveValue(value);
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Returns the non-default breakpoint overrides from a `ResponsiveValue`.
|
|
13
23
|
* For a plain value, returns an empty array. For an object, returns one
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { getDefaultFromResponsiveValue, getResponsiveOverrides } from "./responsiveValue.js";
|
|
2
|
+
import { getDefaultFromResponsiveValue, getDefaultOrUndefined, getResponsiveOverrides } from "./responsiveValue.js";
|
|
3
3
|
describe("getDefaultFromResponsiveValue", () => {
|
|
4
4
|
it("returns the value when given a plain number", () => {
|
|
5
5
|
expect(getDefaultFromResponsiveValue(20)).toBe(20);
|
|
@@ -43,3 +43,20 @@ describe("getResponsiveOverrides", () => {
|
|
|
43
43
|
expect(result).toEqual([{ breakpointKey: "mobile", value: "20px" }]);
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
|
+
describe("getDefaultOrUndefined", () => {
|
|
47
|
+
it("returns undefined when given undefined", () => {
|
|
48
|
+
expect(getDefaultOrUndefined(undefined)).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
it("returns the value when given a plain value", () => {
|
|
51
|
+
expect(getDefaultOrUndefined(20)).toBe(20);
|
|
52
|
+
});
|
|
53
|
+
it("returns the default property from an object", () => {
|
|
54
|
+
expect(getDefaultOrUndefined({ default: 20, mobile: 10 })).toBe(20);
|
|
55
|
+
});
|
|
56
|
+
it("works with string type", () => {
|
|
57
|
+
expect(getDefaultOrUndefined("24px")).toBe("24px");
|
|
58
|
+
});
|
|
59
|
+
it("returns default from a string responsive object", () => {
|
|
60
|
+
expect(getDefaultOrUndefined({ default: "32px", mobile: "24px" })).toBe("32px");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1,4 +1,51 @@
|
|
|
1
1
|
import type { ResponsiveValue } from "./responsiveValue.js";
|
|
2
|
+
/**
|
|
3
|
+
* Single source of truth for text style property names and their value types.
|
|
4
|
+
* Both `TextStyles` and `TextVariantStyles` are derived from this interface.
|
|
5
|
+
*/
|
|
6
|
+
interface TextStyleMap {
|
|
7
|
+
fontFamily: string;
|
|
8
|
+
fontSize: string;
|
|
9
|
+
fontWeight: string | number;
|
|
10
|
+
fontStyle: string;
|
|
11
|
+
lineHeight: string | number;
|
|
12
|
+
letterSpacing: string;
|
|
13
|
+
textDecoration: string;
|
|
14
|
+
textTransform: string;
|
|
15
|
+
color: string;
|
|
16
|
+
bottomSpacing: string;
|
|
17
|
+
}
|
|
18
|
+
/** Base text styles where each property holds a plain value. */
|
|
19
|
+
export type TextStyles = {
|
|
20
|
+
[K in keyof TextStyleMap]?: TextStyleMap[K];
|
|
21
|
+
};
|
|
22
|
+
/** Variant text styles where each property supports responsive values. */
|
|
23
|
+
export type TextVariantStyles = {
|
|
24
|
+
[K in keyof TextStyleMap]?: ResponsiveValue<TextStyleMap[K]>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Defines the variants available on the `MjmlText` component.
|
|
28
|
+
*
|
|
29
|
+
* ```ts
|
|
30
|
+
* declare module "@comet/mail-react" {
|
|
31
|
+
* interface TextVariants {
|
|
32
|
+
* heading: true;
|
|
33
|
+
* body: true;
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export interface TextVariants {
|
|
39
|
+
}
|
|
40
|
+
type VariantsRecord = keyof TextVariants extends never ? Record<string, TextVariantStyles> : {
|
|
41
|
+
[K in keyof TextVariants]?: TextVariantStyles;
|
|
42
|
+
};
|
|
43
|
+
export type VariantName = keyof TextVariants extends never ? string : keyof TextVariants;
|
|
44
|
+
/** Theme configuration for text styles, variants, and default variant. */
|
|
45
|
+
export interface ThemeText extends TextStyles {
|
|
46
|
+
defaultVariant?: VariantName;
|
|
47
|
+
variants?: VariantsRecord;
|
|
48
|
+
}
|
|
2
49
|
/**
|
|
3
50
|
* A resolved breakpoint with its numeric value and a ready-to-use media query
|
|
4
51
|
* string that targets viewports narrower than this breakpoint.
|
|
@@ -35,6 +82,15 @@ export interface ThemeSizes {
|
|
|
35
82
|
/** Content indentation (left/right padding) in pixels, supporting per-breakpoint values. */
|
|
36
83
|
contentIndentation: ResponsiveValue;
|
|
37
84
|
}
|
|
85
|
+
/** Background color tokens for usage through the email layout. */
|
|
86
|
+
export interface ThemeBackgroundColors {
|
|
87
|
+
body: string;
|
|
88
|
+
content: string;
|
|
89
|
+
}
|
|
90
|
+
/** Color tokens for usage through the email layout. */
|
|
91
|
+
export interface ThemeColors {
|
|
92
|
+
background: ThemeBackgroundColors;
|
|
93
|
+
}
|
|
38
94
|
/**
|
|
39
95
|
* The root theme object that holds all design tokens for `@comet/mail-react`.
|
|
40
96
|
*
|
|
@@ -44,4 +100,7 @@ export interface ThemeSizes {
|
|
|
44
100
|
export interface Theme {
|
|
45
101
|
sizes: ThemeSizes;
|
|
46
102
|
breakpoints: ThemeBreakpoints;
|
|
103
|
+
text: ThemeText;
|
|
104
|
+
colors: ThemeColors;
|
|
47
105
|
}
|
|
106
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comet/mail-react",
|
|
3
|
-
"version": "9.0.0-beta.
|
|
3
|
+
"version": "9.0.0-beta.2",
|
|
4
4
|
"description": "Utilities for building HTML emails with React",
|
|
5
5
|
"license": "BSD-2-Clause",
|
|
6
6
|
"type": "module",
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"storybook": "^10.3.1",
|
|
38
38
|
"typescript": "^5.9.3",
|
|
39
39
|
"vitest": "^4.0.16",
|
|
40
|
-
"@comet/cli": "9.0.0-beta.
|
|
41
|
-
"@comet/eslint-config": "9.0.0-beta.
|
|
40
|
+
"@comet/cli": "9.0.0-beta.2",
|
|
41
|
+
"@comet/eslint-config": "9.0.0-beta.2"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
44
|
+
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
45
45
|
"storybook": ">=10.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependenciesMeta": {
|
|
@@ -66,6 +66,9 @@
|
|
|
66
66
|
"lint": "pnpm generate-block-types && run-p lint:prettier lint:eslint lint:tsc",
|
|
67
67
|
"lint:ci": "pnpm run lint",
|
|
68
68
|
"lint:eslint": "eslint --max-warnings 0 src/ '**/*.json' --no-warn-ignored",
|
|
69
|
+
"lint:fix": "run-p lint:fix:eslint lint:fix:prettier",
|
|
70
|
+
"lint:fix:eslint": "pnpm run lint:eslint --fix",
|
|
71
|
+
"lint:fix:prettier": "pnpm run lint:prettier --write",
|
|
69
72
|
"lint:prettier": "pnpm exec prettier --check '*.{ts,js,json,md,yml,yaml}'",
|
|
70
73
|
"lint:tsc": "tsc",
|
|
71
74
|
"openspec:install-skills": "openspec init --tools github-copilot,cursor,claude",
|