html`
`;
-const TemplateNoSlot = ({ trip, tripBack, ...args }: Args): TemplateResult =>
+const TemplateNoSlot = ({ trip, tripBack, now, ...args }: Args): TemplateResult =>
html``;
diff --git a/src/elements-experimental/journey-summary/journey-summary.ts b/src/elements-experimental/journey-summary/journey-summary.ts
index f3313513b4..7378fa8a2b 100644
--- a/src/elements-experimental/journey-summary/journey-summary.ts
+++ b/src/elements-experimental/journey-summary/journey-summary.ts
@@ -1,6 +1,7 @@
import { SbbLanguageController } from '@sbb-esta/lyne-elements/core/controllers.js';
-import { defaultDateAdapter, readDataNow } from '@sbb-esta/lyne-elements/core/datetime.js';
+import { defaultDateAdapter } from '@sbb-esta/lyne-elements/core/datetime.js';
import { i18nTripDuration } from '@sbb-esta/lyne-elements/core/i18n.js';
+import type { SbbDateLike } from '@sbb-esta/lyne-elements/core/interfaces/types';
import type { SbbTitleLevel } from '@sbb-esta/lyne-elements/title.js';
import { format, isValid } from 'date-fns';
import type { CSSResultGroup, TemplateResult } from 'lit';
@@ -59,6 +60,16 @@ export class SbbJourneySummaryElement extends LitElement {
*/
@property({ attribute: 'disable-animation', type: Boolean }) public disableAnimation?: boolean;
+ /** A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. */
+ @property()
+ public set now(value: SbbDateLike | undefined) {
+ this._now = defaultDateAdapter.getValidDateOrNull(defaultDateAdapter.deserialize(value));
+ }
+ public get now(): Date {
+ return this._now ?? new Date();
+ }
+ private _now: Date | null = null;
+
private _hasContentSlot: boolean = false;
private _language = new SbbLanguageController(this);
@@ -67,11 +78,6 @@ export class SbbJourneySummaryElement extends LitElement {
this._hasContentSlot = Boolean(this.querySelector?.('[slot="content"]'));
}
- private _now(): number {
- const dataNow = readDataNow(this);
- return isNaN(dataNow) ? Date.now() : dataNow;
- }
-
/** renders the date of the journey or if it is the current or next day */
private _renderJourneyStart(
departureTime: Date | undefined,
@@ -132,7 +138,7 @@ export class SbbJourneySummaryElement extends LitElement {
.arrivalWalk=${arrivalWalk}
.legs=${legs}
.disableAnimation=${this.disableAnimation}
- data-now=${this._now()}
+ .now=${this.now}
>
`;
diff --git a/src/elements-experimental/journey-summary/readme.md b/src/elements-experimental/journey-summary/readme.md
index 471cf86d84..821ed040e0 100644
--- a/src/elements-experimental/journey-summary/readme.md
+++ b/src/elements-experimental/journey-summary/readme.md
@@ -16,17 +16,22 @@ If the tripBack prop is passed to the component a second journey-summary, withou
```
+To simulate the current datetime, you can use the `now` property,
+which accepts a `Date` or a timestamp in milliseconds (as number or string).
+This is helpful if you need a specific state of the component.
+
## Properties
-| Name | Attribute | Privacy | Type | Default | Description |
-| ------------------ | ------------------- | ------- | --------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------- |
-| `disableAnimation` | `disable-animation` | public | `boolean \| undefined` | | Per default, the current location has a pulsating animation. You can disable the animation with this property. |
-| `headerLevel` | `header-level` | public | `SbbTitleLevel` | `'3'` | Heading level of the journey header element (e.g. h1-h6). |
-| `roundTrip` | `round-trip` | public | `boolean \| undefined` | | The RoundTrip prop. This prop controls if one or two arrows are displayed in the header. |
-| `trip` | `trip` | public | `InterfaceSbbJourneySummaryAttributes` | | The trip prop |
-| `tripBack` | `trip-back` | public | `InterfaceSbbJourneySummaryAttributes \| undefined` | | The tripBack prop |
+| Name | Attribute | Privacy | Type | Default | Description |
+| ------------------ | ------------------- | ------- | --------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- |
+| `disableAnimation` | `disable-animation` | public | `boolean \| undefined` | | Per default, the current location has a pulsating animation. You can disable the animation with this property. |
+| `headerLevel` | `header-level` | public | `SbbTitleLevel` | `'3'` | Heading level of the journey header element (e.g. h1-h6). |
+| `now` | `now` | public | `Date` | `null` | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. |
+| `roundTrip` | `round-trip` | public | `boolean \| undefined` | | The RoundTrip prop. This prop controls if one or two arrows are displayed in the header. |
+| `trip` | `trip` | public | `InterfaceSbbJourneySummaryAttributes` | | The trip prop |
+| `tripBack` | `trip-back` | public | `InterfaceSbbJourneySummaryAttributes \| undefined` | | The tripBack prop |
## Slots
diff --git a/src/elements-experimental/pearl-chain-time/pearl-chain-time.spec.ts b/src/elements-experimental/pearl-chain-time/pearl-chain-time.spec.ts
index 0d5d9b730d..d541e79152 100644
--- a/src/elements-experimental/pearl-chain-time/pearl-chain-time.spec.ts
+++ b/src/elements-experimental/pearl-chain-time/pearl-chain-time.spec.ts
@@ -9,7 +9,7 @@ import type { SbbPearlChainTimeElement } from './pearl-chain-time.js';
import './pearl-chain-time.js';
-const now = new Date('2022-08-16T15:00:00Z').valueOf();
+const now = '2022-08-16T15:00:00Z';
describe(`sbb-pearl-chain-time`, () => {
it('should render component with time', async () => {
@@ -17,7 +17,7 @@ describe(`sbb-pearl-chain-time`, () => {
`);
@@ -28,7 +28,7 @@ describe(`sbb-pearl-chain-time`, () => {
];
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
expect(element).shadowDom.to.be.equal(`
@@ -39,7 +39,7 @@ describe(`sbb-pearl-chain-time`, () => {
12:00
-
+
`);
@@ -67,7 +67,7 @@ describe(`sbb-pearl-chain-time`, () => {
];
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
expect(element).shadowDom.to.be.equal(`
@@ -95,7 +95,7 @@ describe(`sbb-pearl-chain-time`, () => {
12:00
-
+
`);
@@ -123,7 +123,7 @@ describe(`sbb-pearl-chain-time`, () => {
];
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
expect(element).shadowDom.to.be.equal(`
@@ -134,7 +134,7 @@ describe(`sbb-pearl-chain-time`, () => {
12:00
-
+
`);
@@ -180,7 +180,7 @@ describe(`sbb-pearl-chain-time`, () => {
];
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
expect(element).shadowDom.to.be.equal(`
@@ -208,7 +208,7 @@ describe(`sbb-pearl-chain-time`, () => {
12:00
-
+
```
-## Testing
-
-To specify a specific date for the current datetime, you can use the `data-now` attribute (timestamp in milliseconds).
+To simulate the current datetime, you can use the `now` property,
+which accepts a `Date` or a timestamp in milliseconds (as number or string).
This is helpful if you need a specific state of the component.
@@ -62,3 +61,4 @@ This is helpful if you need a specific state of the component.
| `departureWalk` | `departure-walk` | public | `number \| undefined` | | Optional prop to render the walk time (in minutes) before departure |
| `disableAnimation` | `disable-animation` | public | `boolean \| undefined` | | Per default, the current location has a pulsating animation. You can disable the animation with this property. |
| `legs` | `legs` | public | `(Leg \| PtRideLeg)[]` | | define the legs of the pearl-chain. Format: `{"legs": \[{"duration": 25}, ...]}` `duration` in minutes. Duration of the leg is relative to the total travel time. Example: departure 16:30, change at 16:40, arrival at 17:00. So the change should have a duration of 33.33%. |
+| `now` | `now` | public | `Date` | `null` | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. |
diff --git a/src/elements-experimental/pearl-chain/pearl-chain.stories.ts b/src/elements-experimental/pearl-chain/pearl-chain.stories.ts
index 22eea67c4b..c8c2a34a6f 100644
--- a/src/elements-experimental/pearl-chain/pearl-chain.stories.ts
+++ b/src/elements-experimental/pearl-chain/pearl-chain.stories.ts
@@ -1,7 +1,7 @@
import type { InputType } from '@storybook/types';
import type { Meta, StoryObj, ArgTypes, Args } from '@storybook/web-components';
import isChromatic from 'chromatic/isChromatic';
-import type { TemplateResult } from 'lit';
+import { nothing, type TemplateResult } from 'lit';
import { html } from 'lit';
import { sbbSpread } from '../../storybook/helpers/spread.js';
@@ -32,16 +32,20 @@ const now: InputType = {
const defaultArgTypes: ArgTypes = {
'disable-animation': disableAnimation,
- 'data-now': now,
+ now,
};
const defaultArgs: Args = {
'disable-animation': isChromatic(),
- 'data-now': new Date('2022-12-01T12:11:00').valueOf(),
+ now: new Date('2022-12-01T12:11:00').valueOf(),
};
-const Template = ({ legs, ...args }: Args): TemplateResult => {
- return html``;
+const Template = ({ legs, now, ...args }: Args): TemplateResult => {
+ return html``;
};
export const NoStops: StoryObj = {
@@ -86,7 +90,7 @@ export const withPosition: StoryObj = {
args: {
...defaultArgs,
legs: [progressLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
@@ -96,7 +100,7 @@ export const Past: StoryObj = {
args: {
...defaultArgs,
legs: [pastLeg, pastLeg],
- 'data-now': new Date('2023-11-01T12:11:00').valueOf(),
+ now: new Date('2023-11-01T12:11:00').valueOf(),
},
};
@@ -106,7 +110,7 @@ export const DepartureStopSkipped: StoryObj = {
args: {
...defaultArgs,
legs: [pastLeg, progressLeg, longFutureLeg, redirectedOnDepartureLeg, futureLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
@@ -116,7 +120,7 @@ export const ArrivalStopSkipped: StoryObj = {
args: {
...defaultArgs,
legs: [pastLeg, progressLeg, longFutureLeg, redirectedOnArrivalLeg, futureLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
@@ -126,7 +130,7 @@ export const FirstStopSkipped: StoryObj = {
args: {
...defaultArgs,
legs: [redirectedOnDepartureLeg, futureLeg, longFutureLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
@@ -136,7 +140,7 @@ export const LastStopSkipped: StoryObj = {
args: {
...defaultArgs,
legs: [futureLeg, longFutureLeg, redirectedOnArrivalLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
@@ -146,7 +150,7 @@ export const Mixed: StoryObj = {
args: {
...defaultArgs,
legs: [pastLeg, progressLeg, longFutureLeg, cancelledLeg, futureLeg],
- 'data-now': new Date('2022-12-05T12:11:00').valueOf(),
+ now: new Date('2022-12-05T12:11:00').valueOf(),
},
};
diff --git a/src/elements-experimental/pearl-chain/pearl-chain.ts b/src/elements-experimental/pearl-chain/pearl-chain.ts
index e1d9637f58..dee8c6bf77 100644
--- a/src/elements-experimental/pearl-chain/pearl-chain.ts
+++ b/src/elements-experimental/pearl-chain/pearl-chain.ts
@@ -1,4 +1,5 @@
-import { readDataNow } from '@sbb-esta/lyne-elements/core/datetime.js';
+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 type { CSSResultGroup, TemplateResult } from 'lit';
import { html, LitElement, nothing } from 'lit';
@@ -36,10 +37,15 @@ export class SbbPearlChainElement extends LitElement {
*/
@property({ attribute: 'disable-animation', type: Boolean }) public disableAnimation?: boolean;
- private _now(): number {
- const dataNow = readDataNow(this);
- return isNaN(dataNow) ? Date.now() : dataNow;
+ /** A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. */
+ @property()
+ public set now(value: SbbDateLike | undefined) {
+ this._now = defaultDateAdapter.getValidDateOrNull(defaultDateAdapter.deserialize(value));
}
+ public get now(): Date | null {
+ return this._now;
+ }
+ private _now: Date | null = null;
private _getAllDuration(legs: PtRideLeg[]): number {
return legs?.reduce((sum: number, leg) => {
@@ -72,34 +78,36 @@ export class SbbPearlChainElement extends LitElement {
);
const allDurations = this._getAllDuration(legs);
- if (allDurations === 0) return 100;
+ if (allDurations === 0) {
+ return 100;
+ }
return (duration / allDurations) * 100;
}
return 0;
}
- private _getProgress(start?: Date, end?: Date): number {
+ private _getProgress(now: Date, start?: Date, end?: Date): number {
if (!start || !end) {
return 0;
}
const total = differenceInMinutes(end, start);
- const progress = differenceInMinutes(this._now(), start);
+ const progress = differenceInMinutes(now, start);
return total && (progress / total) * 100;
}
- private _getStatus(end?: Date, start?: Date): Status {
- if (start && end && isBefore(start, this._now()) && isAfter(end, this._now())) {
+ private _getStatus(now: Date, end?: Date, start?: Date): Status {
+ if (start && end && isBefore(start, now) && isAfter(end, now)) {
return 'progress';
- } else if (end && isBefore(end, this._now())) {
+ } else if (end && isBefore(end, now)) {
return 'past';
}
return 'future';
}
- private _renderPosition(start?: Date, end?: Date): TemplateResult | undefined {
- const currentPosition = this._getProgress(start, end);
+ private _renderPosition(now: Date, start?: Date, end?: Date): TemplateResult | undefined {
+ const currentPosition = this._getProgress(now, start, end);
if (currentPosition < 0 && currentPosition > 100) return undefined;
const statusStyle = (): Record => {
@@ -118,6 +126,8 @@ export class SbbPearlChainElement extends LitElement {
}
protected override render(): TemplateResult {
+ const now = this.now ?? new Date();
+
const rideLegs: PtRideLeg[] = this.legs?.filter((leg) => isRideLeg(leg)) as PtRideLeg[];
const departureTime =
@@ -157,12 +167,12 @@ export class SbbPearlChainElement extends LitElement {
const statusClassDeparture =
rideLegs && departureTime && arrivalTime && !departureCancelClass
- ? 'sbb-pearl-chain__bullet--' + this._getStatus(arrivalTime, departureTime)
+ ? 'sbb-pearl-chain__bullet--' + this._getStatus(now, arrivalTime, departureTime)
: '';
const statusClassArrival =
rideLegs && arrivalTime && !arrivalCancelClass
- ? 'sbb-pearl-chain__bullet--' + this._getStatus(arrivalTime)
+ ? 'sbb-pearl-chain__bullet--' + this._getStatus(now, arrivalTime)
: '';
if (this._isAllCancelled(rideLegs)) {
@@ -211,14 +221,16 @@ export class SbbPearlChainElement extends LitElement {
const legStatus =
!cancelled &&
- this._getStatus(departure, arrival) &&
- 'sbb-pearl-chain__leg--' + this._getStatus(arrival, departure);
+ this._getStatus(now, departure, arrival) &&
+ 'sbb-pearl-chain__leg--' + this._getStatus(now, arrival, departure);
const legStyle = (): Record => {
return {
'--sbb-pearl-chain-leg-width': `${duration}%`,
- ...(this._getStatus(arrival, departure) === 'progress' && !cancelled
- ? { '--sbb-pearl-chain-leg-status': `${this._getProgress(departure, arrival)}%` }
+ ...(this._getStatus(now, arrival, departure) === 'progress' && !cancelled
+ ? {
+ '--sbb-pearl-chain-leg-status': `${this._getProgress(now, departure, arrival)}%`,
+ }
: {}),
};
};
@@ -230,8 +242,8 @@ export class SbbPearlChainElement extends LitElement {
${index > 0 && index < rideLegs.length
? html``
: nothing}
- ${this._getStatus(arrival, departure) === 'progress' && !cancelled
- ? this._renderPosition(departure, arrival)
+ ${this._getStatus(now, arrival, departure) === 'progress' && !cancelled
+ ? this._renderPosition(now, departure, arrival)
: nothing}
`;
})}
diff --git a/src/elements-experimental/pearl-chain/readme.md b/src/elements-experimental/pearl-chain/readme.md
index 4b9532c8ae..5b0cd0cce2 100644
--- a/src/elements-experimental/pearl-chain/readme.md
+++ b/src/elements-experimental/pearl-chain/readme.md
@@ -46,9 +46,8 @@ The `legs` property is mandatory.
```
-## Testing
-
-To specify a specific date for the current datetime, you can use the `data-now` attribute (timestamp in milliseconds).
+To simulate the current datetime, you can use the `now` property,
+which accepts a `Date` or a timestamp in milliseconds (as number or string).
This is helpful if you need a specific state of the component.
@@ -59,3 +58,4 @@ This is helpful if you need a specific state of the component.
| ------------------ | ------------------- | ------- | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `disableAnimation` | `disable-animation` | public | `boolean \| undefined` | | Per default, the current location has a pulsating animation. You can disable the animation with this property. |
| `legs` | `legs` | public | `(Leg \| PtRideLeg)[] \| undefined` | | Define the legs of the pearl-chain. Format: `{"legs": \[{"duration": 25}, ...]}` `duration` in minutes. Duration of the leg is relative to the total travel time. Example: departure 16:30, change at 16:40, arrival at 17:00. So the change should have a duration of 33.33%. |
+| `now` | `now` | public | `Date \| null` | `null` | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. |
diff --git a/src/elements-experimental/timetable-row/readme.md b/src/elements-experimental/timetable-row/readme.md
index dc8c2d4404..ea4ac293c8 100644
--- a/src/elements-experimental/timetable-row/readme.md
+++ b/src/elements-experimental/timetable-row/readme.md
@@ -39,9 +39,8 @@ _`tripProp` property_
```
-## Testing
-
-To specify a specific date for the current datetime, you can use the `data-now` attribute (timestamp in milliseconds).
+To simulate the current datetime, you can use the `now` property,
+which accepts a `Date` or a timestamp in milliseconds (as number or string).
This is helpful if you need a specific state of the component.
@@ -57,5 +56,6 @@ This is helpful if you need a specific state of the component.
| `disableAnimation` | `disable-animation` | public | `boolean \| undefined` | | This will be forwarded to the sbb-pearl-chain component - if true the position won't be animated. |
| `loadingPrice` | `loading-price` | public | `boolean` | `false` | The loading state - when this is true it will be render skeleton with an idling animation |
| `loadingTrip` | `loading-trip` | public | `boolean` | `false` | The loading state - when this is true it will be render skeleton with an idling animation |
+| `now` | `now` | public | `Date` | `null` | A configured date which acts as the current date instead of the real current date. Recommended for testing purposes. |
| `price` | `price` | public | `Price \| undefined` | | The price Prop, which consists of the data for the badge. |
| `trip` | `trip` | public | `ITripItem` | | The trip Prop. |
diff --git a/src/elements-experimental/timetable-row/timetable-row.spec.ts b/src/elements-experimental/timetable-row/timetable-row.spec.ts
index 4098f3d001..9c07676b8b 100644
--- a/src/elements-experimental/timetable-row/timetable-row.spec.ts
+++ b/src/elements-experimental/timetable-row/timetable-row.spec.ts
@@ -14,20 +14,20 @@ import {
walkTimeTrip,
} from './timetable-row.sample-data.js';
-const now = new Date('2022-08-16T15:00:00Z').valueOf();
+const now = '2022-08-16T15:00:00Z';
describe(`sbb-timetable-row`, () => {
let element: SbbTimetableRowElement;
describe('sbb-timetable-row with defaultTrip', () => {
it('renders component with config', async () => {
- element = await fixture(html``);
+ element = await fixture(html``);
element.trip = defaultTrip as ITripItem;
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
@@ -87,7 +87,6 @@ describe(`sbb-timetable-row`, () => {
@@ -112,13 +111,13 @@ describe(`sbb-timetable-row`, () => {
describe('sbb-timetable-row with BusTrip', () => {
it('renders component with config', async () => {
- element = await fixture(html``);
+ element = await fixture(html``);
element.trip = busTrip as ITripItem;
await waitForLitRender(element);
expect(element).dom.to.be.equal(`
-
+
`);
@@ -144,7 +143,7 @@ describe(`sbb-timetable-row`, () => {
Direction Spiegel, Blinzern
-
+