@factorialco/f0-react-native 0.28.0 → 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/README.md +42 -30
- 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 +94 -38
- 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 +5 -4
- 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 +94 -38
- package/src/components/primitives/F0Text/F0Text/F0Text.styles.ts +4 -3
- 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
|
@@ -142,6 +142,7 @@ describe("F0Text", () => {
|
|
|
142
142
|
)
|
|
143
143
|
const element = getByText("Underlined text")
|
|
144
144
|
expect(element.props.className).toContain("underline")
|
|
145
|
+
expect(element.props.className).not.toContain("no-underline")
|
|
145
146
|
})
|
|
146
147
|
|
|
147
148
|
it("applies transform classes", () => {
|
|
@@ -150,6 +151,7 @@ describe("F0Text", () => {
|
|
|
150
151
|
)
|
|
151
152
|
const element = getByText("uppercase text")
|
|
152
153
|
expect(element.props.className).toContain("uppercase")
|
|
154
|
+
expect(element.props.className).not.toContain("normal-case")
|
|
153
155
|
})
|
|
154
156
|
|
|
155
157
|
it("sets numberOfLines prop correctly", () => {
|
|
@@ -193,36 +195,358 @@ describe("F0Text", () => {
|
|
|
193
195
|
expect(element.props.style).toBeUndefined()
|
|
194
196
|
})
|
|
195
197
|
|
|
196
|
-
it("
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
it("handles ref forwarding", () => {
|
|
199
|
+
const ref = React.createRef<RNText>()
|
|
200
|
+
render(<F0Text ref={ref}>Ref text</F0Text>)
|
|
201
|
+
expect(ref.current).toBeTruthy()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it("renders nested children", () => {
|
|
205
|
+
const { getByText } = render(
|
|
206
|
+
<F0Text>
|
|
207
|
+
Parent <F0Text variant="body-xs-medium">nested</F0Text> text
|
|
208
|
+
</F0Text>
|
|
209
|
+
)
|
|
210
|
+
expect(getByText("nested")).toBeTruthy()
|
|
211
|
+
})
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
describe("className — layout classes pass through", () => {
|
|
215
|
+
it("applies margin classes", () => {
|
|
216
|
+
const { getByText } = render(
|
|
217
|
+
<F0Text className="mx-3 mt-4 mb-2">Margin text</F0Text>
|
|
218
|
+
)
|
|
219
|
+
const element = getByText("Margin text")
|
|
220
|
+
expect(element.props.className).toContain("mt-4")
|
|
221
|
+
expect(element.props.className).toContain("mb-2")
|
|
222
|
+
expect(element.props.className).toContain("mx-3")
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
it("applies padding classes", () => {
|
|
226
|
+
const { getByText } = render(
|
|
227
|
+
<F0Text className="p-4 px-2 py-1">Padded text</F0Text>
|
|
228
|
+
)
|
|
229
|
+
const element = getByText("Padded text")
|
|
230
|
+
expect(element.props.className).toContain("p-4")
|
|
231
|
+
expect(element.props.className).toContain("px-2")
|
|
232
|
+
expect(element.props.className).toContain("py-1")
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
it("applies flex classes", () => {
|
|
236
|
+
const { getByText } = render(
|
|
237
|
+
<F0Text className="flex-1 flex-shrink-0">Flex text</F0Text>
|
|
238
|
+
)
|
|
239
|
+
const element = getByText("Flex text")
|
|
240
|
+
expect(element.props.className).toContain("flex-1")
|
|
241
|
+
expect(element.props.className).toContain("flex-shrink-0")
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it("applies self-alignment classes", () => {
|
|
245
|
+
const { getByText } = render(
|
|
246
|
+
<F0Text className="self-center">Self-aligned text</F0Text>
|
|
247
|
+
)
|
|
248
|
+
const element = getByText("Self-aligned text")
|
|
249
|
+
expect(element.props.className).toContain("self-center")
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it("applies position classes", () => {
|
|
253
|
+
const { getByText } = render(
|
|
254
|
+
<F0Text className="absolute top-0 left-0">Positioned text</F0Text>
|
|
255
|
+
)
|
|
256
|
+
const element = getByText("Positioned text")
|
|
257
|
+
expect(element.props.className).toContain("absolute")
|
|
258
|
+
expect(element.props.className).toContain("top-0")
|
|
259
|
+
expect(element.props.className).toContain("left-0")
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
it("applies width and height classes", () => {
|
|
263
|
+
const { getByText } = render(
|
|
264
|
+
<F0Text className="w-full max-w-xs">Sized text</F0Text>
|
|
265
|
+
)
|
|
266
|
+
const element = getByText("Sized text")
|
|
267
|
+
expect(element.props.className).toContain("w-full")
|
|
268
|
+
expect(element.props.className).toContain("max-w-xs")
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
it("applies opacity classes", () => {
|
|
272
|
+
const { getByText } = render(
|
|
273
|
+
<F0Text className="opacity-50">Faded text</F0Text>
|
|
274
|
+
)
|
|
275
|
+
const element = getByText("Faded text")
|
|
276
|
+
expect(element.props.className).toContain("opacity-50")
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
it("applies display/visibility classes", () => {
|
|
280
|
+
const { getByText } = render(
|
|
281
|
+
<F0Text className="hidden">Hidden text</F0Text>
|
|
282
|
+
)
|
|
283
|
+
const element = getByText("Hidden text")
|
|
284
|
+
expect(element.props.className).toContain("hidden")
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
it("applies z-index classes", () => {
|
|
288
|
+
const { getByText } = render(
|
|
289
|
+
<F0Text className="z-10">Layered text</F0Text>
|
|
290
|
+
)
|
|
291
|
+
const element = getByText("Layered text")
|
|
292
|
+
expect(element.props.className).toContain("z-10")
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
it("preserves layout classes with all variant combinations", () => {
|
|
296
|
+
const { getByText } = render(
|
|
297
|
+
<F0Text
|
|
298
|
+
variant="heading-lg"
|
|
299
|
+
color="critical"
|
|
300
|
+
align="center"
|
|
301
|
+
decoration="underline"
|
|
302
|
+
transform="uppercase"
|
|
303
|
+
className="mt-4 flex-1 self-end"
|
|
304
|
+
>
|
|
305
|
+
Full combo
|
|
306
|
+
</F0Text>
|
|
307
|
+
)
|
|
308
|
+
const element = getByText("Full combo")
|
|
309
|
+
expect(element.props.className).toContain("mt-4")
|
|
310
|
+
expect(element.props.className).toContain("flex-1")
|
|
311
|
+
expect(element.props.className).toContain("self-end")
|
|
312
|
+
expect(element.props.className).toContain("text-[24px]")
|
|
313
|
+
expect(element.props.className).toContain("font-semibold")
|
|
314
|
+
expect(element.props.className).toContain("text-f0-foreground-critical")
|
|
315
|
+
expect(element.props.className).toContain("text-center")
|
|
316
|
+
expect(element.props.className).toContain("underline")
|
|
317
|
+
expect(element.props.className).toContain("uppercase")
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
describe("className — typography overrides are rejected", () => {
|
|
322
|
+
it("rejects font-size override", () => {
|
|
323
|
+
const { getByText } = render(
|
|
324
|
+
<F0Text variant="body-sm-default" className="text-xs">
|
|
325
|
+
Size override attempt
|
|
326
|
+
</F0Text>
|
|
327
|
+
)
|
|
328
|
+
const element = getByText("Size override attempt")
|
|
329
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
330
|
+
expect(element.props.className).not.toContain("text-xs")
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
it("rejects arbitrary font-size override", () => {
|
|
334
|
+
const { getByText } = render(
|
|
335
|
+
<F0Text variant="body-sm-default" className="text-[40px]">
|
|
336
|
+
Size override attempt
|
|
337
|
+
</F0Text>
|
|
338
|
+
)
|
|
339
|
+
const element = getByText("Size override attempt")
|
|
340
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
341
|
+
expect(element.props.className).not.toContain("text-[40px]")
|
|
342
|
+
})
|
|
343
|
+
|
|
344
|
+
it("rejects font-weight override", () => {
|
|
345
|
+
const { getByText } = render(
|
|
346
|
+
<F0Text variant="body-sm-default" className="font-bold">
|
|
347
|
+
Weight override attempt
|
|
348
|
+
</F0Text>
|
|
349
|
+
)
|
|
350
|
+
const element = getByText("Weight override attempt")
|
|
351
|
+
expect(element.props.className).toContain("font-normal")
|
|
352
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
it("rejects line-height override", () => {
|
|
356
|
+
const { getByText } = render(
|
|
357
|
+
<F0Text variant="body-sm-default" className="leading-10">
|
|
358
|
+
Leading override attempt
|
|
359
|
+
</F0Text>
|
|
360
|
+
)
|
|
361
|
+
const element = getByText("Leading override attempt")
|
|
362
|
+
expect(element.props.className).toContain("leading-[20px]")
|
|
363
|
+
expect(element.props.className).not.toContain("leading-10")
|
|
364
|
+
})
|
|
365
|
+
|
|
366
|
+
it("rejects letter-spacing override", () => {
|
|
367
|
+
const { getByText } = render(
|
|
368
|
+
<F0Text variant="heading-lg" className="tracking-widest">
|
|
369
|
+
Tracking override attempt
|
|
370
|
+
</F0Text>
|
|
371
|
+
)
|
|
372
|
+
const element = getByText("Tracking override attempt")
|
|
373
|
+
expect(element.props.className).toContain("tracking-[-0.2px]")
|
|
374
|
+
expect(element.props.className).not.toContain("tracking-widest")
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
it("rejects text-color override", () => {
|
|
378
|
+
const { getByText } = render(
|
|
379
|
+
<F0Text color="default" className="text-red-500">
|
|
380
|
+
Color override attempt
|
|
381
|
+
</F0Text>
|
|
382
|
+
)
|
|
383
|
+
const element = getByText("Color override attempt")
|
|
384
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
385
|
+
expect(element.props.className).not.toContain("text-red-500")
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
it("rejects text-align override", () => {
|
|
389
|
+
const { getByText } = render(
|
|
390
|
+
<F0Text align="left" className="text-center">
|
|
391
|
+
Align override attempt
|
|
392
|
+
</F0Text>
|
|
393
|
+
)
|
|
394
|
+
const element = getByText("Align override attempt")
|
|
395
|
+
expect(element.props.className).toContain("text-left")
|
|
396
|
+
expect(element.props.className).not.toContain("text-center")
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it("rejects text-decoration override", () => {
|
|
400
|
+
const { getByText } = render(
|
|
401
|
+
<F0Text decoration="none" className="underline">
|
|
402
|
+
Decoration override attempt
|
|
403
|
+
</F0Text>
|
|
404
|
+
)
|
|
405
|
+
const element = getByText("Decoration override attempt")
|
|
406
|
+
const classes = element.props.className.split(" ")
|
|
407
|
+
expect(classes).not.toContain("underline")
|
|
408
|
+
expect(classes).toContain("no-underline")
|
|
409
|
+
})
|
|
410
|
+
|
|
411
|
+
it("rejects text-transform override", () => {
|
|
412
|
+
const { getByText } = render(
|
|
413
|
+
<F0Text transform="none" className="uppercase">
|
|
414
|
+
Transform override attempt
|
|
415
|
+
</F0Text>
|
|
416
|
+
)
|
|
417
|
+
const element = getByText("Transform override attempt")
|
|
418
|
+
expect(element.props.className).not.toContain("uppercase")
|
|
419
|
+
})
|
|
420
|
+
|
|
421
|
+
it("rejects multiple typography overrides simultaneously", () => {
|
|
422
|
+
const { getByText } = render(
|
|
423
|
+
<F0Text
|
|
424
|
+
variant="body-sm-default"
|
|
425
|
+
color="default"
|
|
426
|
+
className="text-xl leading-loose font-bold tracking-wider text-blue-500"
|
|
427
|
+
>
|
|
428
|
+
Multi override attempt
|
|
429
|
+
</F0Text>
|
|
430
|
+
)
|
|
431
|
+
const element = getByText("Multi override attempt")
|
|
432
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
433
|
+
expect(element.props.className).toContain("font-normal")
|
|
434
|
+
expect(element.props.className).toContain("leading-[20px]")
|
|
435
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
436
|
+
expect(element.props.className).not.toContain("text-xl")
|
|
437
|
+
expect(element.props.className).not.toContain("font-bold")
|
|
438
|
+
expect(element.props.className).not.toContain("leading-loose")
|
|
439
|
+
expect(element.props.className).not.toContain("text-blue-500")
|
|
440
|
+
expect(element.props.className).not.toContain("tracking-wider")
|
|
441
|
+
})
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
describe("className — mixed layout + typography attempts", () => {
|
|
445
|
+
it("keeps layout classes, rejects typography overrides", () => {
|
|
201
446
|
const { getByText } = render(
|
|
202
447
|
<F0Text
|
|
203
|
-
|
|
448
|
+
variant="heading-md"
|
|
449
|
+
className="mt-4 flex-1 p-2 text-xl font-bold text-red-500"
|
|
204
450
|
>
|
|
205
|
-
|
|
451
|
+
Mixed attempt
|
|
206
452
|
</F0Text>
|
|
207
453
|
)
|
|
208
|
-
const element = getByText("
|
|
454
|
+
const element = getByText("Mixed attempt")
|
|
455
|
+
expect(element.props.className).toContain("mt-4")
|
|
456
|
+
expect(element.props.className).toContain("p-2")
|
|
457
|
+
expect(element.props.className).toContain("flex-1")
|
|
458
|
+
expect(element.props.className).toContain("text-[20px]")
|
|
459
|
+
expect(element.props.className).toContain("font-semibold")
|
|
460
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
209
461
|
expect(element.props.className).not.toContain("font-bold")
|
|
210
462
|
expect(element.props.className).not.toContain("text-red-500")
|
|
463
|
+
expect(element.props.className).not.toContain("text-xl")
|
|
211
464
|
})
|
|
212
465
|
|
|
213
|
-
it("handles
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
466
|
+
it("handles real-world card pattern without View wrapper", () => {
|
|
467
|
+
const { getByText } = render(
|
|
468
|
+
<F0Text variant="heading-sm" className="mb-2">
|
|
469
|
+
Card Title
|
|
470
|
+
</F0Text>
|
|
471
|
+
)
|
|
472
|
+
const element = getByText("Card Title")
|
|
473
|
+
expect(element.props.className).toContain("mb-2")
|
|
474
|
+
expect(element.props.className).toContain("text-[16px]")
|
|
475
|
+
expect(element.props.className).toContain("font-semibold")
|
|
217
476
|
})
|
|
218
477
|
|
|
219
|
-
it("
|
|
478
|
+
it("handles icon + text row pattern", () => {
|
|
220
479
|
const { getByText } = render(
|
|
221
|
-
<F0Text>
|
|
222
|
-
|
|
480
|
+
<F0Text variant="body-sm-default" className="ml-2 flex-1">
|
|
481
|
+
Row item text
|
|
223
482
|
</F0Text>
|
|
224
483
|
)
|
|
225
|
-
|
|
484
|
+
const element = getByText("Row item text")
|
|
485
|
+
expect(element.props.className).toContain("flex-1")
|
|
486
|
+
expect(element.props.className).toContain("ml-2")
|
|
487
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
488
|
+
expect(element.props.className).toContain("font-normal")
|
|
489
|
+
})
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
describe("className — edge cases", () => {
|
|
493
|
+
it("works correctly when className is undefined", () => {
|
|
494
|
+
const { getByText } = render(<F0Text>No className</F0Text>)
|
|
495
|
+
const element = getByText("No className")
|
|
496
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
497
|
+
expect(element.props.className).toContain("font-normal")
|
|
498
|
+
expect(element.props.className).toContain("text-f0-foreground")
|
|
499
|
+
})
|
|
500
|
+
|
|
501
|
+
it("works correctly when className is empty string", () => {
|
|
502
|
+
const { getByText } = render(
|
|
503
|
+
<F0Text className="">Empty className</F0Text>
|
|
504
|
+
)
|
|
505
|
+
const element = getByText("Empty className")
|
|
506
|
+
expect(element.props.className).toContain("text-[14px]")
|
|
507
|
+
expect(element.props.className).toContain("font-normal")
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
it("handles className with extra whitespace", () => {
|
|
511
|
+
const { getByText } = render(
|
|
512
|
+
<F0Text className=" mt-4 mb-2 ">Whitespace className</F0Text>
|
|
513
|
+
)
|
|
514
|
+
const element = getByText("Whitespace className")
|
|
515
|
+
expect(element.props.className).toContain("mt-4")
|
|
516
|
+
expect(element.props.className).toContain("mb-2")
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
it("still blocks style prop even when className is allowed", () => {
|
|
520
|
+
const propsWithBoth = {
|
|
521
|
+
style: { color: "red", marginTop: 10 },
|
|
522
|
+
className: "mt-4",
|
|
523
|
+
children: "Both props",
|
|
524
|
+
}
|
|
525
|
+
const { getByText } = render(
|
|
526
|
+
<F0Text {...(propsWithBoth as React.ComponentProps<typeof F0Text>)}>
|
|
527
|
+
Both props
|
|
528
|
+
</F0Text>
|
|
529
|
+
)
|
|
530
|
+
const element = getByText("Both props")
|
|
531
|
+
expect(element.props.style).toBeUndefined()
|
|
532
|
+
expect(element.props.className).toContain("mt-4")
|
|
533
|
+
})
|
|
534
|
+
|
|
535
|
+
it("produces correct output without className", () => {
|
|
536
|
+
const { getByText } = render(
|
|
537
|
+
<F0Text variant="heading-lg" color="accent" align="center">
|
|
538
|
+
No className
|
|
539
|
+
</F0Text>
|
|
540
|
+
)
|
|
541
|
+
const element = getByText("No className")
|
|
542
|
+
expect(element.props.className).toContain("no-underline")
|
|
543
|
+
expect(element.props.className).toContain("normal-case")
|
|
544
|
+
expect(element.props.className).toContain("text-[24px]")
|
|
545
|
+
expect(element.props.className).toContain("leading-[32px]")
|
|
546
|
+
expect(element.props.className).toContain("tracking-[-0.2px]")
|
|
547
|
+
expect(element.props.className).toContain("font-semibold")
|
|
548
|
+
expect(element.props.className).toContain("text-f0-foreground-accent")
|
|
549
|
+
expect(element.props.className).toContain("text-center")
|
|
226
550
|
})
|
|
227
551
|
})
|
|
228
552
|
})
|