Skip to content

Conversation

ttsahi
Copy link
Contributor

@ttsahi ttsahi commented Oct 7, 2025

🎯 Add Generic Price and Address Components

Overview

This PR introduces two new foundational components to the headless-components library: Price and Address. These components provide flexible, internationalized solutions for displaying monetary values and addresses across different contexts and locales.

🚀 What's New

💰 Price Component

A comprehensive price display system built with composable primitives following Radix UI architecture patterns.

Key Features:

  • Multi-currency support with proper localization
  • Flexible rendering options - formatted, amount-only, or currency-only displays
  • Advanced formatting - configurable precision, currency display modes, and accounting formats
  • Currency service integration with Wix Services Manager
  • TypeScript-first with comprehensive type definitions

Components:

  • Price.Root - Context provider with currency service setup
  • Price.Amount - Raw amount display without currency formatting
  • Price.Currency - Currency symbol/code display
  • Price.Formatted - Fully formatted price with locale-aware formatting

🏠 Address Component

An international address formatting system powered by @fragaria/address-formatter.

Key Features:

  • International formatting - Supports 200+ countries with local conventions
  • Multiple display modes - Single formatted string or array of lines
  • Flexible formatting options - Abbreviations, country appending, custom country codes
  • TypeScript-first with comprehensive address data types
  • Layout flexibility - Custom rendering with access to formatted data

Components:

  • Address.Root - Context provider for address data
  • Address.Formatted - Single formatted address string
  • Address.Lines - Array of address lines for custom layouts

🎨 Component Architecture

Both components follow established patterns from the headless-components library:

Compound Component Pattern - Composable sub-components
Context-based Data Sharing - Efficient prop drilling elimination
AsChild Support - Full custom rendering flexibility
TypeScript Integration - Comprehensive type safety
Test ID Standardization - Consistent testing approach
Documentation Completeness - Full API documentation with examples

📝 Usage Examples

Basic Price Display

<Price.Root price={{ money: { amount: 29.99, currency: 'USD' } }}>
  <Price.Formatted className="text-2xl font-bold text-foreground" />
</Price.Root>

Custom Price Layout

<Price.Root price={{ money: { amount: 1999, currency: 'EUR' }, locale: 'de-DE' }}>
  <div className="flex items-center gap-2">
    <Price.Amount className="text-3xl font-bold text-foreground" />
    <Price.Currency className="text-lg text-secondary-foreground" />
  </div>
</Price.Root>

International Address Display

<Address.Root address={{
  houseNumber: '10',
  road: 'Downing Street', 
  city: 'London',
  postcode: 'SW1A 2AA',
  countryCode: 'GB'
}}>
  <Address.Formatted className="whitespace-pre-line font-paragraph" />
</Address.Root>

Custom Address Layout

<Address.Root address={addressData}>
  <Address.Lines asChild>
    {React.forwardRef(({ lines, ...props }, ref) => (
      <div ref={ref} {...props} className="space-y-1">
        {lines.map((line, index) => (
          <div key={index} className={index === 0 ? 'font-bold' : 'text-secondary-foreground'}>
            {line}
          </div>
        ))}
      </div>
    ))}
  </Address.Lines>
</Address.Root>

🛠️ Implementation Details

Price Component Integration

  • Currency Service: Integrated with @wix/services-manager-react for centralized currency/locale management
  • Formatting: Uses Intl.NumberFormat for proper locale-aware number formatting
  • Flexibility: Supports symbol, code, name, and narrow symbol currency displays

Address Component Integration

  • Formatter Library: Leverages @fragaria/address-formatter for international address standards
  • Country Support: 200+ countries with proper local formatting conventions
  • Customization: Configurable abbreviations, country appending, and fallback options

Code Quality

  • TypeScript Coverage: 100% TypeScript with exported interfaces
  • Testing Support: Comprehensive test IDs following established patterns
  • Documentation: Complete API documentation with usage examples
  • Architecture Compliance: Follows headless-components patterns and conventions

📋 Files Changed

