Skip to content

feat: add new improved text component#3551

Draft
aaroncaballero-f wants to merge 4 commits intomainfrom
feat/f0text-v2
Draft

feat: add new improved text component#3551
aaroncaballero-f wants to merge 4 commits intomainfrom
feat/f0text-v2

Conversation

@aaroncaballero-f
Copy link
Contributor

Description

Add new F0TextV2 component as a clean replacement for F0Text. This is the first step of a migration plan: create new → migrate usages → delete old → rename back.

F0TextV2 introduces a simplified two-prop architecture (variant + size) that is fully self-contained — no dependency on ui/Text/. It supports 8 semantic variants (title, heading, subtitle, body, label, description, caption, code), 7 size overrides, and 12 foreground color tokens. The old F0Text is marked @deprecated.

Screenshots (if applicable)

image image

Implementation details

  • Created F0TextV2/ with a clean 3-file structure: index.tsx (component + Component() wrapper), types.ts (token types), utils.ts (class maps)
  • Each variant defines its own default font-size, font-weight, HTML tag, and color (secondary for description, subtitle, caption, label)
  • Uses CVA for variant/size class resolution, cn() for merging
  • Follows F0Box architecture pattern with Component({ name, type: "info" })
  • Props: variant, size, color, decoration, transform, align, ellipsis, noEllipsisTooltip
  • No as prop, no markdown prop — intentionally simpler API surface
  • 72 unit tests covering all variants, sizes, colors, decorations, transforms, alignment, ellipsis, ref forwarding, and edge cases
  • Full Storybook stories included
  • F0Text and ui/Text/ remain 100% unmodified (only @deprecated JSDoc added to F0Text exports)
  • Exported via components/exports.ts

@aaroncaballero-f aaroncaballero-f self-assigned this Feb 27, 2026
@aaroncaballero-f aaroncaballero-f requested a review from a team as a code owner February 27, 2026 12:26
Copilot AI review requested due to automatic review settings February 27, 2026 12:26
@github-actions github-actions bot added feat react Changes affect packages/react labels Feb 27, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

✅ No New Circular Dependencies

No new circular dependencies detected. Current count: 0

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

📦 Alpha Package Version Published

Use pnpm i github:factorialco/f0#npm/alpha-pr-3551 to install the package

Use pnpm i github:factorialco/f0#0886b900f3053e6a148f26a26470dfe562eefb66 to install this specific commit

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

🔍 Visual review for your branch is published 🔍

Here are the links to:

@github-actions
Copy link
Contributor

github-actions bot commented Feb 27, 2026

Coverage Report for packages/react

Status Category Percentage Covered / Total
🔵 Lines 41.69% 8105 / 19441
🔵 Statements 41.09% 8320 / 20247
🔵 Functions 32.42% 1766 / 5446
🔵 Branches 31.06% 4790 / 15421
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/react/src/components/exports.ts 100% 100% 100% 100%
packages/react/src/components/F0Text/index.tsx 100% 100% 100% 100%
packages/react/src/components/F0TextV2/F0TextV2.tsx 95.45% 93.1% 100% 94.44% 186-196
packages/react/src/components/F0TextV2/index.tsx 100% 100% 100% 100%
packages/react/src/components/F0TextV2/types.ts 100% 100% 100% 100%
packages/react/src/components/F0TextV2/utils.ts 100% 100% 100% 100%
Generated in workflow #11344 for commit 23f9831 by the Vitest Coverage Report Action

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces F0TextV2 as a clean, simplified replacement for F0Text, implementing the first step of a migration strategy (create new → migrate usages → delete old → rename back). F0TextV2 provides a self-contained two-prop architecture (variant + size) with 8 semantic variants, 7 size overrides, and 12 color tokens, eliminating the dependency on ui/Text/ that F0Text has. The old F0Text is marked @deprecated but remains unchanged.

