Skip to content

Commit

Permalink
feat: dynamically collapse legs based on available space (#55)
Browse files Browse the repository at this point in the history
* feat: dynamically collapse legs based on available space

* refactor: use correct imports

Co-authored-by: Morten Nordseth <43166974+mortennordseth@users.noreply.github.com>

* refactor: use correct imports

Co-authored-by: Morten Nordseth <43166974+mortennordseth@users.noreply.github.com>

* style: fix color of footer border

* fix: use useEffect instead of useLayoutEffect

* fix: use other imports

* fix: export explicitly

* refactor: use function syntax

* refactor: rework leg lines

* fix: rename variable

* docs: add comment

* fix: remove abundant semi colon

* fix: fix parsing of non transit trips

---------

Co-authored-by: Morten Nordseth <43166974+mortennordseth@users.noreply.github.com>
  • Loading branch information
adriansberg and mortennordseth authored Nov 9, 2023
1 parent 1ef868f commit 6ca8716
Show file tree
Hide file tree
Showing 18 changed files with 378 additions and 234 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@atb-as/theme": "^7.1.1",
"@leile/lobo-t": "^1.0.5",
"@react-aria/focus": "^3.14.2",
"@react-hook/resize-observer": "^1.2.6",
"@types/node": "20.6.2",
"@types/react": "18.2.21",
"@types/react-dom": "18.2.7",
Expand Down
7 changes: 6 additions & 1 deletion src/page-modules/assistant/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
export * from './types';
export * from './utils';
export {
parseTripQuery,
createTripQuery,
departureDateToDepartureMode,
departureModeToDepartureDate,
} from './utils';
export type { AssistantLayoutProps } from './layout';
export { default as AssistantLayout } from './layout';
4 changes: 2 additions & 2 deletions src/page-modules/assistant/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ import { FocusScope } from '@react-aria/focus';
import {
createTripQuery,
departureDateToDepartureMode,
DepartureMode,
departureModeToDepartureDate,
} from '@atb/page-modules/assistant';
} from './utils';
import { DepartureMode } from './types';
import SwapButton from '@atb/components/search/swap-button';
import GeolocationButton from '@atb/components/search/geolocation-button';
import { AnimatePresence, motion } from 'framer-motion';
Expand Down
23 changes: 12 additions & 11 deletions src/page-modules/assistant/server/journey-planner/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,13 @@ import type {
NonTransitTripData,
NonTransitTripInput,
TripInput,
} from '@atb/page-modules/assistant/types';
} from '../../types';
import {
getTransportModesEnums,
isTransportModeType,
isTransportSubmodeType,
} from '@atb/page-modules/departures/server/journey-planner';
import {
filterOutDuplicates,
getCursorByDepartureMode,
} from '@atb/page-modules/assistant';
import { filterOutDuplicates, getCursorByDepartureMode } from '../../utils';

