From d7fb240f6269fb70c6b9b0dfbda596f960ac6287 Mon Sep 17 00:00:00 2001 From: Marco Montalbano Date: Thu, 17 Aug 2023 14:31:37 +0200 Subject: [PATCH] chore: manage 'no tracking details' scenario --- .../app-elements/src/ui/atoms/Steps.test.tsx | 86 +++++++++++ packages/app-elements/src/ui/atoms/Steps.tsx | 30 ++-- .../atoms/__snapshots__/Steps.test.tsx.snap | 46 ++++++ .../src/ui/resources/LineItems.tsx | 2 +- .../ui/resources/ShipmentParcels.mocks.tsx | 27 ++++ .../src/ui/resources/ShipmentParcels.tsx | 145 +++++++++--------- .../__snapshots__/LineItems.test.tsx.snap | 10 +- .../resources/ShipmentParcels.stories.tsx | 16 +- 8 files changed, 275 insertions(+), 87 deletions(-) create mode 100644 packages/app-elements/src/ui/atoms/Steps.test.tsx create mode 100644 packages/app-elements/src/ui/atoms/__snapshots__/Steps.test.tsx.snap diff --git a/packages/app-elements/src/ui/atoms/Steps.test.tsx b/packages/app-elements/src/ui/atoms/Steps.test.tsx new file mode 100644 index 000000000..eab1c20e8 --- /dev/null +++ b/packages/app-elements/src/ui/atoms/Steps.test.tsx @@ -0,0 +1,86 @@ +import { render } from '@testing-library/react' +import { Steps } from './Steps' + +describe('Steps', () => { + it('Should be rendered', () => { + const { container } = render( + + ) + expect(container).toMatchSnapshot() + }) + + it('Should render as many `li` as many steps are provided', () => { + const { getByRole } = render( + + ) + + expect(getByRole('list').children.length).toEqual(3) + expect(getByRole('list').children[0].textContent).toEqual('Step 1') + expect(getByRole('list').children[1].textContent).toEqual('Step 2') + expect(getByRole('list').children[2].textContent).toEqual('Step 3') + }) + + it('Should highlight all non "active" steps', () => { + const { getByText } = render( + + ) + + expect(getByText('Step 1').previousSibling).toHaveClass('bg-gray-100') + expect(getByText('Step 2').previousSibling).toHaveClass('bg-gray-100') + expect(getByText('Step 3').previousSibling).toHaveClass('bg-gray-100') + }) + + it('Should highlight the "active" steps', () => { + const { getByText } = render( + + ) + + expect(getByText('Step 1').previousSibling).toHaveClass('bg-primary') + expect(getByText('Step 1').previousSibling).not.toHaveClass( + 'after:bg-white' + ) + expect(getByText('Step 2').previousSibling).toHaveClass( + 'bg-primary after:bg-white' + ) + expect(getByText('Step 3').previousSibling).toHaveClass('bg-gray-100') + expect(getByText('Step 3').previousSibling).not.toHaveClass( + 'after:bg-white' + ) + }) + + it('Should mark as "active" all previous step from the latest "active"', () => { + const { getByText } = render( + + ) + + expect(getByText('Step 1').previousSibling).toHaveClass('bg-primary') + expect(getByText('Step 1').previousSibling).not.toHaveClass( + 'after:bg-white' + ) + expect(getByText('Step 2').previousSibling).toHaveClass( + 'bg-primary after:bg-white' + ) + expect(getByText('Step 3').previousSibling).toHaveClass('bg-gray-100') + expect(getByText('Step 3').previousSibling).not.toHaveClass( + 'after:bg-white' + ) + }) +}) diff --git a/packages/app-elements/src/ui/atoms/Steps.tsx b/packages/app-elements/src/ui/atoms/Steps.tsx index fcc90fd92..ea6590589 100644 --- a/packages/app-elements/src/ui/atoms/Steps.tsx +++ b/packages/app-elements/src/ui/atoms/Steps.tsx @@ -1,4 +1,5 @@ import cn from 'classnames' +import { useCallback, useMemo } from 'react' interface Step { label: string @@ -10,18 +11,24 @@ interface Props { } export const Steps: React.FC = ({ steps }) => { - const lastActiveIndex = steps.findLastIndex((step) => step.active === true) + const lastActiveIndex = useMemo( + () => steps.findLastIndex((step) => step.active === true), + [steps] + ) - const fixActive = (step: Step, index: number): Step => { - if (index < lastActiveIndex) { - return { - ...step, - active: true + const fixActive = useCallback( + (step: Step, index: number): Step => { + if (index < lastActiveIndex) { + return { + ...step, + active: true + } } - } - return step - } + return step + }, + [lastActiveIndex] + ) return (
    = ({ steps }) => { )} > {steps.map(fixActive).map((step, index) => { + const position = + index === 0 ? 'first' : index === steps.length - 1 ? 'last' : 'other' + const activePosition = index < lastActiveIndex ? 'before' : index === lastActiveIndex ? 'active' : 'after' - const position = - index === 0 ? 'first' : index === steps.length - 1 ? 'last' : 'other' return (
  • Should be rendered 1`] = ` +
    +
      +
    • +
      +
      + Step 1 +
      +
    • +
    • +
      +
      + Step 2 +
      +
    • +
    • +
      +
      + Step 3 +
      +
    • +
    +
    +`; diff --git a/packages/app-elements/src/ui/resources/LineItems.tsx b/packages/app-elements/src/ui/resources/LineItems.tsx index 48a2191c7..f5f9824a6 100644 --- a/packages/app-elements/src/ui/resources/LineItems.tsx +++ b/packages/app-elements/src/ui/resources/LineItems.tsx @@ -171,7 +171,7 @@ export const LineItems = withSkeletonTemplate<{ )} @@ -311,19 +311,19 @@ const TrackingDetails = withSkeletonTemplate<{ steps={[ { label: 'Pre-Transit', - active: lastEvent.tracking.status === 'pre_transit' + active: lastEvent?.tracking.status === 'pre_transit' }, { label: 'In Transit', - active: lastEvent.tracking.status === 'in_transit' + active: lastEvent?.tracking.status === 'in_transit' }, { label: 'Out for delivery', - active: lastEvent.tracking.status === 'out_for_delivery' + active: lastEvent?.tracking.status === 'out_for_delivery' }, { label: 'Delivered', - active: lastEvent.tracking.status === 'delivered' + active: lastEvent?.tracking.status === 'delivered' } ]} /> @@ -366,71 +366,78 @@ const TrackingDetails = withSkeletonTemplate<{ - - Last update:{' '} - - {formatDate({ - isoDate: lastEvent.date, - format: 'full', - timezone: user?.timezone - })} - - - } - /> -
    - {Object.entries(groupedEvents).map(([date, eventsByDate]) => ( -
    - - - {eventsByDate.map((event) => ( - - - - - - ))} -
    -
    - {formatDate({ - format: 'time', - isoDate: event.date, - timezone: user?.timezone - })} -
    -
    -
    -
    - {event.position !== 'first' && ( -
    - )} -
    -
    -
    - {event.tracking.message} -
    -
    - {event.tracking.tracking_location != null - ? `${event.tracking.tracking_location.city}${ - event.tracking.tracking_location.country != null - ? `, ${event.tracking.tracking_location.country}` - : '' - }` - : ''} -
    -
    + {lastEvent != null && ( + <> + + Last update:{' '} + + {formatDate({ + isoDate: lastEvent.date, + format: 'full', + timezone: user?.timezone + })} + + + } + /> +
    + {Object.entries(groupedEvents).map(([date, eventsByDate]) => ( +
    + + + + {eventsByDate.map((event) => ( + + + + + + ))} + +
    +
    + {formatDate({ + format: 'time', + isoDate: event.date, + timezone: user?.timezone + })} +
    +
    +
    +
    + {event.position !== 'first' && ( +
    + )} +
    +
    +
    + {event.tracking.message} +
    +
    + {event.tracking.tracking_location != null + ? `${event.tracking.tracking_location.city}${ + event.tracking.tracking_location.country != + null + ? `, ${event.tracking.tracking_location.country}` + : '' + }` + : ''} +
    +
    +
    + ))}
    - ))} -
    + + )} ) }) diff --git a/packages/app-elements/src/ui/resources/__snapshots__/LineItems.test.tsx.snap b/packages/app-elements/src/ui/resources/__snapshots__/LineItems.test.tsx.snap index d6e7872d6..a76356872 100644 --- a/packages/app-elements/src/ui/resources/__snapshots__/LineItems.test.tsx.snap +++ b/packages/app-elements/src/ui/resources/__snapshots__/LineItems.test.tsx.snap @@ -22,7 +22,7 @@ exports[`LineItems > should render 1`] = ` />
    should render the Footer prop when defined 1`] = ` />
    should render the InputSpinner and the trash icon when edit />
    should render the bundle 1`] = ` />
    should render the list item options 1`] = ` />
    = ( + args +): JSX.Element => +NoTrackingDetails.args = { + onRemoveParcel: function (parcelId) { + alert(`removed parcel "${parcelId}"`) + }, + shipment: shipmentWithoutTrackingDetails +} + /** * When there's no parcel, there's nothing to show. */