Changes:

  • Adds F0TextV2 component with complete implementation, comprehensive unit tests (72 tests), and full Storybook stories
  • Marks F0Text as @deprecated with JSDoc comments directing users to F0TextV2
  • Exports F0TextV2 via components/exports.ts following established patterns

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/react/src/components/exports.ts Adds @deprecated JSDoc to F0Text export and exports new F0TextV2
packages/react/src/components/F0TextV2/index.tsx Main component implementation using CVA, Component() wrapper, forwardRef, and OneEllipsis integration
packages/react/src/components/F0TextV2/types.ts Token type definitions for variant, size, color, alignment, decoration, and transform props
packages/react/src/components/F0TextV2/utils.ts Variant class mappings, default tag map, and secondary color variant set
packages/react/src/components/F0TextV2/tests/Text.test.tsx Comprehensive unit tests covering all variants, sizes, colors, decorations, transforms, and edge cases
packages/react/src/components/F0TextV2/stories/Text.stories.tsx Complete Storybook stories including Snapshot story for Chromatic visual regression testing
packages/react/src/components/F0Text/index.tsx Adds @deprecated JSDoc comment to F0Text exports
Comments suppressed due to low confidence (1)

packages/react/src/components/F0TextV2/types.ts:18

  • Documentation discrepancy: The comment table states that "label" defaults to "md (14px)" but the actual implementation uses "text-base" which is correct (14px). However, the table also lists description as "md (14px)" but description also uses "text-base" in the implementation. The documentation is correct about the px values but inconsistent with the Tailwind class naming.

More critically, the "label" row in line 15 claims it defaults to "md (14px) + secondary" but the implementation shows "label" uses "text-base" which is correct. The issue is the table header uses "Default Size" but the actual classes are embedded in variantVariants. Consider clarifying the table to show the actual Tailwind classes used (text-base, text-lg, text-sm, etc.) to avoid confusion.

 * | Variant     | Weight       | Default Tag | Default Size           |
 * |-------------|-------------|-------------|------------------------|
 * | title       | semibold    | h2          | 3xl (26px)             |
 * | heading     | semibold    | h3          | xl  (18px)             |
 * | subtitle    | normal      | p           | lg  (16px) + secondary |
 * | body        | normal      | p           | md  (14px)             |
 * | label       | medium      | label       | md  (14px) + secondary |
 * | description | normal      | p           | md  (14px) + secondary |
 * | caption     | normal      | span        | sm  (12px) + secondary |
 * | code        | normal+mono | code        | sm  (12px)             |

Comment on lines +173 to +179
export const F0TextV2 = Component(
{
name: "F0TextV2",
type: "info",
},
F0TextV2Inner
)
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F0TextV2 should be wrapped with withDataTestId() in addition to Component(). Following the pattern used in F0Icon and other components, F0TextV2 needs data-testid support. Wrap the Component() result with withDataTestId() like this:

import { withDataTestId } from "@/lib/data-testid"

export const F0TextV2 = withDataTestId(
  Component(
    {
      name: "F0TextV2",
      type: "info",
    },
    F0TextV2Inner
  )
)

Copilot uses AI. Check for mistakes.
],
control: "select",
description:
"Semantic text color. description/subtitle/caption default to secondary.",
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation error: The description states "description/subtitle/caption default to secondary" but omits "label", which also defaults to secondary color. Looking at the SECONDARY_COLOR_VARIANTS set in utils.ts, it includes ["description", "subtitle", "caption", "label"]. The description should be updated to "description/subtitle/caption/label default to secondary." for accuracy.

Suggested change
"Semantic text color. description/subtitle/caption default to secondary.",
"Semantic text color. description/subtitle/caption/label default to secondary.",

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +528
import { describe, expect, it } from "vitest"
import "@testing-library/jest-dom/vitest"
import { zeroRender as render, screen } from "@/testing/test-utils"

import { F0TextV2 } from "../"

describe("F0TextV2 Component", () => {
describe("Title variant", () => {
it("renders with default size (3xl) and h2 tag", () => {
render(<F0TextV2 variant="title" content="Page Title" />)
const el = screen.getByText("Page Title")
expect(el.tagName).toBe("H2")
expect(el).toHaveClass("text-3xl", "font-semibold")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="title" size="2xl" content="Smaller Title" />)
const el = screen.getByText("Smaller Title")
expect(el.tagName).toBe("H2")
expect(el).toHaveClass("text-2xl", "font-semibold")
})
})

describe("Heading variant", () => {
it("renders with default size (xl) and h3 tag", () => {
render(<F0TextV2 variant="heading" content="Section Heading" />)
const el = screen.getByText("Section Heading")
expect(el.tagName).toBe("H3")
expect(el).toHaveClass("text-xl", "font-semibold")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="heading" size="lg" content="Smaller Heading" />)
const el = screen.getByText("Smaller Heading")
expect(el.tagName).toBe("H3")
expect(el).toHaveClass("text-lg", "font-semibold")
})
})

describe("Subtitle variant", () => {
it("renders with default size (lg/text-lg) and p tag", () => {
render(<F0TextV2 variant="subtitle" content="Subtitle text" />)
const el = screen.getByText("Subtitle text")
expect(el.tagName).toBe("P")
expect(el).toHaveClass("text-lg", "font-normal")
})

it("defaults to secondary color", () => {
render(<F0TextV2 variant="subtitle" content="Subtitle secondary" />)
const el = screen.getByText("Subtitle secondary")
expect(el).toHaveClass("text-f1-foreground-secondary")
})

it("allows explicit color to override secondary default", () => {
render(
<F0TextV2
variant="subtitle"
color="critical"
content="Critical subtitle"
/>
)
const el = screen.getByText("Critical subtitle")
expect(el).toHaveClass("text-f1-foreground-critical")
})

it('allows color="default" to override secondary default', () => {
render(
<F0TextV2
variant="subtitle"
color="default"
content="Default subtitle"
/>
)
const el = screen.getByText("Default subtitle")
expect(el).not.toHaveClass("text-f1-foreground-secondary")
})

it("renders with explicit size override", () => {
render(
<F0TextV2 variant="subtitle" size="lg" content="Bigger subtitle" />
)
const el = screen.getByText("Bigger subtitle")
expect(el).toHaveClass("text-lg")
})
})

describe("Body variant", () => {
it("renders with default size (md) and p tag", () => {
render(<F0TextV2 variant="body" content="Body text" />)
const el = screen.getByText("Body text")
expect(el.tagName).toBe("P")
expect(el).toHaveClass("text-base", "font-normal")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="body" size="lg" content="Larger body" />)
const el = screen.getByText("Larger body")
expect(el.tagName).toBe("P")
expect(el).toHaveClass("text-lg", "font-normal")
})
})

describe("Label variant", () => {
it("renders with default size (md/text-base) and label tag", () => {
render(<F0TextV2 variant="label" content="Field label" />)
const el = screen.getByText("Field label")
expect(el.tagName).toBe("LABEL")
expect(el).toHaveClass("text-base", "font-medium")
})

it("defaults to secondary color", () => {
render(<F0TextV2 variant="label" content="Label secondary" />)
const el = screen.getByText("Label secondary")
expect(el).toHaveClass("text-f1-foreground-secondary")
})

it("allows explicit color to override secondary default", () => {
render(
<F0TextV2 variant="label" color="critical" content="Critical label" />
)
const el = screen.getByText("Critical label")
expect(el).toHaveClass("text-f1-foreground-critical")
})

it('allows color="default" to override secondary default', () => {
render(
<F0TextV2 variant="label" color="default" content="Default label" />
)
const el = screen.getByText("Default label")
expect(el).not.toHaveClass("text-f1-foreground-secondary")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="label" size="sm" content="Smaller label" />)
const el = screen.getByText("Smaller label")
expect(el.tagName).toBe("LABEL")
expect(el).toHaveClass("text-sm", "font-medium")
})
})

describe("Description variant", () => {
it("renders with default size (md/text-base) and secondary color", () => {
render(<F0TextV2 variant="description" content="Help text" />)
const el = screen.getByText("Help text")
expect(el.tagName).toBe("P")
expect(el).toHaveClass("text-base", "font-normal")
expect(el).toHaveClass("text-f1-foreground-secondary")
})

it("allows explicit color to override secondary default", () => {
render(
<F0TextV2 variant="description" color="critical" content="Error help" />
)
const el = screen.getByText("Error help")
expect(el).toHaveClass("text-f1-foreground-critical")
})

it('allows color="default" to override secondary default', () => {
render(
<F0TextV2
variant="description"
color="default"
content="Default color description"
/>
)
const el = screen.getByText("Default color description")
expect(el).not.toHaveClass("text-f1-foreground-secondary")
})

it("renders with explicit size override", () => {
render(
<F0TextV2 variant="description" size="sm" content="Smaller help" />
)
const el = screen.getByText("Smaller help")
expect(el).toHaveClass("text-sm")
})
})

describe("Caption variant", () => {
it("renders with default size (sm) and span tag", () => {
render(<F0TextV2 variant="caption" content="Caption text" />)
const el = screen.getByText("Caption text")
expect(el.tagName).toBe("SPAN")
expect(el).toHaveClass("text-sm", "font-normal")
})

it("defaults to secondary color", () => {
render(<F0TextV2 variant="caption" content="Caption secondary" />)
const el = screen.getByText("Caption secondary")
expect(el).toHaveClass("text-f1-foreground-secondary")
})

it("allows explicit color to override secondary default", () => {
render(
<F0TextV2 variant="caption" color="warning" content="Warning caption" />
)
const el = screen.getByText("Warning caption")
expect(el).toHaveClass("text-f1-foreground-warning")
})

it('allows color="default" to override secondary default', () => {
render(
<F0TextV2 variant="caption" color="default" content="Default caption" />
)
const el = screen.getByText("Default caption")
expect(el).not.toHaveClass("text-f1-foreground-secondary")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="caption" size="xs" content="Tiny caption" />)
const el = screen.getByText("Tiny caption")
expect(el).toHaveClass("text-xs")
})
})

describe("Code variant", () => {
it("renders with default size (sm), monospace font, and code tag", () => {
render(<F0TextV2 variant="code" content="const x = 1" />)
const el = screen.getByText("const x = 1")
expect(el.tagName).toBe("CODE")
expect(el).toHaveClass("text-sm", "font-normal", "font-mono")
})

it("renders with explicit size override", () => {
render(<F0TextV2 variant="code" size="md" content="const y = 2" />)
const el = screen.getByText("const y = 2")
expect(el.tagName).toBe("CODE")
expect(el).toHaveClass("text-base", "font-mono")
})
})

describe("Size is optional — default baked into variant", () => {
it("title defaults to 3xl", () => {
render(<F0TextV2 variant="title" content="T" />)
expect(screen.getByText("T")).toHaveClass("text-3xl")
})

it("heading defaults to xl", () => {
render(<F0TextV2 variant="heading" content="H" />)
expect(screen.getByText("H")).toHaveClass("text-xl")
})

it("subtitle defaults to lg (text-lg)", () => {
render(<F0TextV2 variant="subtitle" content="S" />)
expect(screen.getByText("S")).toHaveClass("text-lg")
})

it("body defaults to md (text-base)", () => {
render(<F0TextV2 variant="body" content="B" />)
expect(screen.getByText("B")).toHaveClass("text-base")
})

it("label defaults to md (text-base)", () => {
render(<F0TextV2 variant="label" content="L" />)
expect(screen.getByText("L")).toHaveClass("text-base")
})

it("description defaults to md (text-base)", () => {
render(<F0TextV2 variant="description" content="D" />)
expect(screen.getByText("D")).toHaveClass("text-base")
})

it("caption defaults to sm", () => {
render(<F0TextV2 variant="caption" content="C" />)
expect(screen.getByText("C")).toHaveClass("text-sm")
})

it("code defaults to sm", () => {
render(<F0TextV2 variant="code" content="X" />)
expect(screen.getByText("X")).toHaveClass("text-sm")
})
})

describe('Size "default" keeps variant built-in size', () => {
it("title with size=default keeps 3xl", () => {
render(<F0TextV2 variant="title" size="default" content="T" />)
expect(screen.getByText("T")).toHaveClass("text-3xl")
})

it("heading with size=default keeps xl", () => {
render(<F0TextV2 variant="heading" size="default" content="H" />)
expect(screen.getByText("H")).toHaveClass("text-xl")
})

it("body with size=default keeps text-base", () => {
render(<F0TextV2 variant="body" size="default" content="B" />)
expect(screen.getByText("B")).toHaveClass("text-base")
})

it("caption with size=default keeps sm", () => {
render(<F0TextV2 variant="caption" size="default" content="C" />)
expect(screen.getByText("C")).toHaveClass("text-sm")
})
})

describe("Size independence", () => {
it("allows any size with any variant", () => {
render(<F0TextV2 variant="heading" size="3xl" content="Big heading" />)
const el = screen.getByText("Big heading")
expect(el).toHaveClass("text-3xl", "font-semibold")
})

it("allows xs size with title variant", () => {
render(<F0TextV2 variant="title" size="xs" content="Tiny title" />)
const el = screen.getByText("Tiny title")
expect(el).toHaveClass("text-xs", "font-semibold")
})
})

