Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright (c) 2025, Salesforce, Inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import React from 'react'
import {useTheme} from '@chakra-ui/react'
import PropTypes from 'prop-types'
import {useIntl} from 'react-intl'
import StatusBar from '@salesforce/retail-react-app/app/components/order-status-bar/status-bar'

const steps = ['Ordered', 'Dispatched', 'Out for delivery', 'Delivered']

const OrderStatusBar = ({currentStepLabel}) => {
const theme = useTheme()
const intl = useIntl()

const getLocalizedMessage = (status) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

@sf-madhuri-uppu Why are we hard coding localized message for every step. This adds maintenance on our part. Is there any way to take the list of steps/texts from backend and translate the array on UI instead of each step ?

Copy link
Contributor Author

@sf-madhuri-uppu sf-madhuri-uppu Jul 29, 2025

Choose a reason for hiding this comment

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

React Intl requires message IDs to be statically evaluable for extraction. Dynamic key generation like status_bar.${status.toLowerCase().replace(/\s+/g, '_')} is not static. I tried using a for loop earlier and it did not work so I had to use a switch case. The message itself is not hardcoded for every locale. We are only giving the step label as default messages. Those labels will be translated based on locale by React.intl

switch (status) {
case 'Ordered':
return intl.formatMessage({id: 'status_bar.ordered', defaultMessage: 'Ordered'})
case 'Dispatched':
return intl.formatMessage({
id: 'status_bar.dispatched',
defaultMessage: 'Dispatched'
})
case 'Out for delivery':
return intl.formatMessage({
id: 'status_bar.out_for_delivery',
defaultMessage: 'Out for delivery'
})
case 'Delivered':
return intl.formatMessage({id: 'status_bar.delivered', defaultMessage: 'Delivered'})
default:
return status
}
}

// Convert steps to the format expected by StatusBar component
const statusBarSteps = steps.map((step) => ({
label: getLocalizedMessage(step),
status: step,
description: `Order status: ${getLocalizedMessage(step)}`
}))

// Find current step index
let currentStep = steps.findIndex((step) => step === currentStepLabel)
if (currentStep === -1) currentStep = 0

// Define colors using theme
const colors = {
completed: theme.colors.teal[100], // completed steps
current: theme.colors.blue[900], // current step
future: theme.colors.gray[200], // future steps
completedText: theme.colors.black[600], // completed steps text
currentText: 'white', // current step text
futureText: theme.colors.black[600] // future steps text
}

return (
<StatusBar
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for refactoring!

steps={statusBarSteps}
currentStep={currentStep}
colors={colors}
ariaLabel="Order Status Steps"
width={1080}
height={50}
chevronWidth={24}
radius={25}
/>
)
}

OrderStatusBar.propTypes = {
currentStepLabel: PropTypes.string
}

export default OrderStatusBar
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2025, Salesforce, Inc.
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import React from 'react'
import {screen} from '@testing-library/react'
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
import OrderStatusBar from '.'

describe('OrderStatusBar', () => {
beforeEach(() => jest.clearAllMocks())

test('renders all step labels with localized messages', () => {
renderWithProviders(<OrderStatusBar />)

expect(screen.getByText('Ordered')).toBeInTheDocument()
expect(screen.getByText('Dispatched')).toBeInTheDocument()
expect(screen.getByText('Out for delivery')).toBeInTheDocument()
expect(screen.getByText('Delivered')).toBeInTheDocument()
})

test('renders step labels with responsive font sizes and word wrapping', () => {
renderWithProviders(<OrderStatusBar />)

const labels = screen.getAllByText(/Ordered|Dispatched|Out for delivery|Delivered/)
labels.forEach((label) => {
// Check that the component renders without errors
expect(label).toBeInTheDocument()
})
})

test('renders SVG element with correct attributes', () => {
renderWithProviders(<OrderStatusBar />)

const svg = document.querySelector('svg')
expect(svg).toBeInTheDocument()
expect(svg).toHaveAttribute('viewBox', '0 0 1080 50')
expect(svg).toHaveAttribute('width', '100%')
expect(svg).toHaveAttribute('preserveAspectRatio', 'none')
expect(svg).toHaveStyle({display: 'block'})
})

test('renders all step paths in SVG with correct count', () => {
renderWithProviders(<OrderStatusBar />)

const svg = document.querySelector('svg')
const paths = svg.querySelectorAll('path')
expect(paths).toHaveLength(4) // Should have 4 paths for 4 steps
})

test('renders with different currentStepLabel props and updates colors accordingly', () => {
const {rerender} = renderWithProviders(<OrderStatusBar currentStepLabel="Ordered" />)
expect(screen.getByText('Ordered')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel="Dispatched" />)
expect(screen.getByText('Dispatched')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel="Delivered" />)
expect(screen.getByText('Delivered')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel="Invalid Step" />)
expect(screen.getByText('Ordered')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel="" />)
expect(screen.getByText('Ordered')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel={undefined} />)
expect(screen.getByText('Ordered')).toBeInTheDocument()

rerender(<OrderStatusBar currentStepLabel={['Ordered', 'Dispatched']} />)
expect(screen.getByText('Ordered')).toBeInTheDocument()
})

test('renders container with correct positioning and dimensions', () => {
renderWithProviders(<OrderStatusBar />)

// The container is a Chakra UI Box component, so we check for the SVG instead
const svg = document.querySelector('svg')
expect(svg).toBeInTheDocument()
expect(svg).toHaveAttribute('width', '100%')
expect(svg).toHaveAttribute('viewBox', '0 0 1080 50')
})

test('renders text labels with correct styling properties', () => {
renderWithProviders(<OrderStatusBar />)

const labels = screen.getAllByText(/Ordered|Dispatched|Out for delivery|Delivered/)
labels.forEach((label) => {
// Check that labels are rendered
expect(label).toBeInTheDocument()
})
})

test('handles case-sensitive step label matching', () => {
// Test case-insensitive matching
const {rerender} = renderWithProviders(<OrderStatusBar currentStepLabel="ordered" />)
expect(screen.getByText('Ordered')).toBeInTheDocument()

// Test with different case
rerender(<OrderStatusBar currentStepLabel="DISPATCHED" />)
expect(screen.getByText('Dispatched')).toBeInTheDocument()

// Test with mixed case
rerender(<OrderStatusBar currentStepLabel="Out For Delivery" />)
expect(screen.getByText('Out for delivery')).toBeInTheDocument()
})
})
Loading
Loading