Skip to content

[AI-component]BpkTextV2#4150

Open
IrinaWei wants to merge 1 commit intomainfrom
BPKTextV2
Open

[AI-component]BpkTextV2#4150
IrinaWei wants to merge 1 commit intomainfrom
BPKTextV2

Conversation

@IrinaWei
Copy link
Contributor

[BpkTextV2] Add Icon Support with Compound Components Pattern

🎯 Summary

This PR introduces BpkTextV2, a new version of the BpkText component that adds prefix and suffix icon support using the compound components pattern (similar to Ark UI). The original BpkText component remains unchanged to maintain backward compatibility.

✨ Features

New Compound Components API

BpkTextV2 introduces three new sub-components:

  • BpkText.LeadingIcon - Prefix icon support
  • BpkText.TrailingIcon - Suffix icon support
  • BpkText.Content - Optional explicit content wrapper

Key Capabilities

  • Fixed 0.5rem spacing between icons and text using bpk-spacing-md() token
  • Icon-only mode with accessibilityLabel prop for screen readers
  • Automatic RTL support via CSS logical properties (margin-inline-start/end)
  • Color inheritance - Icons automatically inherit text color via fill: currentColor
  • Full backward compatibility - All existing BpkText functionality preserved

📝 Usage Examples

Basic Icon Support

import BpkText, { TEXT_STYLES } from '@skyscanner/backpack-web/bpk-component-text/src/BpkTextV2';
import BeachIconSm from '@skyscanner/backpack-web/bpk-component-icon/sm/beach';

// Leading icon
<BpkText textStyle={TEXT_STYLES.lg}>
  <BpkText.LeadingIcon>
    <BeachIconSm />
  </BpkText.LeadingIcon>
  Airport Rentals
</BpkText>

// Trailing icon
<BpkText color={TEXT_COLORS.textSecondary}>
  Pickup: Shuttle bus (MCO)
  <BpkText.TrailingIcon>
    <ChevronDownIconSm />
  </BpkText.TrailingIcon>
</BpkText>

// Both icons
<BpkText>
  <BpkText.LeadingIcon><BeachIconSm /></BpkText.LeadingIcon>
  Free cancellation
  <BpkText.TrailingIcon><ChevronDownIconSm /></BpkText.TrailingIcon>
</BpkText>

Icon-Only Mode

// Icon only with accessibility label
<BpkText accessibilityLabel="Close">
  <BpkText.LeadingIcon>
    <CloseIconSm />
  </BpkText.LeadingIcon>
</BpkText>

🏗️ Technical Implementation

Architecture

  • React Context API - Coordinates communication between parent and child components
  • State Management - Detects presence of icons via useState hooks in sub-components
  • CSS Flexbox - Uses inline-flex with align-items: center for alignment
  • Semantic HTML - Icons wrapped in <span> elements with appropriate classes
  • Accessibility - Automatically adds role="img" for icon-only span elements

CSS Classes

.bpk-text--with-icons       // Applied when icons are present
.bpk-text__leading-icon     // Prefix icon wrapper
.bpk-text__trailing-icon    // Suffix icon wrapper
.bpk-text--icon-only        // Applied for icon-only mode

📦 Files Added

Component Implementation

  • packages/bpk-component-text/src/BpkTextV2/BpkText.tsx
  • packages/bpk-component-text/src/BpkTextV2/BpkText.module.scss
  • packages/bpk-component-text/src/BpkTextV2/index.ts
  • packages/bpk-component-text/src/BpkTextV2/README.md

Testing

  • packages/bpk-component-text/src/BpkTextV2/BpkText-test.tsx (37 tests)
  • packages/bpk-component-text/src/BpkTextV2/accessibility-test.tsx (5 tests)

Examples & Documentation

  • examples/bpk-component-text-v2/examples.tsx
  • examples/bpk-component-text-v2/stories.tsx
  • examples/bpk-component-text-v2/examples.module.scss

✅ Testing

Test Coverage

  • 37 unit tests - All passing

    • Icon rendering (leading, trailing, both)
    • Icon-only mode with accessibility
    • Color inheritance
    • className forwarding
    • Backward compatibility
    • Edge cases
  • 5 accessibility tests - All passing with jest-axe

    • Leading icon
    • Trailing icon
    • Icon-only mode
    • Heading with icons
    • Basic text
  • Storybook stories - 4 new icon-specific stories

    • WithLeadingIcon
    • WithTrailingIcon
    • WithBothIcons
    • IconOnly

Manual Testing Checklist

  • Test in Storybook
  • Test in Carhire

🔄 Backward Compatibility

Important: The original BpkText component is completely unchanged and all existing usages will continue to work. BpkTextV2 is a separate implementation that can be adopted gradually.

Original BpkText

  • ✅ 28 tests passing
  • ✅ No breaking changes
  • ✅ All functionality preserved