describe("Default variant", () => {
it("defaults to body when no variant specified", () => {
render(<F0TextV2 content="Default" />)
const el = screen.getByText("Default")
expect(el.tagName).toBe("P")
expect(el).toHaveClass("text-base", "font-normal")
})
})

describe("HTML tags determined by variant", () => {
it("title renders as h2", () => {
render(<F0TextV2 variant="title" content="T" />)
expect(screen.getByText("T").tagName).toBe("H2")
})

it("heading renders as h3", () => {
render(<F0TextV2 variant="heading" content="H" />)
expect(screen.getByText("H").tagName).toBe("H3")
})

it("subtitle renders as p", () => {
render(<F0TextV2 variant="subtitle" content="S" />)
expect(screen.getByText("S").tagName).toBe("P")
})

it("body renders as p", () => {
render(<F0TextV2 variant="body" content="B" />)
expect(screen.getByText("B").tagName).toBe("P")
})

it("label renders as label", () => {
render(<F0TextV2 variant="label" content="L" />)
expect(screen.getByText("L").tagName).toBe("LABEL")
})

it("description renders as p", () => {
render(<F0TextV2 variant="description" content="D" />)
expect(screen.getByText("D").tagName).toBe("P")
})

it("caption renders as span", () => {
render(<F0TextV2 variant="caption" content="C" />)
expect(screen.getByText("C").tagName).toBe("SPAN")
})

it("code renders as code", () => {
render(<F0TextV2 variant="code" content="X" />)
expect(screen.getByText("X").tagName).toBe("CODE")
})
})

describe("Color prop", () => {
it("renders with default color", () => {
render(<F0TextV2 color="default" content="Default" />)
expect(screen.getByText("Default")).toHaveClass("text-f1-foreground")
})

it("renders with secondary color", () => {
render(<F0TextV2 color="secondary" content="Secondary" />)
expect(screen.getByText("Secondary")).toHaveClass(
"text-f1-foreground-secondary"
)
})

it("renders with tertiary color", () => {
render(<F0TextV2 color="tertiary" content="Tertiary" />)
expect(screen.getByText("Tertiary")).toHaveClass(
"text-f1-foreground-tertiary"
)
})

it("renders with critical color", () => {
render(<F0TextV2 color="critical" content="Critical" />)
expect(screen.getByText("Critical")).toHaveClass(
"text-f1-foreground-critical"
)
})

it("renders with warning color", () => {
render(<F0TextV2 color="warning" content="Warning" />)
expect(screen.getByText("Warning")).toHaveClass(
"text-f1-foreground-warning"
)
})

it("renders with positive color", () => {
render(<F0TextV2 color="positive" content="Positive" />)
expect(screen.getByText("Positive")).toHaveClass(
"text-f1-foreground-positive"
)
})

it("renders with info color", () => {
render(<F0TextV2 color="info" content="Info" />)
expect(screen.getByText("Info")).toHaveClass("text-f1-foreground-info")
})

it("renders with inverse color", () => {
render(<F0TextV2 color="inverse" content="Inverse" />)
expect(screen.getByText("Inverse")).toHaveClass(
"text-f1-foreground-inverse"
)
})

it("renders with inverse-secondary color", () => {
render(<F0TextV2 color="inverse-secondary" content="InverseSecondary" />)
expect(screen.getByText("InverseSecondary")).toHaveClass(
"text-f1-foreground-inverse-secondary"
)
})

it("renders with disabled color", () => {
render(<F0TextV2 color="disabled" content="Disabled" />)
expect(screen.getByText("Disabled")).toHaveClass(
"text-f1-foreground-disabled"
)
})

it("renders with accent color", () => {
render(<F0TextV2 color="accent" content="Accent" />)
expect(screen.getByText("Accent")).toHaveClass(
"text-f1-foreground-accent"
)
})

it("renders with selected color", () => {
render(<F0TextV2 color="selected" content="Selected" />)
expect(screen.getByText("Selected")).toHaveClass(
"text-f1-foreground-selected"
)
})

it("combines color with variant independently", () => {
render(
<F0TextV2
variant="heading"
color="critical"
content="Critical Heading"
/>
)
const el = screen.getByText("Critical Heading")
expect(el).toHaveClass("font-semibold", "text-f1-foreground-critical")
})
})

describe("Text decoration", () => {
it("renders with underline decoration", () => {
render(<F0TextV2 decoration="underline" content="Underlined" />)
expect(screen.getByText("Underlined")).toHaveClass("underline")
})

it("renders with overline decoration", () => {
render(<F0TextV2 decoration="overline" content="Overlined" />)
expect(screen.getByText("Overlined")).toHaveClass("overline")
})

it("renders with line-through decoration", () => {
render(<F0TextV2 decoration="line-through" content="Strikethrough" />)
expect(screen.getByText("Strikethrough")).toHaveClass("line-through")
})

it("renders without decoration by default", () => {
render(<F0TextV2 content="No decoration" />)
const el = screen.getByText("No decoration")
expect(el).not.toHaveClass("underline")
expect(el).not.toHaveClass("overline")
expect(el).not.toHaveClass("line-through")
})
})

describe("Text transform", () => {
it("renders with uppercase transform", () => {
render(<F0TextV2 transform="uppercase" content="Uppercase" />)
expect(screen.getByText("Uppercase")).toHaveClass("uppercase")
})

it("renders with lowercase transform", () => {
render(<F0TextV2 transform="lowercase" content="Lowercase" />)
expect(screen.getByText("Lowercase")).toHaveClass("lowercase")
})

it("renders with capitalize transform", () => {
render(<F0TextV2 transform="capitalize" content="Capitalize" />)
expect(screen.getByText("Capitalize")).toHaveClass("capitalize")
})

it("renders without transform by default", () => {
render(<F0TextV2 content="No transform" />)
const el = screen.getByText("No transform")
expect(el).not.toHaveClass("uppercase")
expect(el).not.toHaveClass("lowercase")
expect(el).not.toHaveClass("capitalize")
})
})

describe("Combined props", () => {
it("combines variant, size, color, decoration, and transform", () => {
render(
<F0TextV2
variant="label"
size="md"
color="critical"
decoration="underline"
transform="uppercase"
content="Combined"
/>
)
const el = screen.getByText("Combined")
expect(el.tagName).toBe("LABEL")
expect(el).toHaveClass(
"font-medium",
"text-base",
"text-f1-foreground-critical",
"underline",
"uppercase"
)
})
})
})
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for the align prop. The component supports left/center/right alignment via the align prop (defaulting to "left"), but there are no unit tests validating this functionality. Add test cases to verify:

  • Default alignment is left
  • Explicit align="left" applies text-left class
  • align="center" applies text-center class
  • align="right" applies text-right class

Copilot uses AI. Check for mistakes.

