Skip to content

Commit

Permalink
Merge pull request #284 from wmde/live-time-formatting
Browse files Browse the repository at this point in the history
  • Loading branch information
Abban authored Nov 22, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents fb98c49 + 5127981 commit 1d71839
Showing 20 changed files with 180 additions and 10 deletions.
15 changes: 15 additions & 0 deletions src/utils/DynamicContent/DynamicCampaignText.ts
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ import { CampaignProjection } from '@src/utils/DynamicContent/CampaignProjection
import { ImpressionCount } from '@src/utils/ImpressionCount';
import { ProgressBarContent } from '@src/utils/DynamicContent/generators/ProgressBarContent';
import { DynamicProgressBarContent } from '@src/utils/DynamicContent/DynamicProgressBarContent';
import { CurrentDateAndTime } from '@src/utils/DynamicContent/generators/CurrentDateAndTime';

export default class DynamicCampaignText implements DynamicContent {
private readonly _date: Date;
@@ -24,6 +25,7 @@ export default class DynamicCampaignText implements DynamicContent {
private _campaignTimeRange: TimeRange;
private _campaignProjection: CampaignProjection;
private _progressBarContent: ProgressBarContent;
private _currentDateAndTime: CurrentDateAndTime;

public constructor( date: Date, translator: Translator, formatters: Formatters, campaignParameters: CampaignParameters, impressionCount: ImpressionCount ) {
this._date = date;
@@ -32,6 +34,7 @@ export default class DynamicCampaignText implements DynamicContent {
this._campaignParameters = campaignParameters;
this._impressionCount = impressionCount;
this._cache = new Map<string, string>();
this.getCurrentDateAndTime = this.getCurrentDateAndTime.bind( this );
}

private getCampaignTimeRange(): TimeRange {
@@ -64,6 +67,18 @@ export default class DynamicCampaignText implements DynamicContent {
} );
}

/**
* Current time returns time to the minute, and needs to be updated dynamically
* This means we can't cache the return value, so instead manually cache the CurrentTime object
*/
public getCurrentDateAndTime(): string {
if ( !this._currentDateAndTime ) {
this._currentDateAndTime = new CurrentDateAndTime( this._translator, this._formatters.ordinal, this._formatters.time );
}

return this._currentDateAndTime.getText( new Date() );
}

public get currentDayName(): string {
return this.getCachedValue( 'currentDayName', () => {
return new DayName( this._date, this._translator ).getText();
5 changes: 5 additions & 0 deletions src/utils/DynamicContent/DynamicContent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { DynamicProgressBarContent } from '@src/utils/DynamicContent/DynamicProgressBarContent';

/**
* Properties in this interface are for items that are generated once and are then static after
* Methods are for items that need to be updated per call
*/
export interface DynamicContent {
currentDayName: string;
currentDate: string;
getCurrentDateAndTime: () => string;
daysLeftSentence: string;
campaignDaySentence: string;
visitorsVsDonorsSentence: string;
2 changes: 2 additions & 0 deletions src/utils/DynamicContent/Formatters.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Ordinal } from '@src/utils/DynamicContent/formatters/Ordinal';
import { Currency } from '@src/utils/DynamicContent/formatters/Currency';
import { Integer } from '@src/utils/DynamicContent/formatters/Integer';
import { Time } from '@src/utils/DynamicContent/formatters/Time';

export interface Formatters {
currency: Currency;
ordinal: Ordinal;
integer: Integer;
time: Time;
}
3 changes: 3 additions & 0 deletions src/utils/DynamicContent/formatters/Time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface Time {
getFormatted( date: Date ): string;
}
7 changes: 7 additions & 0 deletions src/utils/DynamicContent/formatters/TimeDe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Time } from '@src/utils/DynamicContent/formatters/Time';

export class TimeDe implements Time {
public getFormatted( date: Date ): string {
return date.toLocaleString( 'de-DE', { hour: 'numeric', minute: 'numeric' } );
}
}
7 changes: 7 additions & 0 deletions src/utils/DynamicContent/formatters/TimeEn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Time } from '@src/utils/DynamicContent/formatters/Time';

export class TimeEn implements Time {
public getFormatted( date: Date ): string {
return date.toLocaleString( 'en-GB', { hour: 'numeric', hour12: true, minute: 'numeric' } );
}
}
23 changes: 23 additions & 0 deletions src/utils/DynamicContent/generators/CurrentDateAndTime.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { TextGenerator } from '@src/utils/DynamicContent/generators/TextGenerator';
import { Translator } from '@src/Translator';
import { Ordinal } from '@src/utils/DynamicContent/formatters/Ordinal';
import { Time } from '@src/utils/DynamicContent/formatters/Time';

export class CurrentDateAndTime implements TextGenerator<Date> {
private readonly _translator: Translator;
private _ordinalFormatter: Ordinal;
private _timeFormatter: Time;

public constructor( translator: Translator, ordinalFormatter: Ordinal, timeFormatter: Time ) {
this._translator = translator;
this._ordinalFormatter = ordinalFormatter;
this._timeFormatter = timeFormatter;
}

public getText( date: Date ): string {
return this._translator.translate( 'date-month-time-' + ( date.getMonth() + 1 ), {
day: this._ordinalFormatter.getFormatted( date.getDate() ),
time: this._timeFormatter.getFormatted( date )
} );
}
}
7 changes: 5 additions & 2 deletions src/utils/DynamicContent/generators/TextGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export interface TextGenerator {
getText(): string;
/**
* @param T - Defines the type of optional parameter that can be passed to getText()
*/
export interface TextGenerator<T = void> {
getText( parameter: T ): string;
}
15 changes: 14 additions & 1 deletion src/utils/DynamicContent/messages/DynamicCampaignText.de.ts
Original file line number Diff line number Diff line change
@@ -42,7 +42,20 @@ const Translations: TranslationMessages = {
'date-month-9': '{{day}} September',
'date-month-10': '{{day}} Oktober',
'date-month-11': '{{day}} November',
'date-month-12': '{{day}} Dezember'
'date-month-12': '{{day}} Dezember',

'date-month-time-1': '{{day}} Januar, {{time}}',
'date-month-time-2': '{{day}} Februar, {{time}}',
'date-month-time-3': '{{day}} März, {{time}}',
'date-month-time-4': '{{day}} April, {{time}}',
'date-month-time-5': '{{day}} Mai, {{time}}',
'date-month-time-6': '{{day}} Juni, {{time}}',
'date-month-time-7': '{{day}} Juli, {{time}}',
'date-month-time-8': '{{day}} August, {{time}}',
'date-month-time-9': '{{day}} September, {{time}}',
'date-month-time-10': '{{day}} Oktober, {{time}}',
'date-month-time-11': '{{day}} November, {{time}}',
'date-month-time-12': '{{day}} Dezember, {{time}}'
};

export default Translations;
15 changes: 14 additions & 1 deletion src/utils/DynamicContent/messages/DynamicCampaignText.en.ts
Original file line number Diff line number Diff line change
@@ -42,7 +42,20 @@ const Translations: TranslationMessages = {
'date-month-9': 'September {{day}}',
'date-month-10': 'October {{day}}',
'date-month-11': 'November {{day}}',
'date-month-12': 'December {{day}}'
'date-month-12': 'December {{day}}',

'date-month-time-1': 'January {{day}}, {{time}}',
'date-month-time-2': 'February {{day}}, {{time}}',
'date-month-time-3': 'March {{day}}, {{time}}',
'date-month-time-4': 'April {{day}}, {{time}}',
'date-month-time-5': 'May {{day}}, {{time}}',
'date-month-time-6': 'June {{day}}, {{time}}',
'date-month-time-7': 'July {{day}}, {{time}}',
'date-month-time-8': 'August {{day}}, {{time}}',
'date-month-time-9': 'September {{day}}, {{time}}',
'date-month-time-10': 'October {{day}}, {{time}}',
'date-month-time-11': 'November {{day}}, {{time}}',
'date-month-time-12': 'December {{day}}, {{time}}'
};

export default Translations;
2 changes: 1 addition & 1 deletion src/utils/LocaleFactory.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,6 @@ import { FundsContentLoader } from '@src/utils/UseOfFunds/FundsContentLoader';

export interface LocaleFactory {
getCurrencyFormatter(): Currency;
getFormatters(): Formatters
getFormatters(): Formatters;
getUseOfFundsLoader(): FundsContentLoader;
}
4 changes: 3 additions & 1 deletion src/utils/LocaleFactory/LocaleFactoryDe.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { FundsContentLoader } from '@src/utils/UseOfFunds/FundsContentLoader';
import { OrdinalDe } from '@src/utils/DynamicContent/formatters/OrdinalDe';
import { IntegerDe } from '@src/utils/DynamicContent/formatters/IntegerDe';
import { UseOfFundsDeLoader } from '@environment/UseOfFundsDeLoader';
import { TimeDe } from '@src/utils/DynamicContent/formatters/TimeDe';

export class LocaleFactoryDe implements LocaleFactory {
private readonly _currencyFormatter: Currency;
@@ -22,7 +23,8 @@ export class LocaleFactoryDe implements LocaleFactory {
return {
currency: this._currencyFormatter,
ordinal: new OrdinalDe(),
integer: new IntegerDe()
integer: new IntegerDe(),
time: new TimeDe()
};
}

4 changes: 3 additions & 1 deletion src/utils/LocaleFactory/LocaleFactoryEn.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { FundsContentLoader } from '@src/utils/UseOfFunds/FundsContentLoader';
import { OrdinalEn } from '@src/utils/DynamicContent/formatters/OrdinalEn';
import { IntegerEn } from '@src/utils/DynamicContent/formatters/IntegerEn';
import { UseOfFundsEnLoader } from '@environment/UseOfFundsEnLoader';
import { TimeEn } from '@src/utils/DynamicContent/formatters/TimeEn';

export class LocaleFactoryEn implements LocaleFactory {
private readonly _currencyFormatter: Currency;
@@ -22,7 +23,8 @@ export class LocaleFactoryEn implements LocaleFactory {
return {
currency: this._currencyFormatter,
ordinal: new OrdinalEn(),
integer: new IntegerEn()
integer: new IntegerEn(),
time: new TimeEn()
};
}

4 changes: 3 additions & 1 deletion src/utils/LocaleFactory/LocaleFactoryWpDe.ts
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import { FundsContentLoader } from '@src/utils/UseOfFunds/FundsContentLoader';
import { OrdinalDe } from '@src/utils/DynamicContent/formatters/OrdinalDe';
import { IntegerDe } from '@src/utils/DynamicContent/formatters/IntegerDe';
import { DeJSONFundsContentLoader } from '@src/utils/UseOfFunds/DeJSONFundsContentLoader';
import { TimeDe } from '@src/utils/DynamicContent/formatters/TimeDe';

export class LocaleFactoryWpDe implements LocaleFactory {
private readonly _currencyFormatter: Currency;
@@ -22,7 +23,8 @@ export class LocaleFactoryWpDe implements LocaleFactory {
return {
currency: this._currencyFormatter,
ordinal: new OrdinalDe(),
integer: new IntegerDe()
integer: new IntegerDe(),
time: new TimeDe()
};
}

1 change: 1 addition & 0 deletions test/banners/dynamicCampaignContent.ts
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ export function newDynamicContent(): DynamicContent {
return {
campaignDaySentence: '',
currentDate: '',
getCurrentDateAndTime: () => '',
currentDayName: '',
daysLeftSentence: 'daysLeftSentence',
donorsNeededSentence: '',
1 change: 1 addition & 0 deletions test/components/ProgressBar/ProgressBar.spec.ts
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ describe( 'ProgressBar.vue', () => {
const dynamicCampaignContent: DynamicContent = {
campaignDaySentence: '',
currentDate: '',
getCurrentDateAndTime: () => '',
currentDayName: '',
daysLeftSentence: 'daysLeftSentence',
donorsNeededSentence: '',
18 changes: 16 additions & 2 deletions test/unit/utils/DynamicContent/DynamicCampaignText.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { beforeEach, describe, expect, it } from 'vitest';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import DynamicCampaignText from '@src/utils/DynamicContent/DynamicCampaignText';
import { Translator } from '@src/Translator';
import { Formatters } from '@src/utils/DynamicContent/Formatters';
@@ -8,10 +8,12 @@ import { IntegerEn } from '@src/utils/DynamicContent/formatters/IntegerEn';
import { CampaignParameters } from '@src/domain/CampaignParameters';
import { DynamicContent } from '@src/utils/DynamicContent/DynamicContent';
import { ImpressionCount } from '@src/utils/ImpressionCount';
import { TimeEn } from '@src/utils/DynamicContent/formatters/TimeEn';

const translator = new Translator( {
'campaign-day-nth-day': 'campaign day {{days}}',
'date-month-11': 'current month {{day}}',
'date-month-time-11': 'current month {{day}} current time {{time}}',
'day-name-friday': 'current day name',
'prefix-days-left': 'only',
'suffix-days-left': 'left',
@@ -22,7 +24,7 @@ const translator = new Translator( {
'amount-total': 'Progress total',
'missing-amount': 'Progress missing'
} );
const formatters: Formatters = { currency: new CurrencyEn(), ordinal: new OrdinalEn(), integer: new IntegerEn() };
const formatters: Formatters = { currency: new CurrencyEn(), ordinal: new OrdinalEn(), integer: new IntegerEn(), time: new TimeEn() };
const campaignParameters: CampaignParameters = {
campaignProjection: {
averageAmountPerDonation: 20,
@@ -50,6 +52,7 @@ describe( 'DynamicCampaignText', () => {
let dynamicCampaignText: DynamicContent;

beforeEach( () => {
vi.useFakeTimers();
dynamicCampaignText = new DynamicCampaignText(
new Date( 2023, 10, 10, 12, 0, 0 ),
translator,
@@ -59,6 +62,10 @@ describe( 'DynamicCampaignText', () => {
);
} );

afterEach( () => {
vi.useRealTimers();
} );

it( 'Gets the campaign day sentence', () => {
expect( dynamicCampaignText.campaignDaySentence ).toBe( 'campaign day 8th' );
} );
@@ -67,6 +74,13 @@ describe( 'DynamicCampaignText', () => {
expect( dynamicCampaignText.currentDate ).toBe( 'current month 10th' );
} );

it( 'Gets the current time', () => {
vi.setSystemTime( new Date( 2023, 10, 10, 13, 42, 0 ) );
// In some test environments the output of Date.toLocaleString includes the code for a space,
// but in others it doesn't. To fix that we manually replace it in this test
expect( dynamicCampaignText.getCurrentDateAndTime().replace( '\u202f', ' ' ) ).toBe( 'current month 10th current time 1:42 pm' );
} );

it( 'Gets the current day name', () => {
expect( dynamicCampaignText.currentDayName ).toBe( 'current day name' );
} );
11 changes: 11 additions & 0 deletions test/unit/utils/DynamicContent/formatters/TimeDe.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it } from 'vitest';
import { TimeDe } from '@src/utils/DynamicContent/formatters/TimeDe';

describe( 'TimeDe', () => {
it( 'returns the formatted time', () => {
const time = new TimeDe();
const date = new Date( 2023, 11, 1, 13, 42, 12 );

expect( time.getFormatted( date ) ).toBe( '13:42' );
} );
} );
13 changes: 13 additions & 0 deletions test/unit/utils/DynamicContent/formatters/TimeEn.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { describe, expect, it } from 'vitest';
import { TimeEn } from '@src/utils/DynamicContent/formatters/TimeEn';

describe( 'TimeEn', () => {
it( 'returns the formatted time', () => {
const time = new TimeEn();
const date = new Date( 2023, 11, 1, 13, 42, 12 );

// In some test environments the output of Date.toLocaleString includes the code for a space,
// but in others it doesn't. To fix that we manually replace it in this test
expect( time.getFormatted( date ).replace( '\u202f', ' ' ) ).toStrictEqual( '1:42 pm' );
} );
} );
33 changes: 33 additions & 0 deletions test/unit/utils/DynamicContent/generators/CurrentTime.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { describe, expect, test } from 'vitest';
import { Translator } from '@src/Translator';
import { CurrentDateAndTime } from '@src/utils/DynamicContent/generators/CurrentDateAndTime';
import { Ordinal } from '@src/utils/DynamicContent/formatters/Ordinal';
import { Time } from '@src/utils/DynamicContent/formatters/Time';

const translator = new Translator( {
'date-month-time-1': 'Ick {{day}} - {{time}}',
'date-month-time-2': 'Offle {{day}} - {{time}}',
'date-month-time-10': 'Spune {{day}} - {{time}}',
'date-month-time-11': 'Sektober {{day}} - {{time}}'
} );

const staticOrdinal: Ordinal = {
getFormatted: ( figure: number ) => figure + 'sth'
};

const staticTime: Time = {
getFormatted: ( date: Date ) => `${date.getHours()} ${date.getMinutes()}`
};

describe( 'CurrentTime', () => {
test.each( [
[ 1, 14, 13, 42, 'Ick 14sth - 13 42' ],
[ 2, 27, 8, 16, 'Offle 27sth - 8 16' ],
[ 10, 2, 1, 59, 'Spune 2sth - 1 59' ],
[ 11, 8, 23, 0, 'Sektober 8sth - 23 0' ]
] )( 'returns the proper month name, date, and time', ( month: number, day: number, hours: number, minutes: number, expected: string ) => {
const currentDate = new CurrentDateAndTime( translator, staticOrdinal, staticTime );

expect( currentDate.getText( new Date( 2023, month - 1, day, hours, minutes, 0 ) ) ).toEqual( expected );
} );
} );

0 comments on commit 1d71839

Please sign in to comment.