Core Components

  • packages/headless-components/components/src/react/address.tsx - Address component implementation
  • packages/headless-components/components/src/react/price.tsx - Price component implementation
  • packages/headless-components/components/src/services/currency-service.ts - Currency service for Price

Documentation

  • docs/api/generic-components/ADDRESS_INTERFACE.md - Complete Address API documentation
  • docs/api/generic-components/PRICE_INTERFACE.md - Complete Price API documentation

Examples

  • examples/astro-components-demo/src/components/AddressExamples.jsx - Comprehensive Address usage examples
  • examples/astro-components-demo/src/components/PriceExamples.jsx - Comprehensive Price usage examples

✅ Checklist

  • Components follow headless-components architecture patterns
  • TypeScript interfaces and exports are complete
  • Test IDs follow established naming conventions
  • AsChild pattern implemented for custom rendering
  • Context pattern implemented for data sharing
  • Documentation includes comprehensive examples
  • International localization support implemented
  • Components are exported from main index

🎯 Impact

These components provide essential building blocks for e-commerce, booking, and other applications requiring price and address displays. They eliminate the need for custom formatting logic while ensuring international compatibility and accessibility.


This PR establishes the foundation for consistent price and address handling across all Wix headless components, with full internationalization support and flexible rendering options.

@guyofeck
Copy link
Contributor

guyofeck commented Oct 8, 2025

@ttsahi we need to derive the locale for @wix/essentials
regarding the price, we need to understand wether we need to add currency for essentials as well

// Utility Functions
// ==========================================

function formatAddress(addressData: AddressData): string {
Copy link

Choose a reason for hiding this comment

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

This is not taking into consideration the regional settings so it assumes en-US format,

// ==========================================

function formatAddress(addressData: AddressData): string {
if (addressData.formattedAddress) {
Copy link

Choose a reason for hiding this comment

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

even if you have formatted address it might be an issue if it was not fetched using the right locale


function formatPrice(
money: Money,
locale: string = 'en-US',
Copy link

Choose a reason for hiding this comment

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

the locale should come from the site's regional settings

```tsx
// Creating a new address form (starts empty)
<Address.Root address={{ address: {}, countryList: countries }}>
<Address.Form onAddressChange={setAddressData}>
Copy link

Choose a reason for hiding this comment

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

did you discuss it with Wix Forms? do we have a use case of creating such form outside of forms?


---

### Address.FormCountrySelect
Copy link

Choose a reason for hiding this comment

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

I am not sure we should provided a headless component for this, unless we have something special with the list of countries, did we get an actual request for address form?

}
>;
asChild?: boolean;
separator?: string; // Separator for single-line format - default ', '
Copy link

Choose a reason for hiding this comment

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

This should be locale based, also we should probably support format options like short (shorthand country names and states), full, etc.

Copy link

Choose a reason for hiding this comment

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

did you consider using any 3rd party library for this?

- [Price.Currency](#pricecurrency)
- [Price.Symbol](#pricesymbol)
- [Price.Formatted](#priceformatted)
- [Price.CompareAt](#pricecompareat)
Copy link

Choose a reason for hiding this comment

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

Why do we need all these components? these are all Stores related which are supposed to be something like

<Product.ComareAtPrice>
  <Price.Formatted>
</Product.ComareAtPrice>

Also, for backward compatibility, we should have a default rendering of <Price.Formatted> in the stores price components in case they have no children/use asChild otherwise we need to provide a new set of product components because they are a defined API by now

range?: PriceRange;
discount?: Discount;
isOnSale?: boolean;
locale?: string; // For formatting - defaults to 'en-US'
Copy link

Choose a reason for hiding this comment

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

this should come from regional settings if not provided

}

interface Price {
current: Money;
Copy link

Choose a reason for hiding this comment

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

Isnt this is too much stores related? is this a platformized object? with all these fields? I think that we should only stick to something that formats a Money object using regional settings and use it in components that renders money

@ttsahi ttsahi requested a review from carmelc October 21, 2025 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants