@factorialco/f0-react-native 0.26.0 → 0.28.0
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 +2 -2
- package/lib/module/components/exports.js +1 -1
- package/lib/module/components/exports.js.map +1 -1
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.js +2 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.js.map +1 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.md +159 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.js +2 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.js.map +1 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/index.js +2 -0
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/index.js.map +1 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.js +2 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.js.map +1 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.md +320 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.styles.js +2 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.styles.js.map +1 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.types.js +2 -0
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.types.js.map +1 -0
- package/lib/module/components/primitives/F0Text/F0Text/index.js +2 -0
- package/lib/module/components/primitives/F0Text/F0Text/index.js.map +1 -0
- package/lib/module/components/primitives/F0Text/index.js +2 -0
- package/lib/module/components/primitives/F0Text/index.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/lib/utils.js +1 -1
- package/lib/module/lib/utils.js.map +1 -1
- package/lib/module/styles/theme.css +7 -0
- package/lib/typescript/components/exports.d.ts +1 -0
- package/lib/typescript/components/exports.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.d.ts +5 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.d.ts +45 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/index.d.ts +8 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/index.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.d.ts +8 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.styles.d.ts +144 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.styles.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.types.d.ts +86 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.types.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/index.d.ts +9 -0
- package/lib/typescript/components/primitives/F0Text/F0Text/index.d.ts.map +1 -0
- package/lib/typescript/components/primitives/F0Text/index.d.ts +12 -0
- package/lib/typescript/components/primitives/F0Text/index.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/lib/utils.d.ts +9 -0
- package/lib/typescript/lib/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/exports.ts +3 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.md +159 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.tsx +72 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.ts +61 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/__tests__/AnimatedF0Text.spec.tsx +131 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/__tests__/__snapshots__/AnimatedF0Text.spec.tsx.snap +144 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/index.ts +8 -0
- package/src/components/primitives/F0Text/F0Text/F0Text.md +320 -0
- package/src/components/primitives/F0Text/F0Text/F0Text.styles.ts +71 -0
- package/src/components/primitives/F0Text/F0Text/F0Text.tsx +76 -0
- package/src/components/primitives/F0Text/F0Text/F0Text.types.ts +134 -0
- package/src/components/primitives/F0Text/F0Text/__tests__/F0Text.spec.tsx +228 -0
- package/src/components/primitives/F0Text/F0Text/__tests__/__snapshots__/F0Text.spec.tsx.snap +325 -0
- package/src/components/primitives/F0Text/F0Text/index.ts +22 -0
- package/src/components/primitives/F0Text/index.ts +26 -0
- package/src/index.ts +1 -1
- package/src/lib/__tests__/utils.spec.ts +48 -0
- package/src/lib/utils.ts +19 -0
- package/src/styles/theme.css +7 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { tv, type VariantProps } from "tailwind-variants"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Text component variants using tailwind-variants
|
|
5
|
+
* Font weights (font-normal, font-medium, font-semibold) map to
|
|
6
|
+
* Inter font families (Inter-Regular, Inter-Medium, Inter-SemiBold)
|
|
7
|
+
*/
|
|
8
|
+
export const textVariants = tv({
|
|
9
|
+
base: "",
|
|
10
|
+
variants: {
|
|
11
|
+
variant: {
|
|
12
|
+
// Heading variants
|
|
13
|
+
"heading-xl":
|
|
14
|
+
"text-[36px] leading-[40px] tracking-[-0.2px] font-semibold",
|
|
15
|
+
"heading-lg":
|
|
16
|
+
"text-[24px] leading-[32px] tracking-[-0.2px] font-semibold",
|
|
17
|
+
"heading-md":
|
|
18
|
+
"text-[20px] leading-[28px] tracking-[-0.2px] font-semibold",
|
|
19
|
+
"heading-sm": "text-[16px] leading-[24px] font-semibold",
|
|
20
|
+
|
|
21
|
+
// Body variants with weight included in variant name
|
|
22
|
+
"body-md-default": "text-[16px] leading-[24px] font-normal",
|
|
23
|
+
"body-md-medium": "text-[16px] leading-[24px] font-medium",
|
|
24
|
+
"body-md-semibold": "text-[16px] leading-[24px] font-semibold",
|
|
25
|
+
"body-sm-default": "text-[14px] leading-[20px] font-normal",
|
|
26
|
+
"body-sm-medium": "text-[14px] leading-[20px] font-medium",
|
|
27
|
+
"body-sm-semibold": "text-[14px] leading-[20px] font-semibold",
|
|
28
|
+
"body-xs-medium": "text-[12px] leading-[16px] font-medium",
|
|
29
|
+
},
|
|
30
|
+
color: {
|
|
31
|
+
default: "text-f0-foreground",
|
|
32
|
+
secondary: "text-f0-foreground-secondary",
|
|
33
|
+
tertiary: "text-f0-foreground-tertiary",
|
|
34
|
+
inverse: "text-f0-foreground-inverse",
|
|
35
|
+
"inverse-secondary": "text-f0-foreground-inverse-secondary",
|
|
36
|
+
disabled: "text-f0-foreground-disabled",
|
|
37
|
+
accent: "text-f0-foreground-accent",
|
|
38
|
+
critical: "text-f0-foreground-critical",
|
|
39
|
+
info: "text-f0-foreground-info",
|
|
40
|
+
warning: "text-f0-foreground-warning",
|
|
41
|
+
positive: "text-f0-foreground-positive",
|
|
42
|
+
selected: "text-f0-foreground-selected",
|
|
43
|
+
},
|
|
44
|
+
align: {
|
|
45
|
+
left: "text-left",
|
|
46
|
+
center: "text-center",
|
|
47
|
+
right: "text-right",
|
|
48
|
+
justify: "text-justify",
|
|
49
|
+
},
|
|
50
|
+
decoration: {
|
|
51
|
+
none: "",
|
|
52
|
+
underline: "underline",
|
|
53
|
+
"line-through": "line-through",
|
|
54
|
+
},
|
|
55
|
+
transform: {
|
|
56
|
+
none: "",
|
|
57
|
+
uppercase: "uppercase",
|
|
58
|
+
lowercase: "lowercase",
|
|
59
|
+
capitalize: "capitalize",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
defaultVariants: {
|
|
63
|
+
variant: "body-sm-default",
|
|
64
|
+
color: "default",
|
|
65
|
+
align: "left",
|
|
66
|
+
decoration: "none",
|
|
67
|
+
transform: "none",
|
|
68
|
+
},
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export type TextVariants = VariantProps<typeof textVariants>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { Text as RNText } from "react-native"
|
|
3
|
+
|
|
4
|
+
import { omitProps } from "../../../../lib/utils"
|
|
5
|
+
|
|
6
|
+
import { textVariants } from "./F0Text.styles"
|
|
7
|
+
import { F0_TEXT_BANNED_PROPS, type F0TextProps } from "./F0Text.types"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* F0Text - Primitive Text component with semantic typography variants
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* <F0Text variant="heading-lg">Large Heading</F0Text>
|
|
14
|
+
* <F0Text variant="body-sm-default" color="secondary">Secondary text</F0Text>
|
|
15
|
+
* <F0Text variant="body-md-medium" numberOfLines={2}>Truncated text...</F0Text>
|
|
16
|
+
*/
|
|
17
|
+
const F0TextComponent = React.forwardRef<RNText, F0TextProps>(
|
|
18
|
+
(
|
|
19
|
+
{
|
|
20
|
+
variant = "body-sm-default",
|
|
21
|
+
color = "default",
|
|
22
|
+
align = "left",
|
|
23
|
+
decoration = "none",
|
|
24
|
+
transform = "none",
|
|
25
|
+
children,
|
|
26
|
+
numberOfLines,
|
|
27
|
+
...rest
|
|
28
|
+
},
|
|
29
|
+
ref
|
|
30
|
+
) => {
|
|
31
|
+
const textClassName = React.useMemo(
|
|
32
|
+
() =>
|
|
33
|
+
textVariants({
|
|
34
|
+
variant,
|
|
35
|
+
color,
|
|
36
|
+
align,
|
|
37
|
+
decoration,
|
|
38
|
+
transform,
|
|
39
|
+
}),
|
|
40
|
+
[variant, color, align, decoration, transform]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<RNText
|
|
45
|
+
ref={ref}
|
|
46
|
+
{...omitProps(rest, F0_TEXT_BANNED_PROPS)}
|
|
47
|
+
className={textClassName}
|
|
48
|
+
numberOfLines={numberOfLines}
|
|
49
|
+
ellipsizeMode={numberOfLines ? "tail" : undefined}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
</RNText>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
F0TextComponent.displayName = "F0Text"
|
|
58
|
+
|
|
59
|
+
export const F0Text = React.memo(F0TextComponent)
|
|
60
|
+
|
|
61
|
+
// Export types
|
|
62
|
+
export type { F0TextProps }
|
|
63
|
+
export {
|
|
64
|
+
TYPOGRAPHY_VARIANTS,
|
|
65
|
+
TEXT_COLORS,
|
|
66
|
+
TEXT_ALIGN,
|
|
67
|
+
TEXT_DECORATIONS,
|
|
68
|
+
TEXT_TRANSFORMS,
|
|
69
|
+
} from "./F0Text.types"
|
|
70
|
+
export type {
|
|
71
|
+
TypographyVariant,
|
|
72
|
+
TextColor,
|
|
73
|
+
TextAlign,
|
|
74
|
+
TextDecoration,
|
|
75
|
+
TextTransform,
|
|
76
|
+
} from "./F0Text.types"
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { TextProps as RNTextProps } from "react-native"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Props that must not be passed through to the underlying RN Text
|
|
5
|
+
* (`style` and `className` are handled by F0 instead).
|
|
6
|
+
* Used with omitProps for runtime safety.
|
|
7
|
+
*/
|
|
8
|
+
export const F0_TEXT_BANNED_PROPS = ["style", "className"] as const
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Typography variant types based on semantic design tokens
|
|
12
|
+
*/
|
|
13
|
+
export const TYPOGRAPHY_VARIANTS = [
|
|
14
|
+
"heading-xl",
|
|
15
|
+
"heading-lg",
|
|
16
|
+
"heading-md",
|
|
17
|
+
"heading-sm",
|
|
18
|
+
"body-md-default",
|
|
19
|
+
"body-md-medium",
|
|
20
|
+
"body-md-semibold",
|
|
21
|
+
"body-sm-default",
|
|
22
|
+
"body-sm-medium",
|
|
23
|
+
"body-sm-semibold",
|
|
24
|
+
"body-xs-medium",
|
|
25
|
+
] as const
|
|
26
|
+
|
|
27
|
+
export type TypographyVariant = (typeof TYPOGRAPHY_VARIANTS)[number]
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Text color variants aligned with F0 semantic color system
|
|
31
|
+
*/
|
|
32
|
+
export const TEXT_COLORS = [
|
|
33
|
+
"default",
|
|
34
|
+
"secondary",
|
|
35
|
+
"tertiary",
|
|
36
|
+
"inverse",
|
|
37
|
+
"inverse-secondary",
|
|
38
|
+
"disabled",
|
|
39
|
+
"accent",
|
|
40
|
+
"critical",
|
|
41
|
+
"info",
|
|
42
|
+
"warning",
|
|
43
|
+
"positive",
|
|
44
|
+
"selected",
|
|
45
|
+
] as const
|
|
46
|
+
|
|
47
|
+
export type TextColor = (typeof TEXT_COLORS)[number]
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Text alignment options
|
|
51
|
+
*/
|
|
52
|
+
export const TEXT_ALIGN = ["left", "center", "right", "justify"] as const
|
|
53
|
+
|
|
54
|
+
export type TextAlign = (typeof TEXT_ALIGN)[number]
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Text decoration options
|
|
58
|
+
*/
|
|
59
|
+
export const TEXT_DECORATIONS = ["none", "underline", "line-through"] as const
|
|
60
|
+
|
|
61
|
+
export type TextDecoration = (typeof TEXT_DECORATIONS)[number]
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Text transform options
|
|
65
|
+
*/
|
|
66
|
+
export const TEXT_TRANSFORMS = [
|
|
67
|
+
"none",
|
|
68
|
+
"uppercase",
|
|
69
|
+
"lowercase",
|
|
70
|
+
"capitalize",
|
|
71
|
+
] as const
|
|
72
|
+
|
|
73
|
+
export type TextTransform = (typeof TEXT_TRANSFORMS)[number]
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Internal props for the F0Text component.
|
|
77
|
+
* @private
|
|
78
|
+
*/
|
|
79
|
+
interface F0TextPropsInternal extends Omit<RNTextProps, "style"> {
|
|
80
|
+
/**
|
|
81
|
+
* Semantic typography variant
|
|
82
|
+
* @default "body-sm-default"
|
|
83
|
+
*/
|
|
84
|
+
variant?: TypographyVariant
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Text color from F0 semantic color system
|
|
88
|
+
* @default "default"
|
|
89
|
+
*/
|
|
90
|
+
color?: TextColor
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Text alignment
|
|
94
|
+
* @default "left"
|
|
95
|
+
*/
|
|
96
|
+
align?: TextAlign
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Text decoration
|
|
100
|
+
* @default "none"
|
|
101
|
+
*/
|
|
102
|
+
decoration?: TextDecoration
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Text transform
|
|
106
|
+
* @default "none"
|
|
107
|
+
*/
|
|
108
|
+
transform?: TextTransform
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Maximum number of lines before truncating with ellipsis
|
|
112
|
+
*/
|
|
113
|
+
numberOfLines?: number
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Children content
|
|
117
|
+
*/
|
|
118
|
+
children?: React.ReactNode
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Excluded from public API via Omit<F0TextPropsInternal, "className">.
|
|
122
|
+
* @private
|
|
123
|
+
*/
|
|
124
|
+
className?: string
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Public props for the F0Text component
|
|
129
|
+
*
|
|
130
|
+
* Note: `className` and `style` props are NOT available.
|
|
131
|
+
* Use semantic props (variant, color, align, etc.) for typography.
|
|
132
|
+
* For spacing/layout, wrap F0Text in a View with className.
|
|
133
|
+
*/
|
|
134
|
+
export type F0TextProps = Omit<F0TextPropsInternal, "className">
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { render } from "@testing-library/react-native"
|
|
2
|
+
import React from "react"
|
|
3
|
+
import { Text as RNText } from "react-native"
|
|
4
|
+
|
|
5
|
+
import { F0Text } from "../F0Text"
|
|
6
|
+
|
|
7
|
+
describe("F0Text", () => {
|
|
8
|
+
describe("Snapshots", () => {
|
|
9
|
+
it("renders with default variant (body-sm-default)", () => {
|
|
10
|
+
const { toJSON } = render(<F0Text>Default text</F0Text>)
|
|
11
|
+
expect(toJSON()).toMatchSnapshot()
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("renders all typography variants", () => {
|
|
15
|
+
const variants = [
|
|
16
|
+
"heading-xl",
|
|
17
|
+
"heading-lg",
|
|
18
|
+
"heading-md",
|
|
19
|
+
"heading-sm",
|
|
20
|
+
"body-md-default",
|
|
21
|
+
"body-md-medium",
|
|
22
|
+
"body-md-semibold",
|
|
23
|
+
"body-sm-default",
|
|
24
|
+
"body-sm-medium",
|
|
25
|
+
"body-sm-semibold",
|
|
26
|
+
"body-xs-medium",
|
|
27
|
+
] as const
|
|
28
|
+
|
|
29
|
+
variants.forEach((variant) => {
|
|
30
|
+
const { toJSON } = render(
|
|
31
|
+
<F0Text variant={variant}>{variant} text</F0Text>
|
|
32
|
+
)
|
|
33
|
+
expect(toJSON()).toMatchSnapshot(`variant-${variant}`)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it("renders all color variants", () => {
|
|
38
|
+
const colors = [
|
|
39
|
+
"default",
|
|
40
|
+
"secondary",
|
|
41
|
+
"tertiary",
|
|
42
|
+
"inverse",
|
|
43
|
+
"inverse-secondary",
|
|
44
|
+
"disabled",
|
|
45
|
+
"accent",
|
|
46
|
+
"critical",
|
|
47
|
+
"info",
|
|
48
|
+
"warning",
|
|
49
|
+
"positive",
|
|
50
|
+
"selected",
|
|
51
|
+
] as const
|
|
52
|
+
|
|
53
|
+
colors.forEach((color) => {
|
|
54
|
+
const { toJSON } = render(<F0Text color={color}>{color} text</F0Text>)
|
|
55
|
+
expect(toJSON()).toMatchSnapshot(`color-${color}`)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it("renders with alignment options", () => {
|
|
60
|
+
const alignments = ["left", "center", "right", "justify"] as const
|
|
61
|
+
|
|
62
|
+
alignments.forEach((align) => {
|
|
63
|
+
const { toJSON } = render(<F0Text align={align}>{align} text</F0Text>)
|
|
64
|
+
expect(toJSON()).toMatchSnapshot(`align-${align}`)
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it("renders with text decorations", () => {
|
|
69
|
+
const decorations = ["none", "underline", "line-through"] as const
|
|
70
|
+
|
|
71
|
+
decorations.forEach((decoration) => {
|
|
72
|
+
const { toJSON } = render(
|
|
73
|
+
<F0Text decoration={decoration}>{decoration} text</F0Text>
|
|
74
|
+
)
|
|
75
|
+
expect(toJSON()).toMatchSnapshot(`decoration-${decoration}`)
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("renders with text transforms", () => {
|
|
80
|
+
const transforms = [
|
|
81
|
+
"none",
|
|
82
|
+
"uppercase",
|
|
83
|
+
"lowercase",
|
|
84
|
+
"capitalize",
|
|
85
|
+
] as const
|
|
86
|
+
|
|
87
|
+
transforms.forEach((transform) => {
|
|
88
|
+
const { toJSON } = render(
|
|
89
|
+
<F0Text transform={transform}>{transform} text</F0Text>
|
|
90
|
+
)
|
|
91
|
+
expect(toJSON()).toMatchSnapshot(`transform-${transform}`)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it("renders with numberOfLines truncation", () => {
|
|
96
|
+
const { toJSON } = render(
|
|
97
|
+
<F0Text numberOfLines={2}>
|
|
98
|
+
This is a very long text that should be truncated after two lines with
|
|
99
|
+
an ellipsis at the end
|
|
100
|
+
</F0Text>
|
|
101
|
+
)
|
|
102
|
+
expect(toJSON()).toMatchSnapshot()
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
describe("Behavior", () => {
|
|
107
|
+
it("renders children correctly", () => {
|
|
108
|
+
const { getByText } = render(<F0Text>Hello World</F0Text>)
|
|
109
|
+
expect(getByText("Hello World")).toBeTruthy()
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it("applies correct variant classes", () => {
|
|
113
|
+
const { getByText } = render(
|
|
114
|
+
<F0Text variant="heading-lg">Large Heading</F0Text>
|
|
115
|
+
)
|
|
116
|
+
const element = getByText("Large Heading")
|
|
117
|
+
expect(element.props.className).toContain("text-[24px]")
|
|
118
|
+
expect(element.props.className).toContain("leading-[32px]")
|
|
119
|
+
expect(element.props.className).toContain("tracking-[-0.2px]")
|
|
120
|
+
expect(element.props.className).toContain("font-semibold")
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it("applies correct color classes", () => {
|
|
124
|
+
const { getByText } = render(
|
|
125
|
+
<F0Text color="secondary">Secondary text</F0Text>
|
|
126
|
+
)
|
|
127
|
+
const element = getByText("Secondary text")
|
|
128
|
+
expect(element.props.className).toContain("text-f0-foreground-secondary")
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
it("applies correct alignment classes", () => {
|
|
132
|
+
const { getByText } = render(
|
|
133
|
+
<F0Text align="center">Centered text</F0Text>
|
|
134
|
+
)
|
|
135
|
+
const element = getByText("Centered text")
|
|
136
|
+
expect(element.props.className).toContain("text-center")
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it("applies decoration classes", () => {
|
|
140
|
+
const { getByText } = render(
|
|
141
|
+
<F0Text decoration="underline">Underlined text</F0Text>
|
|
142
|
+
)
|
|
143
|
+
const element = getByText("Underlined text")
|
|
144
|
+
expect(element.props.className).toContain("underline")
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it("applies transform classes", () => {
|
|
148
|
+
const { getByText } = render(
|
|
149
|
+
<F0Text transform="uppercase">uppercase text</F0Text>
|
|
150
|
+
)
|
|
151
|
+
const element = getByText("uppercase text")
|
|
152
|
+
expect(element.props.className).toContain("uppercase")
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it("sets numberOfLines prop correctly", () => {
|
|
156
|
+
const { getByText } = render(
|
|
157
|
+
<F0Text numberOfLines={3}>Multiline text</F0Text>
|
|
158
|
+
)
|
|
159
|
+
const element = getByText("Multiline text")
|
|
160
|
+
expect(element.props.numberOfLines).toBe(3)
|
|
161
|
+
expect(element.props.ellipsizeMode).toBe("tail")
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("does not set ellipsizeMode when numberOfLines is not provided", () => {
|
|
165
|
+
const { getByText } = render(<F0Text>Normal text</F0Text>)
|
|
166
|
+
const element = getByText("Normal text")
|
|
167
|
+
expect(element.props.ellipsizeMode).toBeUndefined()
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it("forwards additional props to React Native Text", () => {
|
|
171
|
+
const onPress = jest.fn()
|
|
172
|
+
const { getByText } = render(
|
|
173
|
+
<F0Text onPress={onPress} testID="test-text">
|
|
174
|
+
Pressable text
|
|
175
|
+
</F0Text>
|
|
176
|
+
)
|
|
177
|
+
const element = getByText("Pressable text")
|
|
178
|
+
expect(element.props.testID).toBe("test-text")
|
|
179
|
+
expect(element.props.onPress).toBe(onPress)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it("filters style prop at runtime (prevents override via spread)", () => {
|
|
183
|
+
const propsWithStyle = {
|
|
184
|
+
style: { color: "red" },
|
|
185
|
+
children: "Text with style attempt",
|
|
186
|
+
}
|
|
187
|
+
const { getByText } = render(
|
|
188
|
+
<F0Text {...(propsWithStyle as React.ComponentProps<typeof F0Text>)}>
|
|
189
|
+
Text with style attempt
|
|
190
|
+
</F0Text>
|
|
191
|
+
)
|
|
192
|
+
const element = getByText("Text with style attempt")
|
|
193
|
+
expect(element.props.style).toBeUndefined()
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it("filters className prop at runtime (prevents override via spread)", () => {
|
|
197
|
+
const propsWithClassName = {
|
|
198
|
+
className: "font-bold text-red-500",
|
|
199
|
+
children: "Text with className attempt",
|
|
200
|
+
}
|
|
201
|
+
const { getByText } = render(
|
|
202
|
+
<F0Text
|
|
203
|
+
{...(propsWithClassName as React.ComponentProps<typeof F0Text>)}
|
|
204
|
+
>
|
|
205
|
+
Text with className attempt
|
|
206
|
+
</F0Text>
|
|
207
|
+
)
|
|
208
|
+
const element = getByText("Text with className attempt")
|
|
209
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
210
|
+
expect(element.props.className).not.toContain("text-red-500")
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it("handles ref forwarding", () => {
|
|
214
|
+
const ref = React.createRef<RNText>()
|
|
215
|
+
render(<F0Text ref={ref}>Ref text</F0Text>)
|
|
216
|
+
expect(ref.current).toBeTruthy()
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
it("renders nested children", () => {
|
|
220
|
+
const { getByText } = render(
|
|
221
|
+
<F0Text>
|
|
222
|
+
Parent <F0Text variant="body-xs-medium">nested</F0Text> text
|
|
223
|
+
</F0Text>
|
|
224
|
+
)
|
|
225
|
+
expect(getByText("nested")).toBeTruthy()
|
|
226
|
+
})
|
|
227
|
+
})
|
|
228
|
+
})
|