Migration Path

Users can gradually migrate to V2 when they need icon support:

// Before (still works)
<BpkText>My text</BpkText>

// After (opt-in for icon support)
import BpkText from '@skyscanner/backpack-web/bpk-component-text/src/BpkTextV2';

<BpkText>
  <BpkText.LeadingIcon><Icon /></BpkText.LeadingIcon>
  My text
</BpkText>

🎨 Design Decisions

Why Compound Components?

User explicitly requested the compound components pattern (like Ark UI) over the props-based approach used in BpkChip because:

  1. Maximum composability - More flexible than props-based API
  2. Clearer visual structure - JSX clearly shows the component hierarchy
  3. Future extensibility - Easy to add new sub-components (badges, tooltips, etc.)
  4. Explicit intent - Clear separation of concerns in markup

Why Not Props-Based?

While BpkChip uses leadingAccessoryView/trailingAccessoryView props, compound components provide:

  • Better developer experience for complex compositions
  • More intuitive API for users familiar with modern React patterns
  • Aligns with industry trends (Radix UI, Ark UI, etc.)

📊 Bundle Impact

  • Minimal impact - Only adds Context API and sub-component definitions
  • Tree-shakeable - V2 is separate, doesn't affect V1 bundle size
  • No runtime performance impact - Context updates only on mount/unmount

🚀 Next Steps

After this PR merges:

  1. Update design system documentation site
  2. Create migration guide for teams
  3. Gather feedback from early adopters
  4. Consider deprecating V1 in future major version (if V2 proves successful)

📸 Screenshots

See Storybook stories for visual examples:

  • Story: bpk-component-text-v2/WithLeadingIcon
  • Story: bpk-component-text-v2/WithTrailingIcon
  • Story: bpk-component-text-v2/WithBothIcons
  • Story: bpk-component-text-v2/IconOnly

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings January 27, 2026 06:35
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 BpkTextV2, a new text component that extends the existing BpkText functionality with prefix and suffix icon support using the compound components pattern. The original BpkText component remains unchanged to maintain backward compatibility.

Changes:

  • Added compound components API (BpkText.LeadingIcon, BpkText.TrailingIcon, BpkText.Content) for icon integration
  • Implemented fixed 0.5rem spacing between icons and text with automatic RTL support
  • Added icon-only mode with accessibility label support

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/bpk-component-text/src/BpkTextV2/BpkText.tsx Main component implementation with Context API for icon coordination
packages/bpk-component-text/src/BpkTextV2/BpkText.module.scss Styles for icon spacing, flexbox layout, and text variants
packages/bpk-component-text/src/BpkTextV2/index.ts Export file for component and types
packages/bpk-component-text/src/BpkTextV2/BpkText-test.tsx Unit tests covering icon rendering and functionality
packages/bpk-component-text/src/BpkTextV2/accessibility-test.tsx Accessibility tests using jest-axe
packages/bpk-component-text/src/BpkTextV2/README.md Documentation for V2 features and migration guide
examples/bpk-component-text-v2/examples.tsx Example implementations of the new component
examples/bpk-component-text-v2/stories.tsx Storybook stories for visual testing
examples/bpk-component-text-v2/examples.module.scss Styles for example components

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

import ChevronDownIconSm from '../../packages/bpk-component-icon/sm/chevron-down';
import CloseIconSm from '../../packages/bpk-component-icon/sm/close-circle';
import BpkText, { TEXT_COLORS, TEXT_STYLES } from '../../packages/bpk-component-text/src/BpkTextV2/BpkText';
import { withDefaultProps , cssModules } from '../../packages/bpk-react-utils';
Copy link

Copilot AI Jan 27, 2026

Choose a reason for hiding this comment

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

Extra space before comma in import statement. Should be 'withDefaultProps,' instead of 'withDefaultProps ,'.

Suggested change
import { withDefaultProps , cssModules } from '../../packages/bpk-react-utils';
import { withDefaultProps, cssModules } from '../../packages/bpk-react-utils';

Copilot uses AI. Check for mistakes.
@IrinaWei IrinaWei added the minor Non breaking change label Jan 27, 2026
@skyscanner-backpack-bot
Copy link

Visit https://backpack.github.io/storybook-prs/4150 to see this build running in a browser.

@skyscanner-backpack-bot
Copy link

Warnings
⚠️

Package source files (e.g. packages/package-name/src/Component.js) were updated, but snapshots weren't. Have you checked that the tests still pass?

⚠️ These new files implement overriding className which is restricted: packages/bpk-component-text/src/BpkTextV2/BpkText.tsx. Please update this component to remove the className prop.

Browser support

If this is a visual change, make sure you've tested it in multiple browsers.

Generated by 🚫 dangerJS against cd28b27

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-week minor Non breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants