diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/banner_var.ts b/banners/desktop/C24_WMDE_Desktop_DE_11/banner_var.ts
index 51ffcb4de..ba0d84d52 100644
--- a/banners/desktop/C24_WMDE_Desktop_DE_11/banner_var.ts
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/banner_var.ts
@@ -1,9 +1,9 @@
import { createVueApp } from '@src/createVueApp';
-import './styles/styles.scss';
+import './styles/styles_var.scss';
import BannerConductor from '@src/components/BannerConductor/FallbackBannerConductor.vue';
-import Banner from './components/BannerCtrl.vue';
+import Banner from './components/BannerVar.vue';
import FallbackBanner from './components/FallbackBanner.vue';
import { UrlRuntimeEnvironment } from '@src/utils/RuntimeEnvironment';
import { WindowResizeHandler } from '@src/utils/ResizeHandler';
@@ -21,12 +21,13 @@ import { createFallbackDonationURL } from '@src/createFallbackDonationURL';
import { LocalStorageCloseTracker } from '@src/utils/LocalCloseTracker';
// Locale-specific imports
-import messages from './messages';
+import messages from './messages_var';
import { LocaleFactoryDe } from '@src/utils/LocaleFactory/LocaleFactoryDe';
// Channel specific form setup
import { createFormItems } from './form_items';
import { createFormActions } from '@src/createFormActions';
+import { currentCampaignTimePercentage } from '@src/components/ProgressBar/currentCampaignTimePercentage';
const date = new Date();
const localeFactory = new LocaleFactoryDe();
@@ -72,5 +73,6 @@ app.provide( 'currencyFormatter', currencyFormatter );
app.provide( 'formItems', createFormItems( translator, currencyFormatter.euroAmount.bind( currencyFormatter ) ) );
app.provide( 'formActions', createFormActions( page.getTracking(), impressionCount, { afo: '1', ap: '0' } ) );
app.provide( 'tracker', tracker );
+app.provide( 'currentCampaignTimePercentage', currentCampaignTimePercentage( date, page.getCampaignParameters() ) );
app.mount( page.getBannerContainer() );
diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.vue b/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.vue
new file mode 100644
index 000000000..68e299be1
--- /dev/null
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.vue
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
onClose( 'SoftClose', CloseChoices.Close )"
+ @maybeLater="() => onClose( 'SoftClose', CloseChoices.MaybeLater )"
+ @timeOutClose="() => onClose( 'SoftClose', CloseChoices.TimeOut )"
+ @maybeLater7Days="() => onClose('SoftClose', CloseChoices.Close)"
+ />
+
+
+
+
+
+
+
+
+
+
diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/messages.ts b/banners/desktop/C24_WMDE_Desktop_DE_11/messages.ts
index 0afbded48..8bb2ea2d4 100644
--- a/banners/desktop/C24_WMDE_Desktop_DE_11/messages.ts
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/messages.ts
@@ -26,8 +26,6 @@ const messages: TranslationMessages = {
'upgrade-to-yearly-no': 'Nein, ich spende einmalig {{amount}}',
'upgrade-to-yearly-yes': 'Ja, ich spende {{amount}} jährlich',
'campaign-day-only-n-days': 'Heute sind es nur noch {{days}} Tage bis zum Ende unserer Spendenkampagne.',
- 'double-progress-close': 'Das wird knapp.',
- 'missing-amount': 'Uns fehlen noch',
'custom-amount-placeholder': 'Wahlbetrag'
};
diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/messages_var.ts b/banners/desktop/C24_WMDE_Desktop_DE_11/messages_var.ts
new file mode 100644
index 000000000..1158d8879
--- /dev/null
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/messages_var.ts
@@ -0,0 +1,35 @@
+import CustomAmountFormDe from '@src/components/DonationForm/Forms/messages/CustomAmountForm.de';
+import DynamicCampaignTextDe from '@src/utils/DynamicContent/messages/DynamicCampaignText.de';
+import { TranslationMessages } from '@src/Translator';
+import UpgradeToYearlyDe from '@src/components/DonationForm/Forms/messages/UpgradeToYearly.de';
+import AddressFormDe from '@src/components/DonationForm/Forms/messages/AddressForm.de';
+import FooterDe from '@src/components/Footer/messages/Footer.de';
+import MainDonationFormDe from '@src/components/DonationForm/Forms/messages/MainDonationForm.de';
+import FallbackBanner from '@src/components/FallbackBanner/messages/FallbackBanner.de';
+import AlreadyDonatedModal from '@src/components/AlreadyDonatedModal/translations/AlreadyDonatedModal.de';
+import SoftCloseDe from '@src/components/SoftClose/messages/SoftClose.de';
+import DoubleProgressBar from '@src/components/ProgressBar/messages/DoubleProgressBar.de';
+
+const messages: TranslationMessages = {
+ ...CustomAmountFormDe,
+ ...DynamicCampaignTextDe,
+ ...UpgradeToYearlyDe,
+ ...AddressFormDe,
+ ...FooterDe,
+ ...MainDonationFormDe,
+ ...AlreadyDonatedModal,
+ ...FallbackBanner,
+ ...SoftCloseDe,
+ ...DoubleProgressBar,
+ 'already-donated-go-away-button': 'Im Moment nicht',
+ 'soft-close-prompt': 'Dürfen wir später nochmal fragen?',
+ 'upgrade-to-yearly-copy': `
Jedes Jahr sind wir auf Menschen wie Sie angewiesen. Jährliche Spenden helfen uns besonders und ermöglichen langfristige Weiterentwicklungen.
+ Sie gehen kein Risiko ein: Jederzeit formlos zu sofort kündbar.
`,
+ 'upgrade-to-yearly-no': 'Nein, ich spende einmalig {{amount}}',
+ 'upgrade-to-yearly-yes': 'Ja, ich spende {{amount}} jährlich',
+ 'campaign-day-only-n-days': 'Heute sind es nur noch {{days}} Tage bis zum Ende unserer Spendenkampagne.',
+ 'custom-amount-placeholder': 'Wahlbetrag',
+ 'prefix-days-left': 'Noch'
+};
+
+export default messages;
diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/styles/MainBannerVar.scss b/banners/desktop/C24_WMDE_Desktop_DE_11/styles/MainBannerVar.scss
new file mode 100644
index 000000000..84ffed429
--- /dev/null
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/styles/MainBannerVar.scss
@@ -0,0 +1,53 @@
+$banner-height: 357px !default;
+$form-width: 300px !default;
+
+.wmde-banner {
+
+ .previous {
+ --slider-chevron: var( --previous-button-fill );
+ }
+
+ &-main {
+ min-height: $banner-height;
+ display: flex;
+ flex-direction: column;
+ padding: 12px 24px 0;
+ }
+
+ &-content {
+ display: flex;
+ flex-direction: row;
+ flex-grow: 1;
+ }
+
+ &-message {
+ padding: 3px 15px;
+ color: var( --message-color );
+ background-color: var( --message-background );
+ border: none;
+ border-radius: 9px;
+ margin-bottom: 5px;
+ }
+
+ &-column-left {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ flex: 1 1 auto;
+ margin-bottom: 0;
+ overflow-y: hidden;
+ margin-right: 30px;
+ padding: 0;
+ border: none;
+ }
+
+ &-column-right {
+ order: 2;
+ flex: 0 0 $form-width;
+ display: flex;
+ flex-direction: column;
+ width: $form-width;
+ padding: 10px 0 0;
+ }
+}
diff --git a/banners/desktop/C24_WMDE_Desktop_DE_11/styles/styles_var.scss b/banners/desktop/C24_WMDE_Desktop_DE_11/styles/styles_var.scss
new file mode 100644
index 000000000..6df7a6018
--- /dev/null
+++ b/banners/desktop/C24_WMDE_Desktop_DE_11/styles/styles_var.scss
@@ -0,0 +1,56 @@
+// This is the file where we import the theme-specific component styles
+@use '../../../../src/themes/Svingle/swatches/skin_default' with (
+ $slider: true,
+ $select-group: true,
+ $upgrade-to-yearly: true,
+ $fallback-banner: true,
+ $double-progress-bar: true,
+ $soft-close: true,
+);
+@use 'src/components/BannerConductor/banner-transition';
+@use 'src/themes/UseOfFunds/swatches/skin_default' as uof-default;
+@use 'Banner';
+@use 'src/themes/UseOfFunds/UseOfFunds';
+@use 'MainBannerVar' with (
+ $banner-height: 357px,
+ $form-width: 300px
+);
+@use 'src/themes/Svingle/defaults';
+@use 'src/themes/Svingle/ButtonClose/ButtonClose';
+@use 'src/themes/Svingle/ProgressBar/DoubleProgressBar';
+@use 'src/themes/Svingle/DonationForm/DonationForm';
+@use 'src/themes/Svingle/DonationForm/MultiStepDonation';
+@use 'src/themes/Svingle/DonationForm/SubComponents/SelectGroup';
+@use 'src/themes/Svingle/DonationForm/SubComponents/SelectGroupRadios' with (
+ $font-size: 14px
+);
+@use 'src/themes/Svingle/DonationForm/SubComponents/SelectCustomAmountRadio' with (
+ $font-size: 14px
+);
+@use 'src/themes/Svingle/DonationForm/SubComponents/SmsBox';
+@use 'src/themes/Svingle/DonationForm/Forms/MainDonationForm';
+@use 'src/themes/Svingle/DonationForm/Forms/UpgradeToYearlyButtonForm' with (
+ $font-size: 14px
+);
+@use 'src/themes/Svingle/DonationForm/Forms/CustomAmountForm';
+@use 'src/themes/Svingle/Footer/FooterAlreadyDonated';
+@use 'src/themes/Svingle/Footer/SelectionInput';
+@use 'src/themes/Svingle/Message/Message';
+@use 'src/themes/Svingle/SoftClose/SoftClose';
+@use 'src/themes/Svingle/Slider/KeenSlider' with (
+ $margin-bottom: 5px
+);
+
+/**
+ * Fallback banner with "Fijitiv" theme
+ * All selectors in Fijitiv theme are prefixed with the ".wmde-banner-fallback" class selector,
+ so they override the "default" styles with the same selector
+ */
+@use 'FallbackBanner';
+@use 'src/themes/Fijitiv/FallbackBanner/FallbackButton';
+@use 'src/themes/Fijitiv/FallbackBanner/LargeFooter';
+@use 'src/themes/Fijitiv/FallbackBanner/SmallFooter';
+@use 'src/themes/Fijitiv/ProgressBar/ProgressBar' as FallbackProgressBar with (
+ $progress-bar-margin: 0 15px
+);
+@use 'src/themes/Fijitiv/Slider/KeenSlider' as FallbackSlider;
diff --git a/src/components/ProgressBar/DoubleProgressBar.vue b/src/components/ProgressBar/DoubleProgressBar.vue
index 9ad5fa79d..41caf0912 100644
--- a/src/components/ProgressBar/DoubleProgressBar.vue
+++ b/src/components/ProgressBar/DoubleProgressBar.vue
@@ -1,20 +1,27 @@
-
-
{{ progressBarContent.amountNeeded }}
-
{{ progressBarContent.amountDonated }}
+
{{ progressBarContent.amountNeeded }}
+
{{ progressBarContent.amountDonated }}
!
+
{{ progressBarContent.amountNeeded }}
-
{{ $translate( 'double-progress-close' ) }}
-
{{ daysLeftSentence }}
+
+ {{ $translate( 'double-progress-close' ) }}
+ {{ daysLeftSentence }}
+
+
+ {{ daysPassedSentence }}
+
+
{{ daysLeftSentence }}
@@ -24,7 +31,7 @@
import { inject } from 'vue';
import { DynamicContent } from '@src/utils/DynamicContent/DynamicContent';
-const { progressBarContent, daysLeftSentence } = inject( 'dynamicCampaignText' );
+const { progressBarContent, daysLeftSentence, daysPassedSentence } = inject( 'dynamicCampaignText' );
const currentCampaignTimePercentage = inject( 'currentCampaignTimePercentage' );
diff --git a/src/components/ProgressBar/messages/DoubleProgressBar.de.ts b/src/components/ProgressBar/messages/DoubleProgressBar.de.ts
new file mode 100644
index 000000000..22dd66c07
--- /dev/null
+++ b/src/components/ProgressBar/messages/DoubleProgressBar.de.ts
@@ -0,0 +1,7 @@
+import { TranslationMessages } from '@src/Translator';
+
+const translations: TranslationMessages = {
+ 'double-progress-close': 'Das wird knapp.'
+};
+
+export default translations;
diff --git a/src/themes/Svingle/ProgressBar/DoubleProgressBar.scss b/src/themes/Svingle/ProgressBar/DoubleProgressBar.scss
index aeee7728a..c3eacf459 100644
--- a/src/themes/Svingle/ProgressBar/DoubleProgressBar.scss
+++ b/src/themes/Svingle/ProgressBar/DoubleProgressBar.scss
@@ -1,5 +1,4 @@
@use 'src/components/ProgressBar/DoubleProgressBar';
-@use '../variables/globals';
@use 'transitionsDouble';
.wmde-banner {
@@ -10,7 +9,6 @@
margin: 0;
font-weight: bold;
font-size: 12px;
- color: var( --color-white );
&-amount,
&-time {
@@ -21,19 +19,25 @@
&-fill,
&-difference {
+ color: var( --double-progress-color );
height: 100%;
padding: 0 10px;
}
&-fill {
display: flex;
- justify-content: space-between;
+ justify-content: right;
}
}
+ &-right-text {
+ float: right;
+ color: var( --double-progress-right-text-color );
+ }
+
&-amount {
margin-bottom: 5px;
- background: var( --color-primary-light );
+ background: var( --double-progress-time-background );
&-fill,
&-difference {
@@ -41,24 +45,30 @@
}
&-fill {
- background: var( --color-primary );
+ background: var( --double-progress-time-fill-background );
z-index: 2;
}
&-difference {
- color: var( --color-white );
- background: var( --color-primary-bright );
+ background: var( --double-progress-difference-background );
z-index: 1;
text-align: right;
}
}
&-time {
- background: var( --color-secondary-lighter );
+ background: var( --double-progress-amount-background );
&-fill {
border-radius: 12px;
- background: var( --color-secondary );
+ background: var( --double-progress-amount-fill-background );
+ }
+ }
+
+ &.is-late-progress {
+ .wmde-banner-double-progress-amount-fill,
+ .wmde-banner-double-progress-time-fill {
+ justify-content: space-between;
}
}
}
diff --git a/src/themes/Svingle/ProgressBar/_transitionsDouble.scss b/src/themes/Svingle/ProgressBar/_transitionsDouble.scss
index 5f8911cbd..e7133f121 100644
--- a/src/themes/Svingle/ProgressBar/_transitionsDouble.scss
+++ b/src/themes/Svingle/ProgressBar/_transitionsDouble.scss
@@ -7,12 +7,6 @@
transition: width 3000ms globals.$banner-easing;
width: 0;
max-width: 100%;
-
- > div {
- opacity: 0;
- transition: opacity 300ms globals.$banner-easing;
- transition-delay: 3000ms;
- }
}
&-double-progress-amount-difference,
@@ -24,6 +18,12 @@
min-width: 100px;
}
+ &-double-progress .text-fade {
+ opacity: 0;
+ transition: opacity 300ms globals.$banner-easing;
+ transition-delay: 3000ms;
+ }
+
&--visible {
.wmde-banner-double-progress-amount-difference,
.wmde-banner-double-progress-time-fill {
@@ -34,11 +34,8 @@
width: var( --wmde-banner-progress-bar-width );
}
- .wmde-banner-double-progress-amount-fill,
- .wmde-banner-double-progress-time-fill {
- > div {
- opacity: 1;
- }
+ .wmde-banner-double-progress .text-fade {
+ opacity: 1;
}
}
}
diff --git a/src/themes/Svingle/swatches/color_dark.scss b/src/themes/Svingle/swatches/color_dark.scss
index 9339f2a8d..f2a129f57 100644
--- a/src/themes/Svingle/swatches/color_dark.scss
+++ b/src/themes/Svingle/swatches/color_dark.scss
@@ -22,6 +22,7 @@ $blue800: #20303f;
@mixin swatch(
$slider: false,
$progress-bar: false,
+ $double-progress-bar: false,
$select-group: false,
$select-group-bubble: false,
$select-group-button: false,
diff --git a/src/themes/Svingle/swatches/color_light.scss b/src/themes/Svingle/swatches/color_light.scss
index 53f46a9f5..4c5b6dfee 100644
--- a/src/themes/Svingle/swatches/color_light.scss
+++ b/src/themes/Svingle/swatches/color_light.scss
@@ -10,9 +10,12 @@ $grey550: #72777d;
$grey600: #747474;
$grey700: #202122;
+$red100: #ffe8e8;
+$red500: #d32e21;
$red600: #990a00;
$red700: #7d0a00;
+$blue100: #dff1ff;
$blue600: #4465a7;
$blue650: #3366cc;
$blue700: #2a4b8d;
@@ -20,6 +23,7 @@ $blue700: #2a4b8d;
@mixin swatch(
$slider: false,
$progress-bar: false,
+ $double-progress-bar: false,
$select-group: false,
$select-group-bubble: false,
$select-group-button: false,
@@ -80,6 +84,17 @@ $blue700: #2a4b8d;
--progress-bar-fill-color: #{$white};
}
+ @if $double-progress-bar {
+ --double-progress-color: #{$white};
+ --double-progress-time-background: #{$red100};
+ --double-progress-time-fill-background: #{$red600};
+ --double-progress-difference-background: #{$red500};
+
+ --double-progress-amount-background: #{$blue100};
+ --double-progress-amount-fill-background: #{$blue700};
+ --double-progress-right-text-color: #{$grey700};
+ }
+
@if $select-group {
--select-group-background: #{$white};
--select-group-radio-label-color: #{$black};
diff --git a/src/themes/Svingle/swatches/skin_default.scss b/src/themes/Svingle/swatches/skin_default.scss
index 46cecd869..b01cd9534 100644
--- a/src/themes/Svingle/swatches/skin_default.scss
+++ b/src/themes/Svingle/swatches/skin_default.scss
@@ -2,6 +2,7 @@
$slider: false !default;
$progress-bar: false !default;
+$double-progress-bar: false !default;
$select-group: false !default;
$select-group-bubble: false !default;
$select-group-button: false !default;
@@ -15,6 +16,7 @@ $minimised-banner: false !default;
@include color_light.swatch(
$slider,
$progress-bar,
+ $double-progress-bar,
$select-group,
$select-group-bubble,
$select-group-button,
diff --git a/src/themes/Svingle/swatches/skin_vector-2022.scss b/src/themes/Svingle/swatches/skin_vector-2022.scss
index aed768b16..137465f8d 100644
--- a/src/themes/Svingle/swatches/skin_vector-2022.scss
+++ b/src/themes/Svingle/swatches/skin_vector-2022.scss
@@ -3,6 +3,7 @@
$slider: false !default;
$progress-bar: false !default;
+$double-progress-bar: false !default;
$select-group: false !default;
$select-group-bubble: false !default;
$select-group-button: false !default;
@@ -16,6 +17,7 @@ $minimised-banner: false !default;
@include color_light.swatch(
$slider,
$progress-bar,
+ $double-progress-bar,
$select-group,
$select-group-bubble,
$select-group-button,
@@ -31,6 +33,7 @@ $minimised-banner: false !default;
@include color_dark.swatch(
$slider,
$progress-bar,
+ $double-progress-bar,
$select-group,
$select-group-bubble,
$select-group-button,
@@ -47,6 +50,7 @@ $minimised-banner: false !default;
@include color_dark.swatch(
$slider,
$progress-bar,
+ $double-progress-bar,
$select-group,
$select-group-bubble,
$select-group-button,
@@ -62,6 +66,7 @@ $minimised-banner: false !default;
@include color_light.swatch(
$slider,
$progress-bar,
+ $double-progress-bar,
$select-group,
$select-group-bubble,
$select-group-button,
diff --git a/src/utils/DynamicContent/DynamicCampaignText.ts b/src/utils/DynamicContent/DynamicCampaignText.ts
index 9b4b6d887..14d7c4bcc 100644
--- a/src/utils/DynamicContent/DynamicCampaignText.ts
+++ b/src/utils/DynamicContent/DynamicCampaignText.ts
@@ -15,6 +15,7 @@ import { ProgressBarContent } from '@src/utils/DynamicContent/generators/Progres
import { DynamicProgressBarContent } from '@src/utils/DynamicContent/DynamicProgressBarContent';
import { CurrentTime } from '@src/utils/DynamicContent/generators/CurrentTime';
import { DateAndTime } from '@src/utils/DynamicContent/DateAndTime';
+import { DaysPassedSentence } from '@src/utils/DynamicContent/generators/DaysPassedSentence';
export default class DynamicCampaignText implements DynamicContent {
private readonly _date: Date;
@@ -116,6 +117,12 @@ export default class DynamicCampaignText implements DynamicContent {
} );
}
+ public get daysPassedSentence(): string {
+ return this.getCachedValue( 'daysPassedSentence', () => {
+ return new DaysPassedSentence( this.getCampaignTimeRange(), this._translator ).getText();
+ } );
+ }
+
private getCampaignProjection(): CampaignProjection {
if ( this._campaignProjection === undefined ) {
const projectionRange = TimeRange.createFromStrings(
diff --git a/src/utils/DynamicContent/DynamicContent.ts b/src/utils/DynamicContent/DynamicContent.ts
index 74b42a335..b19ae60c0 100644
--- a/src/utils/DynamicContent/DynamicContent.ts
+++ b/src/utils/DynamicContent/DynamicContent.ts
@@ -10,6 +10,7 @@ export interface DynamicContent {
currentDate: string;
getCurrentDateAndTime: () => DateAndTime;
daysLeftSentence: string;
+ daysPassedSentence: string;
campaignDaySentence: string;
visitorsVsDonorsSentence: string;
donorsNeededSentence: string;
diff --git a/src/utils/DynamicContent/generators/DaysPassedSentence.ts b/src/utils/DynamicContent/generators/DaysPassedSentence.ts
new file mode 100644
index 000000000..58c6113e4
--- /dev/null
+++ b/src/utils/DynamicContent/generators/DaysPassedSentence.ts
@@ -0,0 +1,27 @@
+import { TextGenerator } from '@src/utils/DynamicContent/generators/TextGenerator';
+import TimeRange from '@src/utils/TimeRange';
+import { Translator } from '@src/Translator';
+
+export class DaysPassedSentence implements TextGenerator {
+ private readonly _campaignDays: TimeRange;
+ private readonly _translator: Translator;
+
+ public constructor( campaignDays: TimeRange, translator: Translator ) {
+ this._campaignDays = campaignDays;
+ this._translator = translator;
+ }
+
+ public getText(): string {
+ const daysSinceStart = this._campaignDays.daysSinceStart();
+ return [
+ daysSinceStart,
+ this.getDayTranslation( daysSinceStart )
+ ].join( ' ' );
+ }
+
+ private getDayTranslation( daysSinceStart: number ): string {
+ return daysSinceStart === 1 ?
+ this._translator.translate( 'day-singular' ) :
+ this._translator.translate( 'day-plural' );
+ }
+}
diff --git a/test/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.spec.ts b/test/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.spec.ts
new file mode 100644
index 000000000..7634ad23f
--- /dev/null
+++ b/test/banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.spec.ts
@@ -0,0 +1,176 @@
+import { afterEach, beforeEach, describe, test, vi } from 'vitest';
+import { mount, VueWrapper } from '@vue/test-utils';
+import Banner from '@banners/desktop/C24_WMDE_Desktop_DE_11/components/BannerVar.vue';
+import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates';
+import { newDynamicContent } from '@test/banners/dynamicCampaignContent';
+import { useOfFundsContent } from '@test/banners/useOfFundsContent';
+import { formItems } from '@test/banners/formItems';
+import { CurrencyEn } from '@src/utils/DynamicContent/formatters/CurrencyEn';
+import { useOfFundsFeatures } from '@test/features/UseOfFunds';
+import {
+ bannerContentAnimatedTextFeatures,
+ bannerContentDateAndTimeFeatures,
+ bannerContentDisplaySwitchFeatures,
+ bannerContentFeatures
+} from '@test/features/BannerContent';
+import { donationFormFeatures } from '@test/features/forms/MainDonation_UpgradeToYearlyButton';
+import { useFormModel } from '@src/components/composables/useFormModel';
+import { resetFormModel } from '@test/resetFormModel';
+import { DynamicContent } from '@src/utils/DynamicContent/DynamicContent';
+import { bannerMainFeatures } from '@test/features/MainBanner';
+import { formActionSwitchFeatures } from '@test/features/form_action_switch/MainDonation_UpgradeToYearlyButton';
+import { softCloseFeatures } from '@test/features/SoftCloseDesktop';
+import { alreadyDonatedModalFeatures } from '@test/features/AlreadyDonatedModal';
+import { softCloseSubmitTrackingFeaturesDesktop } from '@test/features/SoftCloseSubmitTrackingDesktop';
+import { Tracker } from '@src/tracking/Tracker';
+
+const formModel = useFormModel();
+const translator = ( key: string ): string => key;
+let tracker: Tracker;
+
+describe( 'BannerVar.vue', () => {
+
+ beforeEach( () => {
+ resetFormModel( formModel );
+ vi.useFakeTimers();
+ tracker = {
+ trackEvent: vi.fn()
+ };
+ } );
+
+ afterEach( () => {
+ vi.restoreAllMocks();
+ vi.useRealTimers();
+ } );
+
+ const getWrapper = ( dynamicContent: DynamicContent = null ): VueWrapper => {
+ return mount( Banner, {
+ attachTo: document.body,
+ props: {
+ bannerState: BannerStates.Pending,
+ useOfFundsContent,
+ remainingImpressions: 10,
+ localCloseTracker: {
+ getItem: () => '',
+ setItem: () => {}
+ }
+ },
+ global: {
+ mocks: {
+ $translate: translator
+ },
+ provide: {
+ translator: { translate: translator },
+ dynamicCampaignText: dynamicContent ?? newDynamicContent(),
+ currentCampaignTimePercentage: 42,
+ formActions: {
+ donateWithAddressAction: 'https://example.com/with-address',
+ donateAnonymouslyAction: 'https://example.com/without-address'
+ },
+ currencyFormatter: new CurrencyEn(),
+ formItems,
+ tracker
+ }
+ }
+ } );
+ };
+
+ describe( 'Main Banner', () => {
+ test.each( [
+ [ 'expectDoesNotEmitCloseEvent' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerMainFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Content', () => {
+ test.each( [
+ [ 'expectSlideShowPlaysWhenBecomesVisible' ],
+ [ 'expectSlideShowStopsOnFormInteraction' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerContentFeatures[ testName ]( getWrapper() );
+ } );
+
+ test.each( [
+ [ 'expectShowsSlideShowOnSmallSizes' ],
+ [ 'expectShowsMessageOnLargeSizes' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerContentDisplaySwitchFeatures[ testName ]( getWrapper, 1300 );
+ } );
+
+ test.each( [
+ [ 'expectShowsAnimatedVisitorsVsDonorsSentenceInMessage' ],
+ [ 'expectShowsAnimatedVisitorsVsDonorsSentenceInSlideShow' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerContentAnimatedTextFeatures[ testName ]( getWrapper );
+ } );
+
+ test.each( [
+ [ 'expectShowsLiveDateAndTimeInMessage' ],
+ [ 'expectShowsLiveDateAndTimeInSlideshow' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerContentDateAndTimeFeatures[ testName ]( getWrapper );
+ } );
+ } );
+
+ describe( 'Donation Form Happy Paths', () => {
+ test.each( [
+ [ 'expectMainDonationFormSubmitsWhenSofortIsSelected' ],
+ [ 'expectMainDonationFormSubmitsWhenYearlyIsSelected' ],
+ [ 'expectMainDonationFormGoesToUpgrade' ],
+ [ 'expectUpgradeToYearlyFormSubmitsUpgrade' ],
+ [ 'expectUpgradeToYearlyFormSubmitsDontUpgrade' ]
+ ] )( '%s', async ( testName: string ) => {
+ await donationFormFeatures[ testName ]( getWrapper() );
+ } );
+
+ test.each( [
+ [ 'expectMainDonationFormSubmitsWithAddressForDirectDebit' ],
+ [ 'expectMainDonationFormSubmitsWithAddressForPayPal' ],
+ [ 'expectUpgradeToYearlyFormSubmitsWithAddressForDirectDebit' ],
+ [ 'expectUpgradeToYearlyFormSubmitsWithAddressForPayPal' ]
+ ] )( '%s', async ( testName: string ) => {
+ await formActionSwitchFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Soft Close', () => {
+ test.each( [
+ [ 'expectShowsSoftClose' ],
+ [ 'expectEmitsSoftCloseCloseEvent' ],
+ [ 'expectEmitsSoftCloseMaybeLaterEvent' ],
+ [ 'expectEmitsSoftCloseTimeOutEvent' ],
+ [ 'expectEmitsBannerContentChangedOnSoftClose' ],
+ [ 'expectShowsCloseIcon' ],
+ [ 'expectCloseIconEmitsCloseEvent' ]
+ ] )( '%s', async ( testName: string ) => {
+ await softCloseFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Soft Close Submit Tracking', () => {
+ test.each( [
+ [ 'expectEmitsBannerSubmitOnReturnEvent' ],
+ [ 'expectDoesNotEmitsBannerSubmitOnReturnEventWhenLocalStorageItemIsMissing' ]
+ ] )( '%s', async ( testName: string ) => {
+ await softCloseSubmitTrackingFeaturesDesktop[ testName ]( getWrapper(), tracker );
+ } );
+ } );
+
+ describe( 'Already Donated', () => {
+ test.each( [
+ [ 'expectFiresMaybeLaterEventOnLinkClick' ]
+ ] )( '%s', async ( testName: string ) => {
+ await alreadyDonatedModalFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Use of Funds', () => {
+ test.each( [
+ [ 'expectShowsUseOfFunds' ],
+ [ 'expectHidesUseOfFunds' ]
+ ] )( '%s', async ( testName: string ) => {
+ await useOfFundsFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+} );
diff --git a/test/banners/dynamicCampaignContent.ts b/test/banners/dynamicCampaignContent.ts
index 3fdb991be..e06f8de9b 100644
--- a/test/banners/dynamicCampaignContent.ts
+++ b/test/banners/dynamicCampaignContent.ts
@@ -10,6 +10,7 @@ export function newDynamicContent(): DynamicContent {
},
currentDayName: '',
daysLeftSentence: 'daysLeftSentence',
+ daysPassedSentence: 'daysPassedSentence',
donorsNeededSentence: '',
goalDonationSum: '',
remainingDonationSum: '',
diff --git a/test/components/ProgressBar/DoubleProgressBar.spec.ts b/test/components/ProgressBar/DoubleProgressBar.spec.ts
index e07a40619..4ef82abee 100644
--- a/test/components/ProgressBar/DoubleProgressBar.spec.ts
+++ b/test/components/ProgressBar/DoubleProgressBar.spec.ts
@@ -16,6 +16,7 @@ describe( 'DoubleProgressBar.vue', () => {
},
currentDayName: '',
daysLeftSentence: 'daysLeftSentence',
+ daysPassedSentence: 'daysPassedSentence',
donorsNeededSentence: '',
goalDonationSum: '',
remainingDonationSum: '',
@@ -51,4 +52,50 @@ describe( 'DoubleProgressBar.vue', () => {
expect( wrapper.attributes( 'style' ) ).toContain( '--wmde-banner-progress-bar-time-width: 66%' );
} );
+ it( 'shows the early progress content', () => {
+ dynamicCampaignContent.progressBarContent.isLateProgress = false;
+ const wrapper = mount( ProgressBar, {
+ props: { amountToShowOnRight: 'TARGET' },
+ global: {
+ mocks: {
+ $translate: ( key: string ) => key
+ },
+ provide: {
+ dynamicCampaignText: dynamicCampaignContent,
+ currentCampaignTimePercentage: 66
+ }
+ }
+ } );
+
+ expect( wrapper.classes() ).not.toContain( 'is-late-progress' );
+ expect( wrapper.find( '.wmde-banner-double-progress-amount .wmde-banner-double-progress-right-text' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.wmde-banner-double-progress-time .wmde-banner-double-progress-right-text' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.amount-needed' ).exists() ).toBeFalsy();
+ expect( wrapper.find( '.close-text' ).exists() ).toBeFalsy();
+ expect( wrapper.find( '.days-left' ).exists() ).toBeFalsy();
+ } );
+
+ it( 'shows the late progress content', () => {
+ dynamicCampaignContent.progressBarContent.isLateProgress = true;
+ const wrapper = mount( ProgressBar, {
+ props: { amountToShowOnRight: 'TARGET' },
+ global: {
+ mocks: {
+ $translate: ( key: string ) => key
+ },
+ provide: {
+ dynamicCampaignText: dynamicCampaignContent,
+ currentCampaignTimePercentage: 66
+ }
+ }
+ } );
+
+ expect( wrapper.classes() ).toContain( 'is-late-progress' );
+ expect( wrapper.find( '.wmde-banner-double-progress-amount .wmde-banner-double-progress-right-text' ).exists() ).toBeFalsy();
+ expect( wrapper.find( '.wmde-banner-double-progress-time .wmde-banner-double-progress-right-text' ).exists() ).toBeFalsy();
+ expect( wrapper.find( '.amount-needed' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.close-text' ).exists() ).toBeTruthy();
+ expect( wrapper.find( '.days-left' ).exists() ).toBeTruthy();
+ } );
+
} );
diff --git a/test/components/ProgressBar/ProgressBar.spec.ts b/test/components/ProgressBar/ProgressBar.spec.ts
index 33b69284e..4d9748bca 100644
--- a/test/components/ProgressBar/ProgressBar.spec.ts
+++ b/test/components/ProgressBar/ProgressBar.spec.ts
@@ -16,6 +16,7 @@ describe( 'ProgressBar.vue', () => {
},
currentDayName: '',
daysLeftSentence: 'daysLeftSentence',
+ daysPassedSentence: 'daysPassedSentence',
donorsNeededSentence: '',
goalDonationSum: '',
remainingDonationSum: '',
diff --git a/test/unit/utils/DynamicContent/DynamicCampaignText.spec.ts b/test/unit/utils/DynamicContent/DynamicCampaignText.spec.ts
index 4c06b1799..21c273f83 100644
--- a/test/unit/utils/DynamicContent/DynamicCampaignText.spec.ts
+++ b/test/unit/utils/DynamicContent/DynamicCampaignText.spec.ts
@@ -100,6 +100,10 @@ describe( 'DynamicCampaignText', () => {
expect( dynamicCampaignText.daysLeftSentence ).toBe( 'only 52 days left' );
} );
+ it( 'Gets the days passed sentence', () => {
+ expect( dynamicCampaignText.daysPassedSentence ).toBe( '8 days' );
+ } );
+
it( 'Gets the donors needed sentence', () => {
expect( dynamicCampaignText.donorsNeededSentence ).toBe( '445000' );
} );
diff --git a/test/unit/utils/DynamicContent/generators/DaysPassedSentence.spec.ts b/test/unit/utils/DynamicContent/generators/DaysPassedSentence.spec.ts
new file mode 100644
index 000000000..647f50076
--- /dev/null
+++ b/test/unit/utils/DynamicContent/generators/DaysPassedSentence.spec.ts
@@ -0,0 +1,25 @@
+import { describe, it, expect } from 'vitest';
+import TimeRange from '@src/utils/TimeRange';
+import { Translator } from '@src/Translator';
+import { DaysPassedSentence } from '@src/utils/DynamicContent/generators/DaysPassedSentence';
+
+describe( 'DaysPassedSentence', function () {
+ const translator = new Translator( {
+ 'prefix-days-left': 'only',
+ 'suffix-days-left': 'left',
+ 'day-singular': 'day',
+ 'day-plural': 'days'
+ } );
+
+ it( 'should return a sentence for the first day', function () {
+ const campaignDays = new TimeRange( new Date( 2023, 10, 11 ), new Date( 2024, 0, 1 ), new Date( 2023, 10, 12 ) );
+ const daysLeft = new DaysPassedSentence( campaignDays, translator );
+ expect( daysLeft.getText() ).toBe( '1 day' );
+ } );
+
+ it( 'should return a sentence for other days', function () {
+ const campaignDays = new TimeRange( new Date( 2023, 10, 11 ), new Date( 2024, 0, 1 ), new Date( 2023, 11, 25 ) );
+ const daysLeft = new DaysPassedSentence( campaignDays, translator );
+ expect( daysLeft.getText() ).toBe( '44 days' );
+ } );
+} );