/**
* Semantic text color.
* `description`, `subtitle`, and `caption` default to `"secondary"`.
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation error: The JSDoc comment states that only "description", "subtitle", and "caption" default to "secondary" color, but "label" also defaults to secondary according to SECONDARY_COLOR_VARIANTS. Update the comment to include all four variants: "description", "subtitle", "caption", and "label".

Suggested change
* `description`, `subtitle`, and `caption` default to `"secondary"`.
* `description`, `subtitle`, `caption`, and `label` default to `"secondary"`.

Copilot uses AI. Check for mistakes.
@aaroncaballero-f aaroncaballero-f marked this pull request as draft February 27, 2026 13:09
Copilot AI review requested due to automatic review settings February 27, 2026 15:58
@aaroncaballero-f aaroncaballero-f requested review from Copilot and removed request for Copilot February 27, 2026 16:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

/>
<F0TextV2
variant="description"
content="Description default (sm / 12px, secondary)"
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Description default (sm / 12px, secondary)" is incorrect. According to the variant definition in variants.ts (line 24) and the types.ts documentation (line 16), the description variant uses text-base which is md/14px, not sm/12px. The comment should say "Description default (md / 14px, secondary)" to match the actual implementation.

Suggested change
content="Description default (sm / 12px, secondary)"
content="Description default (md / 14px, secondary)"

Copilot uses AI. Check for mistakes.
/>
<F0TextV2
variant="caption"
content="Caption default (xs / 10px, secondary)"
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Caption default (xs / 10px, secondary)" is incorrect. According to the variant definition in variants.ts (line 25) and the types.ts documentation (line 17), the caption variant uses text-sm which is sm/12px, not xs/10px. The comment should say "Caption default (sm / 12px, secondary)" to match the actual implementation.

Suggested change
content="Caption default (xs / 10px, secondary)"
content="Caption default (sm / 12px, secondary)"

Copilot uses AI. Check for mistakes.
Comment on lines +2 to +9
import { F0TextV2Inner, type F0TextV2Props } from "./F0TextV2"

export const F0TextV2 = Component(
{
name: "F0TextV2",
type: "info",
},
F0TextV2Inner
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F0TextV2 uses the Component() wrapper for XRay support but is missing withDataTestId() wrapper for data-testid prop support. Based on the pattern in F0Icon (packages/react/src/components/F0Icon/index.tsx:10-18), components using Component() should also be wrapped with withDataTestId(). The export should be: export const F0TextV2 = withDataTestId(Component({ name: "F0TextV2", type: "info" }, F0TextV2Inner))

Suggested change
import { F0TextV2Inner, type F0TextV2Props } from "./F0TextV2"
export const F0TextV2 = Component(
{
name: "F0TextV2",
type: "info",
},
F0TextV2Inner
import { withDataTestId } from "../../lib/test/withDataTestId"
import { F0TextV2Inner, type F0TextV2Props } from "./F0TextV2"
export const F0TextV2 = withDataTestId(
Component(
{
name: "F0TextV2",
type: "info",
},
F0TextV2Inner
)

Copilot uses AI. Check for mistakes.
Comment on lines +577 to +578
<F0TextV2 variant="label" size="md" content="Label md" />
<F0TextV2 variant="label" size="sm" content="Label sm (default)" />
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Label sm (default)" is incorrect. According to the variant definition in variants.ts (line 23), the label variant defaults to text-base which is md/14px, not sm/12px. This line should not indicate (default), or it should be applied to the md size variant instead.

Suggested change
<F0TextV2 variant="label" size="md" content="Label md" />
<F0TextV2 variant="label" size="sm" content="Label sm (default)" />
<F0TextV2 variant="label" size="md" content="Label md (default)" />
<F0TextV2 variant="label" size="sm" content="Label sm" />

Copilot uses AI. Check for mistakes.
Comment on lines +586 to +591
<F0TextV2 variant="description" size="md" content="Description md" />
<F0TextV2
variant="description"
size="sm"
content="Description sm (default)"
/>
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Description sm (default)" is incorrect. According to the variant definition in variants.ts (line 24), the description variant defaults to text-base which is md/14px, not sm/12px. The default marker should be on line 586 (Description md) instead.

Suggested change
<F0TextV2 variant="description" size="md" content="Description md" />
<F0TextV2
variant="description"
size="sm"
content="Description sm (default)"
/>
<F0TextV2
variant="description"
size="md"
content="Description md (default)"
/>
<F0TextV2 variant="description" size="sm" content="Description sm" />

Copilot uses AI. Check for mistakes.
<F0TextV2 variant="caption" size="sm" content="Caption sm" />
<F0TextV2
variant="caption"
content="Caption default (xs, secondary)"
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Caption default (xs, secondary)" is incorrect. According to the variant definition in variants.ts (line 25), the caption variant uses text-sm which is sm/12px, not xs/10px. The comment should say "Caption default (sm, secondary)" to match the actual implementation.

Suggested change
content="Caption default (xs, secondary)"
content="Caption default (sm, secondary)"

Copilot uses AI. Check for mistakes.
/>
<F0TextV2 variant="label" size="lg" content="Label lg (16px medium)" />
<F0TextV2 variant="label" size="md" content="Label md (14px medium)" />
<F0TextV2 variant="label" content="Label default (sm / 12px medium)" />
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Label default (sm / 12px medium)" is incorrect. According to the variant definition in variants.ts (line 23) and the types.ts documentation (line 15), the label variant uses text-base which is md/14px, not sm/12px. The comment should say "Label default (md / 14px medium)" or simply "Label md (default)" to match the actual implementation.

Suggested change
<F0TextV2 variant="label" content="Label default (sm / 12px medium)" />
<F0TextV2 variant="label" content="Label default (md / 14px medium)" />

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat react Changes affect packages/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants