diff --git a/src/elements-experimental/pearl-chain-time/pearl-chain-time.snapshot.spec.ts b/src/elements-experimental/pearl-chain-time/pearl-chain-time.snapshot.spec.ts index d541e79152..67d60ff047 100644 --- a/src/elements-experimental/pearl-chain-time/pearl-chain-time.snapshot.spec.ts +++ b/src/elements-experimental/pearl-chain-time/pearl-chain-time.snapshot.spec.ts @@ -1,6 +1,5 @@ import { expect } from '@open-wc/testing'; import { fixture } from '@sbb-esta/lyne-elements/core/testing/private.js'; -import { waitForLitRender } from '@sbb-esta/lyne-elements/core/testing.js'; import { html } from 'lit/static-html.js'; import type { PtRideLeg } from '../core/timetable.js'; @@ -17,16 +16,16 @@ describe(`sbb-pearl-chain-time`, () => { `); - element.legs = [ - { - __typename: 'PTRideLeg', - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(` @@ -56,16 +55,16 @@ describe(`sbb-pearl-chain-time`, () => { departure-time="2022-08-16T12:00:00" arrival-time="2022-08-16T15:00:00" departure-walk="10" - .now="${now}" + .now=${now} + .legs=${[ + { + __typename: 'PTRideLeg', + } as PtRideLeg, + ]} > `); - element.legs = [ - { - __typename: 'PTRideLeg', - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(` @@ -112,16 +111,16 @@ describe(`sbb-pearl-chain-time`, () => { departure-time="2022-08-16T12:00:00" arrival-time="2022-08-16T15:00:00" arrival-walk="10" - .now="${now}" + .now=${now} + .legs=${[ + { + __typename: 'PTRideLeg', + } as PtRideLeg, + ]} > `); - element.legs = [ - { - __typename: 'PTRideLeg', - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(` @@ -169,16 +168,16 @@ describe(`sbb-pearl-chain-time`, () => { arrival-time="2022-08-16T15:00:00" departure-walk="20" arrival-walk="10" - .now="${now}" + .now=${now} + .legs=${[ + { + __typename: 'PTRideLeg', + } as PtRideLeg, + ]} > `); - element.legs = [ - { - __typename: 'PTRideLeg', - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(` diff --git a/src/elements-experimental/pearl-chain/pearl-chain.sample-data.ts b/src/elements-experimental/pearl-chain/pearl-chain.sample-data.ts index b85026471d..7332869434 100644 --- a/src/elements-experimental/pearl-chain/pearl-chain.sample-data.ts +++ b/src/elements-experimental/pearl-chain/pearl-chain.sample-data.ts @@ -4,6 +4,8 @@ const future = '2022-12-07T12:11:00+01:00'; const future2 = '2022-12-11T12:13:00+01:00'; const defaultService = { + quayTypeName: 'platform', + quayTypeShortName: 'Pl.', serviceAlteration: { cancelled: false, delayText: 'string', @@ -11,12 +13,21 @@ const defaultService = { unplannedStopPointsText: '', }, }; -const cancelledService = { serviceAlteration: { cancelled: true } }; -const delayedService = { serviceAlteration: { delay: true } }; -const isNotReachableService = { serviceAlteration: { reachable: false } }; -const unplannedStopService = { serviceAlteration: { unplannedStopPointsText: 'unplannedStop' } }; -const redirectedService = { serviceAlteration: { redirectedText: 'Ausnahmsweise kein Halt' } }; + +const busService = { ...defaultService, quayTypeName: 'Stand', quayTypeShortName: 'Stand' }; +const cancelledService = { ...defaultService, serviceAlteration: { cancelled: true } }; +const delayedService = { ...defaultService, serviceAlteration: { delay: true } }; +const isNotReachableService = { ...defaultService, serviceAlteration: { reachable: false } }; +const unplannedStopService = { + ...defaultService, + serviceAlteration: { unplannedStopPointsText: 'unplannedStop' }, +}; +const redirectedService = { + ...defaultService, + serviceAlteration: { redirectedText: 'Exceptionally no stop' }, +}; const departureNotServiced = { + ...defaultService, stopPoints: [{ stopStatus: 'NOT_SERVICED' }, { stopStatus: 'PLANNED' }], }; const arrivalNotServiced = { @@ -68,6 +79,34 @@ export const pastLeg: any = { serviceJourney: defaultService, }; +export const defaultBusLeg: any = { + ...futureLeg, + serviceJourney: busService, +}; + +export const pastBusLeg: any = { + ...pastLeg, + serviceJourney: busService, +}; + +export const defaultShipLeg: any = { + ...futureLeg, + serviceJourney: { + ...defaultService, + quayTypeName: 'Pier', + quayTypeShortName: 'Pier', + }, +}; + +export const defaultTramLeg: any = { + ...futureLeg, + serviceJourney: { + ...defaultService, + quayTypeName: 'Stand', + quayTypeShortName: 'Stand', + }, +}; + export const delayedLeg = { __typename: 'PTRideLeg', arrival: { time: future2 }, @@ -94,8 +133,8 @@ export const redirectedOnDepartureLeg = { arrival: { time: future2 }, departure: { time: future }, serviceJourney: { - ...redirectedService, ...departureNotServiced, + ...redirectedService, }, }; diff --git a/src/elements-experimental/pearl-chain/pearl-chain.snapshot.spec.ts b/src/elements-experimental/pearl-chain/pearl-chain.snapshot.spec.ts index ab6268fe3c..e822aa1a02 100644 --- a/src/elements-experimental/pearl-chain/pearl-chain.snapshot.spec.ts +++ b/src/elements-experimental/pearl-chain/pearl-chain.snapshot.spec.ts @@ -1,6 +1,5 @@ import { expect } from '@open-wc/testing'; import { fixture } from '@sbb-esta/lyne-elements/core/testing/private.js'; -import { waitForLitRender } from '@sbb-esta/lyne-elements/core/testing.js'; import { html } from 'lit/static-html.js'; import type { PtRideLeg } from '../core/timetable.js'; @@ -9,20 +8,23 @@ import type { SbbPearlChainElement } from './pearl-chain.js'; import './pearl-chain.js'; +const now = '2022-08-16T15:00:00'; + describe(`sbb-pearl-chain`, () => { describe('sbb-pearl-chain with one leg', () => { it('renders component with config', async () => { const element = await fixture( - html``, + html``, ); - element.legs = [ - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T05:00' }, - departure: { time: '2022-08-18T04:00' }, - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(``); expect(element).shadowDom.to.be.equal(`
@@ -37,31 +39,32 @@ describe(`sbb-pearl-chain`, () => { describe('sbb-pearl-chain with two legs', () => { it('renders component with config', async () => { const element = await fixture( - html``, + html``, ); - element.legs = [ - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T05:00' }, - departure: { time: '2022-08-18T04:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - }, - } as PtRideLeg, - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T16:00' }, - departure: { time: '2022-08-18T05:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - }, - } as PtRideLeg, - ]; - await waitForLitRender(element); + expect(element).dom.to.be.equal(``); expect(element).shadowDom.to.be.equal(`
@@ -79,45 +82,46 @@ describe(`sbb-pearl-chain`, () => { describe('sbb-pearl-chain with skipped stops', () => { it('renders component with departure skipped', async () => { const element = await fixture( - html``, - ); - element.legs = [ - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T05:00' }, - departure: { time: '2022-08-18T04:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - }, - } as PtRideLeg, - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T16:00' }, - departure: { time: '2022-08-18T05:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - stopPoints: [ - { - stopStatus: 'NOT_SERVICED', + html``, + ); + expect(element).dom.to.be.equal(``); expect(element).shadowDom.to.be.equal(`
-
+
@@ -127,45 +131,45 @@ describe(`sbb-pearl-chain`, () => { it('renders component with arrival skipped', async () => { const element = await fixture( - html``, - ); - element.legs = [ - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T05:00' }, - departure: { time: '2022-08-18T04:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - }, - } as PtRideLeg, - { - __typename: 'PTRideLeg', - arrival: { time: '2022-08-18T16:00' }, - departure: { time: '2022-08-18T05:00' }, - serviceJourney: { - serviceAlteration: { - cancelled: false, - }, - stopPoints: [ - { - stopStatus: 'PLANNED', + html``, + ); expect(element).dom.to.be.equal(``); expect(element).shadowDom.to.be.equal(`
-
+
@@ -173,4 +177,122 @@ describe(`sbb-pearl-chain`, () => { `); }); }); + + describe('sbb-pearl-chain with cancelled legs', () => { + it('renders component with progress leg', async () => { + const element = await fixture( + html``, + ); + + expect(element).dom.to.be.equal(``); + expect(element).shadowDom.to.be.equal(` +
+ + +
+ + +
+
+ + +
+ + +
+ `); + }); + + it('renders component with cancelled instead of progress leg', async () => { + const element = await fixture( + html``, + ); + + expect(element).dom.to.be.equal(``); + expect(element).shadowDom.to.be.equal(` +
+ +
+
+
+ + +
+ + +
+ `); + }); + }); }); diff --git a/src/elements-experimental/pearl-chain/pearl-chain.ts b/src/elements-experimental/pearl-chain/pearl-chain.ts index dee8c6bf77..dc06cae379 100644 --- a/src/elements-experimental/pearl-chain/pearl-chain.ts +++ b/src/elements-experimental/pearl-chain/pearl-chain.ts @@ -1,6 +1,6 @@ import { defaultDateAdapter } from '@sbb-esta/lyne-elements/core/datetime.js'; import type { SbbDateLike } from '@sbb-esta/lyne-elements/core/interfaces/types'; -import { differenceInMinutes, isAfter, isBefore } from 'date-fns'; +import { addMinutes, differenceInMinutes, isAfter, isBefore } from 'date-fns'; import type { CSSResultGroup, TemplateResult } from 'lit'; import { html, LitElement, nothing } from 'lit'; import { customElement, property } from 'lit/decorators.js'; @@ -13,6 +13,10 @@ import { isRideLeg } from '../core/timetable.js'; import style from './pearl-chain.scss?lit&inline'; type Status = 'progress' | 'future' | 'past'; +type Time = { + time?: Date; + delay: number; +}; /** * It visually displays journey information. @@ -87,26 +91,37 @@ export class SbbPearlChainElement extends LitElement { return 0; } - private _getProgress(now: Date, start?: Date, end?: Date): number { - if (!start || !end) { + private _getProgress(now: Date, start?: Time, end?: Time): number { + if (!start?.time || !end?.time) { return 0; } - const total = differenceInMinutes(end, start); - const progress = differenceInMinutes(now, start); + + const startWithDelay = addMinutes(start.time, start.delay ?? 0); + const endWithDelay = addMinutes(end.time, end.delay ?? 0); + const total = differenceInMinutes(endWithDelay, startWithDelay); + const progress = differenceInMinutes(now, startWithDelay); return total && (progress / total) * 100; } - private _getStatus(now: Date, end?: Date, start?: Date): Status { - if (start && end && isBefore(start, now) && isAfter(end, now)) { + private _getStatus(now: Date, start?: Time, end?: Time): Status { + const startWithDelay = start && start.time && addMinutes(start.time, start.delay ?? 0); + const endWithDelay = end && end.time && addMinutes(end.time, end.delay ?? 0); + + if ( + startWithDelay && + isBefore(startWithDelay, now) && + endWithDelay && + isAfter(endWithDelay, now) + ) { return 'progress'; - } else if (end && isBefore(end, now)) { + } else if (endWithDelay && isBefore(endWithDelay, now)) { return 'past'; } return 'future'; } - private _renderPosition(now: Date, start?: Date, end?: Date): TemplateResult | undefined { + private _renderPosition(now: Date, start?: Time, end?: Time): TemplateResult | undefined { const currentPosition = this._getProgress(now, start, end); if (currentPosition < 0 && currentPosition > 100) return undefined; @@ -132,9 +147,18 @@ export class SbbPearlChainElement extends LitElement { const departureTime = rideLegs?.length && removeTimezoneFromISOTimeString(rideLegs[0]?.departure?.time); + const departureWithDelay = departureTime && { + time: departureTime, + delay: rideLegs[0].departure.delay ?? 0, + }; + const arrivalTime = rideLegs?.length && - removeTimezoneFromISOTimeString(rideLegs[rideLegs?.length - 1].arrival?.time); + removeTimezoneFromISOTimeString(rideLegs[rideLegs.length - 1].arrival?.time); + const arrivalTimeDelay = arrivalTime && { + time: arrivalTime, + delay: rideLegs[rideLegs.length - 1]?.arrival.delay ?? 0, + }; const departureNotServiced = ((): string => { return rideLegs && @@ -165,14 +189,17 @@ export class SbbPearlChainElement extends LitElement { : ''; })(); + const status = + departureWithDelay && + arrivalTimeDelay && + this._getStatus(now, departureWithDelay, arrivalTimeDelay); + const statusClassDeparture = - rideLegs && departureTime && arrivalTime && !departureCancelClass - ? 'sbb-pearl-chain__bullet--' + this._getStatus(now, arrivalTime, departureTime) - : ''; + rideLegs && status && !departureCancelClass ? 'sbb-pearl-chain__bullet--' + status : ''; const statusClassArrival = - rideLegs && arrivalTime && !arrivalCancelClass - ? 'sbb-pearl-chain__bullet--' + this._getStatus(now, arrivalTime) + rideLegs && status && !arrivalCancelClass + ? 'sbb-pearl-chain__bullet--' + this._getStatus(now, undefined, arrivalTimeDelay) : ''; if (this._isAllCancelled(rideLegs)) { @@ -219,31 +246,35 @@ export class SbbPearlChainElement extends LitElement { const cancelled = serviceAlteration?.cancelled ? 'sbb-pearl-chain__leg--disruption' : ''; + const legDepartureWithDelay = { time: departure, delay: leg.departure?.delay ?? 0 }; + const legArrivalWithDelay = { time: arrival, delay: leg.arrival?.delay ?? 0 }; + const status = this._getStatus(now, legDepartureWithDelay, legArrivalWithDelay); + const legStatus = !cancelled && - this._getStatus(now, departure, arrival) && - 'sbb-pearl-chain__leg--' + this._getStatus(now, arrival, departure); - + !skippedLeg && + this._getStatus(now, legDepartureWithDelay, legArrivalWithDelay) && + 'sbb-pearl-chain__leg--' + status; const legStyle = (): Record => { return { '--sbb-pearl-chain-leg-width': `${duration}%`, - ...(this._getStatus(now, arrival, departure) === 'progress' && !cancelled + ...(status === 'progress' && !cancelled && !skippedLeg ? { - '--sbb-pearl-chain-leg-status': `${this._getProgress(now, departure, arrival)}%`, + '--sbb-pearl-chain-leg-status': `${this._getProgress(now, legDepartureWithDelay, legArrivalWithDelay)}%`, } : {}), }; }; return html`
${index > 0 && index < rideLegs.length ? html`` : nothing} - ${this._getStatus(now, arrival, departure) === 'progress' && !cancelled - ? this._renderPosition(now, departure, arrival) + ${status === 'progress' && !cancelled && !skippedLeg + ? this._renderPosition(now, legDepartureWithDelay, legArrivalWithDelay) : nothing}
`; })} diff --git a/src/elements-experimental/timetable-row/timetable-row.sample-data.ts b/src/elements-experimental/timetable-row/timetable-row.sample-data.ts index 6b52a42bb4..9e512ea1f9 100644 --- a/src/elements-experimental/timetable-row/timetable-row.sample-data.ts +++ b/src/elements-experimental/timetable-row/timetable-row.sample-data.ts @@ -1,9 +1,13 @@ import type { ITripItem } from '../core/timetable.js'; import { cancelledLeg, + defaultBusLeg, + defaultShipLeg, + defaultTramLeg, extendedLeg, futureLeg, longFutureLeg, + pastBusLeg, pastLeg, progressLeg, redirectedOnArrivalLeg, @@ -286,7 +290,7 @@ export const disturbanceTrip: DeepPartial = { }; export const quayChangeTrip: DeepPartial = { - legs: [pastLeg, progressLeg], + legs: [pastBusLeg, progressLeg], notices: [ { name: 'R', @@ -366,7 +370,7 @@ export const trainTrip: DeepPartial = { }; export const busTrip: DeepPartial = { - legs: [futureLeg, futureLeg, longFutureLeg], + legs: [defaultBusLeg, futureLeg, longFutureLeg], situations: [], summary: { duration: 41, @@ -398,7 +402,7 @@ export const busTrip: DeepPartial = { }; export const shipTrip: DeepPartial = { - legs: [futureLeg], + legs: [defaultShipLeg], situations: [], summary: { duration: 41, @@ -423,7 +427,7 @@ export const shipTrip: DeepPartial = { }; export const walkTimeTrip: DeepPartial = { - legs: [futureLeg, futureLeg, futureLeg, futureLeg, futureLeg, futureLeg], + legs: [defaultTramLeg, futureLeg, futureLeg, futureLeg, futureLeg, futureLeg], notices: [ { name: 'NF', diff --git a/src/elements-experimental/timetable-row/timetable-row.snapshot.spec.ts b/src/elements-experimental/timetable-row/timetable-row.snapshot.spec.ts index e503ec6fe1..ec2d3f85b9 100644 --- a/src/elements-experimental/timetable-row/timetable-row.snapshot.spec.ts +++ b/src/elements-experimental/timetable-row/timetable-row.snapshot.spec.ts @@ -6,7 +6,7 @@ import { html } from 'lit/static-html.js'; import type { ITripItem } from '../core/timetable.js'; import type { SbbTimetableRowElement } from './timetable-row.js'; -import { busTrip, defaultTrip } from './timetable-row.sample-data.js'; +import { busTrip, defaultTrip, trainTrip } from './timetable-row.sample-data.js'; import './timetable-row.js'; @@ -105,14 +105,120 @@ describe(`sbb-timetable-row`, () => { }); }); + describe('sbb-timetable-row with platform', () => { + it('renders component with config', async () => { + element = await fixture( + html``, + ); + + expect(element).dom.to.be.equal(` + + + `); + + expect(element).shadowDom.to.be.equal(` + + + Departure: 16:30, on Pl. 4, Train, IR 35, Direction Chur, Arrival: 17:06, Travel time 41 Minutes, 2 changes, First Class Low to medium occupancy expected. Second Class High occupancy expected. + +
+
+
+ + + + Train + + + + + + + +
+

+ Direction Chur +

+
+ + + +
+
+ `); + }); + }); + describe('sbb-timetable-row with BusTrip', () => { it('renders component with config', async () => { element = await fixture( html``, ); - await waitForLitRender(element); - expect(element).dom.to.be.equal(` @@ -121,7 +227,7 @@ describe(`sbb-timetable-row`, () => { expect(element).shadowDom.to.be.equal(` - Departure: 16:30, from Stand 4, Bus, B 19, Direction Spiegel, Blinzern, Arrival: 17:06, Travel time 41 Minutes, 2 changes, First Class Low to medium occupancy expected. Second Class High occupancy expected. + Departure: 16:30, on Stand 4, Bus, B 19, Direction Spiegel, Blinzern, Arrival: 17:06, Travel time 41 Minutes, 2 changes, First Class Low to medium occupancy expected. Second Class High occupancy expected.
@@ -148,7 +254,7 @@ describe(`sbb-timetable-row`, () => { - from Stand + on Stand