@factorialco/f0-react-native 0.28.1 → 0.29.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/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.js +1 -1
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.js.map +1 -1
- package/lib/module/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.md +45 -8
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.js +1 -1
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.js.map +1 -1
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.md +42 -31
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.styles.js +1 -1
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.styles.js.map +1 -1
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.types.js +1 -1
- package/lib/module/components/primitives/F0Text/F0Text/F0Text.types.js.map +1 -1
- package/lib/module/lib/utils.js.map +1 -1
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.d.ts +14 -0
- package/lib/typescript/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.styles.d.ts +2 -2
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.styles.d.ts.map +1 -1
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.types.d.ts +22 -18
- package/lib/typescript/components/primitives/F0Text/F0Text/F0Text.types.d.ts.map +1 -1
- package/lib/typescript/lib/utils.d.ts +1 -2
- package/lib/typescript/lib/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.md +45 -8
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.tsx +20 -8
- package/src/components/primitives/F0Text/AnimatedF0Text/AnimatedF0Text.types.ts +15 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/__tests__/AnimatedF0Text.spec.tsx +220 -0
- package/src/components/primitives/F0Text/AnimatedF0Text/__tests__/__snapshots__/AnimatedF0Text.spec.tsx.snap +16 -16
- package/src/components/primitives/F0Text/F0Text/F0Text.md +42 -31
- package/src/components/primitives/F0Text/F0Text/F0Text.styles.ts +1 -1
- package/src/components/primitives/F0Text/F0Text/F0Text.tsx +17 -10
- package/src/components/primitives/F0Text/F0Text/F0Text.types.ts +22 -18
- package/src/components/primitives/F0Text/F0Text/__tests__/F0Text.spec.tsx +340 -16
- package/src/components/primitives/F0Text/F0Text/__tests__/__snapshots__/F0Text.spec.tsx.snap +36 -36
- package/src/lib/utils.ts +1 -2
|
@@ -58,4 +58,19 @@ export interface AnimatedF0TextProps extends Omit<
|
|
|
58
58
|
* Children content
|
|
59
59
|
*/
|
|
60
60
|
children?: React.ReactNode
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Tailwind classes for layout and positioning.
|
|
64
|
+
*
|
|
65
|
+
* Allowed: margin, padding, flex, position, width, height, opacity, z-index, etc.
|
|
66
|
+
* Ignored: font-size, font-weight, line-height, letter-spacing, color, text-align,
|
|
67
|
+
* text-decoration, text-transform — these are controlled by semantic props and
|
|
68
|
+
* always take precedence via twMerge.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* className="mt-4 flex-1"
|
|
72
|
+
* className="mb-2 self-center"
|
|
73
|
+
* className="absolute top-0 left-0"
|
|
74
|
+
*/
|
|
75
|
+
className?: string
|
|
61
76
|
}
|
|
@@ -128,4 +128,224 @@ describe("AnimatedF0Text", () => {
|
|
|
128
128
|
expect(element.props.testID).toBe("test-animated-text")
|
|
129
129
|
})
|
|
130
130
|
})
|
|
131
|
+
|
|
132
|
+
describe("className — layout classes pass through", () => {
|
|
133
|
+
it("applies margin classes", () => {
|
|
134
|
+
const { getByText } = render(
|
|
135
|
+
<AnimatedF0Text className="mx-3 mt-4 mb-2">Margin text</AnimatedF0Text>
|
|
136
|
+
)
|
|
137
|
+
const element = getByText("Margin text")
|
|
138
|
+
expect(element.props.className).toContain("mt-4")
|
|
139
|
+
expect(element.props.className).toContain("mb-2")
|
|
140
|
+
expect(element.props.className).toContain("mx-3")
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it("applies padding classes", () => {
|
|
144
|
+
const { getByText } = render(
|
|
145
|
+
<AnimatedF0Text className="p-4 px-2 py-1">Padded text</AnimatedF0Text>
|
|
146
|
+
)
|
|
147
|
+
const element = getByText("Padded text")
|
|
148
|
+
expect(element.props.className).toContain("p-4")
|
|
149
|
+
expect(element.props.className).toContain("px-2")
|
|
150
|
+
expect(element.props.className).toContain("py-1")
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it("applies flex classes", () => {
|
|
154
|
+
const { getByText } = render(
|
|
155
|
+
<AnimatedF0Text className="flex-1 flex-shrink-0">
|
|
156
|
+
Flex text
|
|
157
|
+
</AnimatedF0Text>
|
|
158
|
+
)
|
|
159
|
+
const element = getByText("Flex text")
|
|
160
|
+
expect(element.props.className).toContain("flex-1")
|
|
161
|
+
expect(element.props.className).toContain("flex-shrink-0")
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it("applies position classes", () => {
|
|
165
|
+
const { getByText } = render(
|
|
166
|
+
<AnimatedF0Text className="absolute top-0 left-0">
|
|
167
|
+
Positioned text
|
|
168
|
+
</AnimatedF0Text>
|
|
169
|
+
)
|
|
170
|
+
const element = getByText("Positioned text")
|
|
171
|
+
expect(element.props.className).toContain("absolute")
|
|
172
|
+
expect(element.props.className).toContain("top-0")
|
|
173
|
+
expect(element.props.className).toContain("left-0")
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
it("applies opacity classes", () => {
|
|
177
|
+
const { getByText } = render(
|
|
178
|
+
<AnimatedF0Text className="opacity-50">Faded text</AnimatedF0Text>
|
|
179
|
+
)
|
|
180
|
+
const element = getByText("Faded text")
|
|
181
|
+
expect(element.props.className).toContain("opacity-50")
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it("preserves layout classes with all variant combinations", () => {
|
|
185
|
+
const { getByText } = render(
|
|
186
|
+
<AnimatedF0Text
|
|
187
|
+
variant="heading-lg"
|
|
188
|
+
color="critical"
|
|
189
|
+
align="center"
|
|
190
|
+
decoration="underline"
|
|
191
|
+
transform="uppercase"
|
|
192
|
+
className="mt-4 flex-1 self-end"
|
|
193
|
+
>
|
|
194
|
+
Full combo
|
|
195
|
+
</AnimatedF0Text>
|
|
196
|
+
)
|
|
197
|
+
const element = getByText("Full combo")
|
|
198
|
+
expect(element.props.className).toContain("mt-4")
|
|
199
|
+
expect(element.props.className).toContain("flex-1")
|
|
200
|
+
expect(element.props.className).toContain("self-end")
|
|
201
|
+
expect(element.props.className).toContain("text-[24px]")
|
|
202
|
+
expect(element.props.className).toContain("font-semibold")
|
|
203
|
+
expect(element.props.className).toContain("text-f0-foreground-critical")
|
|
204
|
+
expect(element.props.className).toContain("text-center")
|
|
205
|
+
expect(element.props.className).toContain("underline")
|
|
206
|
+
expect(element.props.className).toContain("uppercase")
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
describe("className — typography overrides are rejected", () => {
|
|
211
|
+
it("rejects font-size override", () => {
|
|
212
|
+
const { getByText } = render(
|
|
213
|
+
<AnimatedF0Text variant="body-sm-default" className="text-xs">
|
|
214
|
+
Size override attempt
|
|
215
|
+
</AnimatedF0Text>
|
|
216
|
+
)
|
|
217
|
+
const element = getByText("Size override attempt")
|
|
218
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
219
|
+
expect(element.props.className).not.toContain("text-xs")
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
it("rejects font-weight override", () => {
|
|
223
|
+
const { getByText } = render(
|
|
224
|
+
<AnimatedF0Text variant="body-sm-default" className="font-bold">
|
|
225
|
+
Weight override attempt
|
|
226
|
+
</AnimatedF0Text>
|
|
227
|
+
)
|
|
228
|
+
const element = getByText("Weight override attempt")
|
|
229
|
+
expect(element.props.className).toContain("font-normal")
|
|
230
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
it("rejects line-height override", () => {
|
|
234
|
+
const { getByText } = render(
|
|
235
|
+
<AnimatedF0Text variant="body-sm-default" className="leading-10">
|
|
236
|
+
Leading override attempt
|
|
237
|
+
</AnimatedF0Text>
|
|
238
|
+
)
|
|
239
|
+
const element = getByText("Leading override attempt")
|
|
240
|
+
expect(element.props.className).toContain("leading-[20px]")
|
|
241
|
+
expect(element.props.className).not.toContain("leading-10")
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it("rejects text-color override", () => {
|
|
245
|
+
const { getByText } = render(
|
|
246
|
+
<AnimatedF0Text color="default" className="text-red-500">
|
|
247
|
+
Color override attempt
|
|
248
|
+
</AnimatedF0Text>
|
|
249
|
+
)
|
|
250
|
+
const element = getByText("Color override attempt")
|
|
251
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
252
|
+
expect(element.props.className).not.toContain("text-red-500")
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
it("rejects multiple typography overrides simultaneously", () => {
|
|
256
|
+
const { getByText } = render(
|
|
257
|
+
<AnimatedF0Text
|
|
258
|
+
variant="body-sm-default"
|
|
259
|
+
color="default"
|
|
260
|
+
className="text-xl leading-loose font-bold tracking-wider text-blue-500"
|
|
261
|
+
>
|
|
262
|
+
Multi override attempt
|
|
263
|
+
</AnimatedF0Text>
|
|
264
|
+
)
|
|
265
|
+
const element = getByText("Multi override attempt")
|
|
266
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
267
|
+
expect(element.props.className).toContain("font-normal")
|
|
268
|
+
expect(element.props.className).toContain("leading-[20px]")
|
|
269
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
270
|
+
expect(element.props.className).not.toContain("text-xl")
|
|
271
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
272
|
+
expect(element.props.className).not.toContain("leading-loose")
|
|
273
|
+
expect(element.props.className).not.toContain("text-blue-500")
|
|
274
|
+
expect(element.props.className).not.toContain("tracking-wider")
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe("className — mixed layout + typography attempts", () => {
|
|
279
|
+
it("keeps layout classes, rejects typography overrides", () => {
|
|
280
|
+
const { getByText } = render(
|
|
281
|
+
<AnimatedF0Text
|
|
282
|
+
variant="heading-md"
|
|
283
|
+
className="mt-4 flex-1 p-2 text-xl font-bold text-red-500"
|
|
284
|
+
>
|
|
285
|
+
Mixed attempt
|
|
286
|
+
</AnimatedF0Text>
|
|
287
|
+
)
|
|
288
|
+
const element = getByText("Mixed attempt")
|
|
289
|
+
expect(element.props.className).toContain("mt-4")
|
|
290
|
+
expect(element.props.className).toContain("p-2")
|
|
291
|
+
expect(element.props.className).toContain("flex-1")
|
|
292
|
+
expect(element.props.className).toContain("text-[20px]")
|
|
293
|
+
expect(element.props.className).toContain("font-semibold")
|
|
294
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
295
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
296
|
+
expect(element.props.className).not.toContain("text-red-500")
|
|
297
|
+
expect(element.props.className).not.toContain("text-xl")
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
it("works with both className and style simultaneously", () => {
|
|
301
|
+
const animatedStyle = { opacity: 0.5 }
|
|
302
|
+
const { getByText } = render(
|
|
303
|
+
<AnimatedF0Text
|
|
304
|
+
variant="heading-sm"
|
|
305
|
+
className="mb-2 flex-1"
|
|
306
|
+
style={animatedStyle}
|
|
307
|
+
>
|
|
308
|
+
className + style
|
|
309
|
+
</AnimatedF0Text>
|
|
310
|
+
)
|
|
311
|
+
const element = getByText("className + style")
|
|
312
|
+
expect(element.props.className).toContain("mb-2")
|
|
313
|
+
expect(element.props.className).toContain("flex-1")
|
|
314
|
+
expect(element.props.className).toContain("text-[16px]")
|
|
315
|
+
expect(element.props.className).toContain("font-semibold")
|
|
316
|
+
expect(element.props.style).toEqual(animatedStyle)
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
|
|
320
|
+
describe("className — edge cases", () => {
|
|
321
|
+
it("works correctly when className is undefined", () => {
|
|
322
|
+
const { getByText } = render(
|
|
323
|
+
<AnimatedF0Text>No className</AnimatedF0Text>
|
|
324
|
+
)
|
|
325
|
+
const element = getByText("No className")
|
|
326
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
327
|
+
expect(element.props.className).toContain("font-normal")
|
|
328
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
329
|
+
})
|
|
330
|
+
|
|
331
|
+
it("works correctly when className is empty string", () => {
|
|
332
|
+
const { getByText } = render(
|
|
333
|
+
<AnimatedF0Text className="">Empty className</AnimatedF0Text>
|
|
334
|
+
)
|
|
335
|
+
const element = getByText("Empty className")
|
|
336
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
337
|
+
expect(element.props.className).toContain("font-normal")
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
it("handles className with extra whitespace", () => {
|
|
341
|
+
const { getByText } = render(
|
|
342
|
+
<AnimatedF0Text className=" mt-4 mb-2 ">
|
|
343
|
+
Whitespace className
|
|
344
|
+
</AnimatedF0Text>
|
|
345
|
+
)
|
|
346
|
+
const element = getByText("Whitespace className")
|
|
347
|
+
expect(element.props.className).toContain("mt-4")
|
|
348
|
+
expect(element.props.className).toContain("mb-2")
|
|
349
|
+
})
|
|
350
|
+
})
|
|
131
351
|
})
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-md-default 1`] = `
|
|
4
4
|
<Text
|
|
5
|
-
className="text-[16px] leading-[24px] font-normal text-f0-foreground text-left"
|
|
5
|
+
className="no-underline normal-case tracking-normal text-[16px] leading-[24px] font-normal text-f0-foreground text-left"
|
|
6
6
|
>
|
|
7
7
|
body-md-default
|
|
8
8
|
text
|
|
@@ -11,7 +11,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
11
11
|
|
|
12
12
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-md-medium 1`] = `
|
|
13
13
|
<Text
|
|
14
|
-
className="text-[16px] leading-[24px] font-medium text-f0-foreground text-left"
|
|
14
|
+
className="no-underline normal-case tracking-normal text-[16px] leading-[24px] font-medium text-f0-foreground text-left"
|
|
15
15
|
>
|
|
16
16
|
body-md-medium
|
|
17
17
|
text
|
|
@@ -20,7 +20,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
20
20
|
|
|
21
21
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-md-semibold 1`] = `
|
|
22
22
|
<Text
|
|
23
|
-
className="text-[16px] leading-[24px] font-semibold text-f0-foreground text-left"
|
|
23
|
+
className="no-underline normal-case tracking-normal text-[16px] leading-[24px] font-semibold text-f0-foreground text-left"
|
|
24
24
|
>
|
|
25
25
|
body-md-semibold
|
|
26
26
|
text
|
|
@@ -29,7 +29,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
29
29
|
|
|
30
30
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-sm-default 1`] = `
|
|
31
31
|
<Text
|
|
32
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
32
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
33
33
|
>
|
|
34
34
|
body-sm-default
|
|
35
35
|
text
|
|
@@ -38,7 +38,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
38
38
|
|
|
39
39
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-sm-medium 1`] = `
|
|
40
40
|
<Text
|
|
41
|
-
className="text-[14px] leading-[20px] font-medium text-f0-foreground text-left"
|
|
41
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-medium text-f0-foreground text-left"
|
|
42
42
|
>
|
|
43
43
|
body-sm-medium
|
|
44
44
|
text
|
|
@@ -47,7 +47,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
47
47
|
|
|
48
48
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-sm-semibold 1`] = `
|
|
49
49
|
<Text
|
|
50
|
-
className="text-[14px] leading-[20px] font-semibold text-f0-foreground text-left"
|
|
50
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-semibold text-f0-foreground text-left"
|
|
51
51
|
>
|
|
52
52
|
body-sm-semibold
|
|
53
53
|
text
|
|
@@ -56,7 +56,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
56
56
|
|
|
57
57
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-xs-medium 1`] = `
|
|
58
58
|
<Text
|
|
59
|
-
className="text-[12px] leading-[16px] font-medium text-f0-foreground text-left"
|
|
59
|
+
className="no-underline normal-case tracking-normal text-[12px] leading-[16px] font-medium text-f0-foreground text-left"
|
|
60
60
|
>
|
|
61
61
|
body-xs-medium
|
|
62
62
|
text
|
|
@@ -65,7 +65,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-body-
|
|
|
65
65
|
|
|
66
66
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-heading-lg 1`] = `
|
|
67
67
|
<Text
|
|
68
|
-
className="text-[24px] leading-[32px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
68
|
+
className="no-underline normal-case text-[24px] leading-[32px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
69
69
|
>
|
|
70
70
|
heading-lg
|
|
71
71
|
text
|
|
@@ -74,7 +74,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-headi
|
|
|
74
74
|
|
|
75
75
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-heading-md 1`] = `
|
|
76
76
|
<Text
|
|
77
|
-
className="text-[20px] leading-[28px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
77
|
+
className="no-underline normal-case text-[20px] leading-[28px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
78
78
|
>
|
|
79
79
|
heading-md
|
|
80
80
|
text
|
|
@@ -83,7 +83,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-headi
|
|
|
83
83
|
|
|
84
84
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-heading-sm 1`] = `
|
|
85
85
|
<Text
|
|
86
|
-
className="text-[16px] leading-[24px] font-semibold text-f0-foreground text-left"
|
|
86
|
+
className="no-underline normal-case tracking-normal text-[16px] leading-[24px] font-semibold text-f0-foreground text-left"
|
|
87
87
|
>
|
|
88
88
|
heading-sm
|
|
89
89
|
text
|
|
@@ -92,7 +92,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-headi
|
|
|
92
92
|
|
|
93
93
|
exports[`AnimatedF0Text Snapshots renders all typography variants: variant-heading-xl 1`] = `
|
|
94
94
|
<Text
|
|
95
|
-
className="text-[36px] leading-[40px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
95
|
+
className="no-underline normal-case text-[36px] leading-[40px] tracking-[-0.2px] font-semibold text-f0-foreground text-left"
|
|
96
96
|
>
|
|
97
97
|
heading-xl
|
|
98
98
|
text
|
|
@@ -101,7 +101,7 @@ exports[`AnimatedF0Text Snapshots renders all typography variants: variant-headi
|
|
|
101
101
|
|
|
102
102
|
exports[`AnimatedF0Text Snapshots renders with color variants: color-accent 1`] = `
|
|
103
103
|
<Text
|
|
104
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground-accent text-left"
|
|
104
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground-accent text-left"
|
|
105
105
|
>
|
|
106
106
|
accent
|
|
107
107
|
text
|
|
@@ -110,7 +110,7 @@ exports[`AnimatedF0Text Snapshots renders with color variants: color-accent 1`]
|
|
|
110
110
|
|
|
111
111
|
exports[`AnimatedF0Text Snapshots renders with color variants: color-critical 1`] = `
|
|
112
112
|
<Text
|
|
113
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground-critical text-left"
|
|
113
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground-critical text-left"
|
|
114
114
|
>
|
|
115
115
|
critical
|
|
116
116
|
text
|
|
@@ -119,7 +119,7 @@ exports[`AnimatedF0Text Snapshots renders with color variants: color-critical 1`
|
|
|
119
119
|
|
|
120
120
|
exports[`AnimatedF0Text Snapshots renders with color variants: color-default 1`] = `
|
|
121
121
|
<Text
|
|
122
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
122
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
123
123
|
>
|
|
124
124
|
default
|
|
125
125
|
text
|
|
@@ -128,7 +128,7 @@ exports[`AnimatedF0Text Snapshots renders with color variants: color-default 1`]
|
|
|
128
128
|
|
|
129
129
|
exports[`AnimatedF0Text Snapshots renders with color variants: color-secondary 1`] = `
|
|
130
130
|
<Text
|
|
131
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground-secondary text-left"
|
|
131
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground-secondary text-left"
|
|
132
132
|
>
|
|
133
133
|
secondary
|
|
134
134
|
text
|
|
@@ -137,7 +137,7 @@ exports[`AnimatedF0Text Snapshots renders with color variants: color-secondary 1
|
|
|
137
137
|
|
|
138
138
|
exports[`AnimatedF0Text Snapshots renders with default variant (body-sm-default) 1`] = `
|
|
139
139
|
<Text
|
|
140
|
-
className="text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
140
|
+
className="no-underline normal-case tracking-normal text-[14px] leading-[20px] font-normal text-f0-foreground text-left"
|
|
141
141
|
>
|
|
142
142
|
Default text
|
|
143
143
|
</Text>
|
|
@@ -40,18 +40,19 @@ import { F0Text } from "@factorialco/f0-react-native"
|
|
|
40
40
|
|
|
41
41
|
### Props
|
|
42
42
|
|
|
43
|
-
| Prop | Type | Default | Description
|
|
44
|
-
| --------------- | ------------------- | ------------------- |
|
|
45
|
-
| `variant` | `TypographyVariant` | `'body-sm-default'` | Typography variant with weight included
|
|
46
|
-
| `color` | `TextColor` | `'default'` | Text color from F0 semantic color system
|
|
47
|
-
| `align` | `TextAlign` | `'left'` | Text alignment (left, center, right, justify)
|
|
48
|
-
| `decoration` | `TextDecoration` | `'none'` | Text decoration (none, underline, line-through)
|
|
49
|
-
| `transform` | `TextTransform` | `'none'` | Text transform (none, uppercase, lowercase, capitalize)
|
|
50
|
-
| `numberOfLines` | `number` | `undefined` | Max lines before truncation with ellipsis
|
|
43
|
+
| Prop | Type | Default | Description |
|
|
44
|
+
| --------------- | ------------------- | ------------------- | -------------------------------------------------------- |
|
|
45
|
+
| `variant` | `TypographyVariant` | `'body-sm-default'` | Typography variant with weight included |
|
|
46
|
+
| `color` | `TextColor` | `'default'` | Text color from F0 semantic color system |
|
|
47
|
+
| `align` | `TextAlign` | `'left'` | Text alignment (left, center, right, justify) |
|
|
48
|
+
| `decoration` | `TextDecoration` | `'none'` | Text decoration (none, underline, line-through) |
|
|
49
|
+
| `transform` | `TextTransform` | `'none'` | Text transform (none, uppercase, lowercase, capitalize) |
|
|
50
|
+
| `numberOfLines` | `number` | `undefined` | Max lines before truncation with ellipsis |
|
|
51
|
+
| `className` | `string` | `undefined` | Layout/positioning classes (margin, padding, flex, etc.) |
|
|
51
52
|
|
|
52
|
-
All React Native `TextProps` are also supported (onPress, testID, etc.).
|
|
53
|
+
All React Native `TextProps` are also supported (onPress, testID, etc.), **except `style`** which is omitted from the type and filtered at runtime.
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
Typography is controlled exclusively by semantic props (variant, color, align, etc.) and always takes precedence — any typography classes passed via `className` are automatically overridden by the semantic props via `twMerge`.
|
|
55
56
|
|
|
56
57
|
### Typography Variants
|
|
57
58
|
|
|
@@ -180,26 +181,33 @@ All variants use **Inter** font family with the weight included in the variant n
|
|
|
180
181
|
|
|
181
182
|
### Spacing & Layout
|
|
182
183
|
|
|
183
|
-
F0Text
|
|
184
|
+
F0Text accepts `className` for layout and positioning. Typography classes in `className` are safely overridden by semantic props via `twMerge`:
|
|
184
185
|
|
|
185
186
|
<!-- prettier-ignore -->
|
|
186
187
|
```tsx
|
|
187
188
|
<>
|
|
188
|
-
{/* Spacing
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
</
|
|
189
|
+
{/* Spacing directly on the text */}
|
|
190
|
+
<F0Text variant="body-sm-default" className="mt-4 mb-2">
|
|
191
|
+
Text with margin
|
|
192
|
+
</F0Text>
|
|
192
193
|
|
|
193
|
-
{/* Layout
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
</
|
|
194
|
+
{/* Layout directly on the text */}
|
|
195
|
+
<F0Text variant="body-sm-default" className="flex-1">
|
|
196
|
+
Flexible text
|
|
197
|
+
</F0Text>
|
|
197
198
|
|
|
198
199
|
{/* Icon + Text pattern */}
|
|
199
200
|
<View className="flex-row items-center gap-2">
|
|
200
201
|
<F0Icon icon={Check} size="sm" />
|
|
201
|
-
<F0Text variant="body-sm-default"
|
|
202
|
+
<F0Text variant="body-sm-default" className="flex-1">
|
|
203
|
+
Success message
|
|
204
|
+
</F0Text>
|
|
202
205
|
</View>
|
|
206
|
+
|
|
207
|
+
{/* Typography override attempts are safely ignored */}
|
|
208
|
+
<F0Text variant="body-sm-default" className="mt-4 font-bold text-red-500">
|
|
209
|
+
font-bold and text-red-500 are ignored; mt-4 is applied
|
|
210
|
+
</F0Text>
|
|
203
211
|
</>
|
|
204
212
|
```
|
|
205
213
|
|
|
@@ -236,18 +244,16 @@ F0Text doesn't accept `className` to prevent typography override. Use a View wra
|
|
|
236
244
|
<!-- prettier-ignore -->
|
|
237
245
|
```tsx
|
|
238
246
|
<View className="rounded-lg bg-f0-background-secondary p-4">
|
|
239
|
-
<
|
|
240
|
-
|
|
241
|
-
</
|
|
247
|
+
<F0Text variant="heading-sm" className="mb-2">
|
|
248
|
+
Card Title
|
|
249
|
+
</F0Text>
|
|
242
250
|
<F0Text variant="body-sm-default" color="secondary" numberOfLines={2}>
|
|
243
251
|
This is a description that will be truncated after two lines if it's too
|
|
244
252
|
long to fit in the available space.
|
|
245
253
|
</F0Text>
|
|
246
|
-
<
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
</F0Text>
|
|
250
|
-
</View>
|
|
254
|
+
<F0Text variant="body-xs-medium" color="tertiary" className="mt-2">
|
|
255
|
+
Last updated 2 hours ago
|
|
256
|
+
</F0Text>
|
|
251
257
|
</View>
|
|
252
258
|
```
|
|
253
259
|
|
|
@@ -347,11 +353,16 @@ to these variables.
|
|
|
347
353
|
|
|
348
354
|
<!-- prettier-ignore -->
|
|
349
355
|
```tsx
|
|
350
|
-
// ✅ Good: Use
|
|
356
|
+
// ✅ Good: Use semantic props for typography
|
|
351
357
|
<F0Text variant="body-md-semibold">Bold text</F0Text>
|
|
352
358
|
|
|
353
|
-
//
|
|
354
|
-
|
|
359
|
+
// ✅ Good: Use className for layout
|
|
360
|
+
<F0Text variant="body-md-semibold" className="mt-4 flex-1">Bold text</F0Text>
|
|
361
|
+
|
|
362
|
+
// ❌ Bad: Don't use className for typography (it will be overridden)
|
|
363
|
+
<F0Text variant="body-sm-default" className="font-bold text-red-500">
|
|
364
|
+
font-bold and text-red-500 are silently ignored
|
|
365
|
+
</F0Text>
|
|
355
366
|
```
|
|
356
367
|
|
|
357
368
|
<!-- prettier-ignore -->
|
|
@@ -7,7 +7,7 @@ import { tv, type VariantProps } from "tailwind-variants"
|
|
|
7
7
|
* via --font-* CSS variables defined in @theme.
|
|
8
8
|
*/
|
|
9
9
|
export const textVariants = tv({
|
|
10
|
-
base: "",
|
|
10
|
+
base: "no-underline normal-case tracking-normal",
|
|
11
11
|
variants: {
|
|
12
12
|
variant: {
|
|
13
13
|
// Heading variants
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import { Text as RNText } from "react-native"
|
|
3
3
|
|
|
4
|
-
import { omitProps } from "../../../../lib/utils"
|
|
4
|
+
import { cn, omitProps } from "../../../../lib/utils"
|
|
5
5
|
|
|
6
6
|
import { textVariants } from "./F0Text.styles"
|
|
7
7
|
import { F0_TEXT_BANNED_PROPS, type F0TextProps } from "./F0Text.types"
|
|
@@ -9,10 +9,13 @@ import { F0_TEXT_BANNED_PROPS, type F0TextProps } from "./F0Text.types"
|
|
|
9
9
|
/**
|
|
10
10
|
* F0Text - Primitive Text component with semantic typography variants
|
|
11
11
|
*
|
|
12
|
+
* Typography is controlled by semantic props and always takes precedence.
|
|
13
|
+
* `className` is accepted for layout/positioning (margin, padding, flex, etc.).
|
|
14
|
+
*
|
|
12
15
|
* @example
|
|
13
16
|
* <F0Text variant="heading-lg">Large Heading</F0Text>
|
|
14
17
|
* <F0Text variant="body-sm-default" color="secondary">Secondary text</F0Text>
|
|
15
|
-
* <F0Text variant="body-
|
|
18
|
+
* <F0Text variant="body-sm-default" className="mt-4 flex-1">Positioned text</F0Text>
|
|
16
19
|
*/
|
|
17
20
|
const F0TextComponent = React.forwardRef<RNText, F0TextProps>(
|
|
18
21
|
(
|
|
@@ -22,6 +25,7 @@ const F0TextComponent = React.forwardRef<RNText, F0TextProps>(
|
|
|
22
25
|
align = "left",
|
|
23
26
|
decoration = "none",
|
|
24
27
|
transform = "none",
|
|
28
|
+
className,
|
|
25
29
|
children,
|
|
26
30
|
numberOfLines,
|
|
27
31
|
...rest
|
|
@@ -30,14 +34,17 @@ const F0TextComponent = React.forwardRef<RNText, F0TextProps>(
|
|
|
30
34
|
) => {
|
|
31
35
|
const textClassName = React.useMemo(
|
|
32
36
|
() =>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
cn(
|
|
38
|
+
className,
|
|
39
|
+
textVariants({
|
|
40
|
+
variant,
|
|
41
|
+
color,
|
|
42
|
+
align,
|
|
43
|
+
decoration,
|
|
44
|
+
transform,
|
|
45
|
+
})
|
|
46
|
+
),
|
|
47
|
+
[variant, color, align, decoration, transform, className]
|
|
41
48
|
)
|
|
42
49
|
|
|
43
50
|
return (
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { TextProps as RNTextProps } from "react-native"
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Props that must not be passed through to the underlying RN Text
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Props that must not be passed through to the underlying RN Text.
|
|
5
|
+
* `style` is blocked to enforce the semantic API; `className` is allowed
|
|
6
|
+
* for layout/positioning and merged with typography classes via twMerge.
|
|
7
7
|
*/
|
|
8
|
-
export const F0_TEXT_BANNED_PROPS = ["style"
|
|
8
|
+
export const F0_TEXT_BANNED_PROPS = ["style"] as const
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Typography variant types based on semantic design tokens
|
|
@@ -73,10 +73,14 @@ export const TEXT_TRANSFORMS = [
|
|
|
73
73
|
export type TextTransform = (typeof TEXT_TRANSFORMS)[number]
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
76
|
+
* Props for the F0Text component.
|
|
77
|
+
*
|
|
78
|
+
* `className` is available for layout/positioning (margin, padding, flex, etc.).
|
|
79
|
+
* Typography is controlled exclusively by semantic props (variant, color, align, etc.)
|
|
80
|
+
* and always takes precedence — any typography classes in `className` are overridden.
|
|
81
|
+
* `style` is NOT available (omitted from RNTextProps and filtered at runtime).
|
|
78
82
|
*/
|
|
79
|
-
interface
|
|
83
|
+
export interface F0TextProps extends Omit<RNTextProps, "style"> {
|
|
80
84
|
/**
|
|
81
85
|
* Semantic typography variant
|
|
82
86
|
* @default "body-sm-default"
|
|
@@ -118,17 +122,17 @@ interface F0TextPropsInternal extends Omit<RNTextProps, "style"> {
|
|
|
118
122
|
children?: React.ReactNode
|
|
119
123
|
|
|
120
124
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
125
|
+
* Tailwind classes for layout and positioning.
|
|
126
|
+
*
|
|
127
|
+
* Allowed: margin, padding, flex, position, width, height, opacity, z-index, etc.
|
|
128
|
+
* Ignored: font-size, font-weight, line-height, letter-spacing, color, text-align,
|
|
129
|
+
* text-decoration, text-transform — these are controlled by semantic props and
|
|
130
|
+
* always take precedence via twMerge.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* className="mt-4 flex-1"
|
|
134
|
+
* className="mb-2 self-center"
|
|
135
|
+
* className="absolute top-0 left-0"
|
|
123
136
|
*/
|
|
124
137
|
className?: string
|
|
125
138
|
}
|
|
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">
|