const MIN_NUMBER_OF_TRIP_PATTERNS = 8;
const MAX_NUMBER_OF_SEARCH_ATTEMPTS = 5;
Expand Down Expand Up @@ -82,13 +79,17 @@ export function createJourneyApi(
let nonTransits: NonTransitTripData = {};

for (let [legType, nonTransitTrip] of Object.entries(result.data)) {
const tripPattern = nonTransitTrip.tripPatterns[0];

if (!tripPattern) {
// No trip pattern for trip, continuing.
continue;
}

const data: Partial<NonTransitData> = {
duration: nonTransitTrip.tripPatterns[0]?.duration,
mode: nonTransitTrip.tripPatterns[0]?.legs[0]?.mode as any,
rentedBike:
nonTransitTrip.tripPatterns[0]?.legs?.some(
(leg) => leg.rentedBike,
) ?? false,
duration: tripPattern.duration,
mode: tripPattern.legs[0]?.mode as any,
rentedBike: tripPattern.legs?.some((leg) => leg.rentedBike) ?? false,
};

const validated = nonTransitSchema.safeParse(data);
Expand Down
103 changes: 11 additions & 92 deletions src/page-modules/assistant/trip/index.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
import {
formatLocaleTime,
formatToSimpleDate,
formatToWeekday,
isInPast,
parseIfNeeded,
} from '@atb/utils/date';
import {
TripData,
type TripPattern,
} from '@atb/page-modules/assistant/server/journey-planner/validators';
type TripPattern as TripPatternType,
} from '../server/journey-planner/validators';
import style from './trip.module.css';
import { PageText, useTranslation } from '@atb/translations';
import { Typo } from '@atb/components/typography';
import { motion } from 'framer-motion';
import { MonoIcon } from '@atb/components/icon';
import { TransportIconWithLabel } from '@atb/components/transport-mode/transport-icon';
import { GeocoderFeature } from '@atb/page-modules/departures';
import { TransportModeFilterOption } from '@atb/components/transport-mode-filter/types';
import { nextTripPatterns } from '@atb/page-modules/assistant/client';
import { nextTripPatterns } from '../client';
import { DepartureMode, NonTransitTripData } from '../types';
import {
DepartureMode,
NonTransitTripData,
createTripQuery,
filterOutDuplicates,
getCursorByDepartureMode,
} from '@atb/page-modules/assistant';
} from '../utils';
import { useEffect, useState } from 'react';
import { getInitialTransportModeFilter } from '@atb/components/transport-mode-filter/utils';
import { Button } from '@atb/components/button';
import { NonTransitTrip } from '../non-transit-pill';
import { isSameDay } from 'date-fns';
import { capitalize } from 'lodash';
import { TripPatternHeader } from '@atb/page-modules/assistant/trip/trip-pattern-header';
import { tripSummary } from '@atb/page-modules/assistant/trip/utils';
import EmptySearchResults from '@atb/components/empty-search-results';
import TripPattern from './trip-pattern';

export type TripProps = {
initialFromFeature: GeocoderFeature;
Expand Down Expand Up @@ -91,7 +84,10 @@ export default function Trip({
)}

{tripPatterns.map((tripPattern, i) => (
<div key={`tripPattern-${tripPattern.expectedStartTime}-${i}`}>
<div
key={`tripPattern-${tripPattern.expectedStartTime}-${i}`}
className={style.tripPatternWrapper}
>
<DayLabel
departureTime={tripPattern.expectedStartTime}
previousDepartureTime={tripPatterns[i - 1]?.expectedStartTime}
Expand Down Expand Up @@ -170,83 +166,6 @@ function useTripPatterns(initialTrip: TripData, departureMode: DepartureMode) {
};
}

type TripPatternProps = {
tripPattern: TripPattern;
delay: number;
index: number;
};

function TripPattern({ tripPattern, delay, index }: TripPatternProps) {
const { t, language } = useTranslation();

return (
<motion.a
href="/assistant" // TODO: Use correct href.
className={style.tripPattern}
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
transition={{
delay, // staggerChildren on parent only works first render
}}
aria-label={tripSummary(
tripPattern,
t,
language,
isInPast(tripPattern.legs[0].expectedStartTime),
index + 1,
)}
>
<TripPatternHeader tripPattern={tripPattern} />

<div className={style.legs}>
{tripPattern.legs.map((leg, i) => (
<div
key={`leg-${leg.expectedStartTime}-${i}`}
className={style.legs__leg}
>
<div className={style.legs__leg__icon}>
{leg.mode ? (
<TransportIconWithLabel
mode={{ mode: leg.mode }}
label={leg.line?.publicCode}
/>
) : (
<div className={style.legs__leg__walkIcon}>
<MonoIcon icon="transportation/Walk" />
</div>
)}
</div>

<Typo.span textType="body__tertiary">
{formatLocaleTime(leg.aimedStartTime, language)}
</Typo.span>
</div>
))}

<div className={style.legs__lastLeg}>
<div className={style.legs__lastLeg__line}></div>
<div className={style.legs__lastLeg__destination}>
<div className={style.legs__lastLeg__destination__icon}>
<MonoIcon icon="places/Destination" />
</div>
<Typo.span textType="body__tertiary">
{formatLocaleTime(tripPattern.expectedEndTime, language)}
</Typo.span>
</div>
</div>
</div>

<footer className={style.footer}>
<Typo.span textType="body__primary" className={style.footer__details}>
{t(PageText.Assistant.trip.tripPattern.details)}
<MonoIcon icon="navigation/ArrowRight" />
</Typo.span>
</footer>
</motion.a>
);
}

type DayLabelProps = {
departureTime: string;
previousDepartureTime?: string;
Expand All @@ -272,7 +191,7 @@ function DayLabel({ departureTime, previousDepartureTime }: DayLabelProps) {
return null;
}

function tripPatternsWithTransitionDelay(tripPatterns: TripPattern[]) {
function tripPatternsWithTransitionDelay(tripPatterns: TripPatternType[]) {
return tripPatterns.map((tripPattern, i) => ({
...tripPattern,
transitionDelay: i * 0.1,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TripPattern } from '../../../server/journey-planner/validators';

export const tripFixture: TripPattern = {
export const tripPatternFixture: TripPattern = {
expectedStartTime: '2023-01-01T00:00:00+01:00',
expectedEndTime: '2023-01-01T01:00:00+01:00',
duration: 3600,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { cleanup, render, screen } from '@testing-library/react';
import mockRouter from 'next-router-mock';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createDynamicRouteParser } from 'next-router-mock/dynamic-routes';
import { tripFixture } from './trip.fixture';
import { tripPatternFixture } from './trip-pattern.fixture';
import {
AppCookiesProvider,
AppCookiesProviderProps,
Expand Down Expand Up @@ -45,13 +45,13 @@ const customRender = (
);
};

describe('trip', function () {
describe('trip pattern', function () {
describe('trip summary', function () {
it('should create correct summary', () => {
const Test = function () {
const { t, language } = useTranslation();

const summary = tripSummary(tripFixture, t, language, false, 1);
const summary = tripSummary(tripPatternFixture, t, language, false, 1);
return (
<div data-testid="test-id" aria-label={summary}>
{summary}
Expand All @@ -66,11 +66,11 @@ describe('trip', function () {
.getAttribute('aria-label');

const startTime = formatToClock(
tripFixture.expectedStartTime,
tripPatternFixture.expectedStartTime,
Language.Norwegian,
);
const endTime = formatToClock(
tripFixture.expectedEndTime,
tripPatternFixture.expectedEndTime,
Language.Norwegian,
);

Expand All @@ -85,7 +85,7 @@ describe('trip', function () {
const Test = function () {
const { t, language } = useTranslation();

const summary = tripSummary(tripFixture, t, language, false, 1);
const summary = tripSummary(tripPatternFixture, t, language, false, 1);
return (
<div data-testid="test-id" aria-label={summary}>
{summary}
Expand All @@ -107,11 +107,11 @@ describe('trip', function () {
.getAttribute('aria-label');

const startTime = formatToClock(
tripFixture.expectedStartTime,
tripPatternFixture.expectedStartTime,
Language.Norwegian,
);
const endTime = formatToClock(
tripFixture.expectedEndTime,
tripPatternFixture.expectedEndTime,
Language.Norwegian,
);

Expand All @@ -126,7 +126,7 @@ describe('trip', function () {
const Test = function () {
const { t, language } = useTranslation();

const summary = tripSummary(tripFixture, t, language, false, 1);
const summary = tripSummary(tripPatternFixture, t, language, false, 1);
return (
<div data-testid="test-id" aria-label={summary}>
{summary}
Expand All @@ -148,11 +148,11 @@ describe('trip', function () {
.getAttribute('aria-label');

const startTime = formatToClock(
tripFixture.expectedStartTime,
tripPatternFixture.expectedStartTime,
Language.Norwegian,
);
const endTime = formatToClock(
tripFixture.expectedEndTime,
tripPatternFixture.expectedEndTime,
Language.Norwegian,
);

Expand All @@ -167,7 +167,7 @@ describe('trip', function () {
const Test = function () {
const { t, language } = useTranslation();

const summary = tripSummary(tripFixture, t, language, true, 1);
const summary = tripSummary(tripPatternFixture, t, language, true, 1);
return (
<div data-testid="test-id" aria-label={summary}>
{summary}
Expand All @@ -190,7 +190,7 @@ describe('trip', function () {
const Test = function () {
const { t, language } = useTranslation();

const summary = tripSummary(tripFixture, t, language, true, 5);
const summary = tripSummary(tripPatternFixture, t, language, true, 5);
return (
<div data-testid="test-id" aria-label={summary}>
{summary}
Expand Down
Loading

0 comments on commit 6ca8716

Please sign in to comment.