diff --git a/banners/mobile/banner_var.ts b/banners/mobile/banner_var.ts
index 94a2a8fb9..5a0378743 100644
--- a/banners/mobile/banner_var.ts
+++ b/banners/mobile/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/BannerConductor.vue';
-import Banner from './components/BannerCtrl.vue';
+import Banner from './components/BannerVar.vue';
import getBannerDelay from '@src/utils/getBannerDelay';
import { WindowResizeHandler } from '@src/utils/ResizeHandler';
import PageWPORG from '@src/page/PageWPORG';
diff --git a/banners/mobile/components/BannerVar.vue b/banners/mobile/components/BannerVar.vue
new file mode 100644
index 000000000..c8d84a3bc
--- /dev/null
+++ b/banners/mobile/components/BannerVar.vue
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ onClose( 'FullPageBanner', CloseChoices.Hide )"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $translate( 'back-button' ) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ onClose( 'SoftClose', CloseChoices.Close )"
+ @maybe-later="() => onClose( 'SoftClose', CloseChoices.MaybeLater )"
+ @time-out-close="() => onClose( 'SoftClose', CloseChoices.TimeOut )"
+ />
+
+
+
+
+
+
diff --git a/banners/mobile/components/MiniBannerVar.vue b/banners/mobile/components/MiniBannerVar.vue
new file mode 100644
index 000000000..9643cb180
--- /dev/null
+++ b/banners/mobile/components/MiniBannerVar.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Jetzt 5€ spenden
+
+
+ Anderen Betrag
+
+
+
+
+
+
+
diff --git a/banners/mobile/content/BannerSlides.vue b/banners/mobile/content/BannerSlides.vue
index ac0252c38..a3500535d 100644
--- a/banners/mobile/content/BannerSlides.vue
+++ b/banners/mobile/content/BannerSlides.vue
@@ -11,10 +11,8 @@
-
- Millionen Menschen nutzen Wikipedia, aber 99 % spenden nicht – sie übergehen diesen Aufruf.
-
- Die meisten Menschen spenden, weil sie Wikipedia nützlich finden.
+ Millionen Menschen nutzen Wikipedia, aber 99 % spenden nicht
+ – sie übergehen diesen Aufruf. Die meisten Menschen spenden, weil sie Wikipedia nützlich finden.
diff --git a/banners/mobile/content/BannerText.vue b/banners/mobile/content/BannerText.vue
index c553f50b6..0f6481d48 100644
--- a/banners/mobile/content/BannerText.vue
+++ b/banners/mobile/content/BannerText.vue
@@ -7,10 +7,8 @@
vielleicht kommen wir gerade ungelegen, aber dennoch: Klicken Sie jetzt bitte nicht weg! Am
heutigen {{ currentDayName }} bitten wir Sie bescheiden, die Unabhängigkeit von Wikipedia zu
- unterstützen.
-
- Millionen Menschen nutzen Wikipedia, aber 99 % spenden nicht – sie übergehen diesen Aufruf.
-
+ unterstützen. Millionen Menschen nutzen Wikipedia,
+ aber 99 % spenden nicht – sie übergehen diesen Aufruf.
Die meisten Menschen spenden, weil sie Wikipedia nützlich finden. Die durchschnittliche Spende
beträgt 22,25 €, doch bereits 5 € helfen uns weiter. Hat Wikipedia Ihnen in
diesem Jahr Wissen im Wert einer Tasse Kaffee geschenkt? Dann entscheiden Sie sich, eine der
diff --git a/banners/mobile/event_map.ts b/banners/mobile/event_map.ts
index 8596b1635..5e57ef7a5 100644
--- a/banners/mobile/event_map.ts
+++ b/banners/mobile/event_map.ts
@@ -13,7 +13,7 @@ import { WMDESizeIssueEvent } from '@src/tracking/WPORG/WMDEBannerSizeIssue';
export default new Map( [
[ CloseEvent.EVENT_NAME, mapCloseEvent ],
[ MobileMiniBannerExpandedEvent.EVENT_NAME,
- ( e: MobileMiniBannerExpandedEvent ): WMDELegacyBannerEvent => new WMDELegacyBannerEvent( e.eventName, 1 ) ],
+ ( e: MobileMiniBannerExpandedEvent ): WMDELegacyBannerEvent => new WMDELegacyBannerEvent( e.eventName + ( e.userChoice !== '' ? `-${e.userChoice}` : '' ), 1 ) ],
[ FormStepShownEvent.EVENT_NAME, mapFormStepShownEvent ],
[ NotShownEvent.EVENT_NAME, mapNotShownEvent ],
diff --git a/banners/mobile/messages.ts b/banners/mobile/messages.ts
index a9655b585..f24a88452 100644
--- a/banners/mobile/messages.ts
+++ b/banners/mobile/messages.ts
@@ -21,7 +21,8 @@ const messages: TranslationMessages = {
'Sie eine Bestätigung per E-Mail.',
'address-type-notice-none': 'Sie verzichten sowohl auf eine Spendenquittung als auch auf eine Bestätigung ' +
'per E-Mail. Sie erhalten von uns keine Information, wenn Wikipedia wieder Hilfe braucht.',
- 'soft-close-prompt': 'Wikipedia später unterstützen?'
+ 'soft-close-prompt': 'Wikipedia später unterstützen?',
+ 'use-of-funds-link': 'Was Ihre Spende bewirkt'
};
export default messages;
diff --git a/banners/mobile/styles/MiniBannerVar.scss b/banners/mobile/styles/MiniBannerVar.scss
new file mode 100644
index 000000000..48e0cd732
--- /dev/null
+++ b/banners/mobile/styles/MiniBannerVar.scss
@@ -0,0 +1,158 @@
+@use '@src/themes/Mikings/variables/colors';
+@use '@src/themes/Mikings/variables/globals';
+@use 'sass:color';
+
+$height: 288px !default;
+$green: #6e9e00;
+
+.wmde-banner {
+ &-mini {
+ display: flex;
+ flex-direction: column;
+ min-height: $height;
+ padding: 16px 0;
+ position: relative;
+ border: 2px solid colors.$primary;
+ background: colors.$white;
+
+ &-close {
+ position: absolute;
+ height: 36px;
+ width: 36px;
+ top: 11px;
+ right: 16px;
+ text-align: center;
+ background: colors.$white;
+ padding: 10px;
+ z-index: 2;
+
+ &-button {
+ margin-top: auto;
+ float: right;
+ height: 16px;
+ line-height: 16px;
+ width: 16px;
+ background: colors.$white;
+ z-index: 2;
+
+ svg {
+ height: 16px;
+ width: 16px;
+ }
+
+ &:hover {
+ cursor: pointer;
+ }
+ }
+ }
+
+ &-headline {
+ height: 25px;
+ text-align: center;
+ margin: 0 16px 16px;
+
+ &-background {
+ position: relative;
+ text-align: left;
+
+ @media ( min-width: 400px ) {
+ text-align: center;
+ }
+
+ /* single line above container */
+ &::before {
+ content: '';
+ display: block;
+ background: colors.$primary;
+ width: 100%;
+ height: 1px;
+ position: absolute;
+ top: 50%;
+ z-index: 1;
+ }
+ }
+
+ &-content {
+ position: relative;
+ display: inline-block;
+ font-weight: bold;
+ font-size: 14px;
+ line-height: 25px;
+ color: colors.$black;
+ background: colors.$white;
+ padding: 0 5px;
+ z-index: 2;
+
+ @media ( min-width: 330px ) {
+ font-size: 16px;
+ }
+
+ @media ( min-width: 360px ) {
+ font-size: 18px;
+ }
+ }
+ }
+
+ &-slideshow {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+ }
+
+ &-button-group {
+ display: flex;
+ justify-content: center;
+ }
+
+ &-button,
+ &-button-preselect {
+ width: 50%;
+ height: 40px;
+ line-height: 40px;
+ border-radius: 20px;
+ font-weight: bold;
+ color: colors.$white;
+ margin: 0 16px;
+ font-size: 14px;
+ white-space: nowrap;
+
+ @media ( min-width: 370px ) {
+ font-size: 16px;
+ }
+ }
+
+ &-button {
+ background: colors.$secondary;
+
+ &:hover,
+ &:focus {
+ background: colors.$secondary-hover;
+ }
+ }
+
+ &-button-preselect {
+ background: $green;
+
+ &:hover,
+ &:focus {
+ background: color.adjust( $green, $lightness: -5% );
+ }
+ }
+
+ .smallprint-mini {
+ text-align: center;
+ font-size: 11px;
+ margin-top: 12px;
+ margin-bottom: -5px;
+
+ a {
+ color: colors.$gray;
+
+ &:hover,
+ &:focus {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+}
diff --git a/banners/mobile/styles/styles_var.scss b/banners/mobile/styles/styles_var.scss
new file mode 100644
index 000000000..c8599e22e
--- /dev/null
+++ b/banners/mobile/styles/styles_var.scss
@@ -0,0 +1,17 @@
+@use 'src/components/BannerConductor/banner-transition';
+@use 'src/themes/UseOfFunds/UseOfFunds';
+@use 'src/themes/Mikings/defaults';
+@use './Banner';
+@use './MiniBannerVar' with (
+ $height: 288px
+);
+@use './FullPageBanner';
+@use 'src/themes/Mikings/Footer/Footer';
+@use 'src/themes/Mikings/Footer/SelectionInput';
+@use 'src/themes/Mikings/DonationForm/MultiStepDonation';
+@use 'src/themes/Mikings/DonationForm/Forms/UpgradeToYearlyButtonForm';
+@use 'src/themes/Mikings/DonationForm/SubComponents/SelectGroup';
+@use 'src/themes/Mikings/DonationForm/SubComponents/SelectCustomAmount';
+@use 'src/themes/Mikings/DonationForm/SubComponents/SmsBox';
+@use 'src/themes/Mikings/Slider/Slider';
+@use 'src/themes/Mikings/SoftClose/SoftClose';
diff --git a/src/tracking/events/MobileMiniBannerExpandedEvent.ts b/src/tracking/events/MobileMiniBannerExpandedEvent.ts
index b1b6fbf00..90b83bb9d 100644
--- a/src/tracking/events/MobileMiniBannerExpandedEvent.ts
+++ b/src/tracking/events/MobileMiniBannerExpandedEvent.ts
@@ -8,4 +8,8 @@ export class MobileMiniBannerExpandedEvent implements TrackingEvent {
public readonly customData: Record = {};
public readonly feature: TrackingFeatureName = 'MiniBanner';
public readonly userChoice: string = '';
+
+ public constructor( userChoice: string = '' ) {
+ this.userChoice = userChoice;
+ }
}
diff --git a/test/banners/mobile/components/BannerVar.spec.ts b/test/banners/mobile/components/BannerVar.spec.ts
new file mode 100644
index 000000000..4ec71f2a0
--- /dev/null
+++ b/test/banners/mobile/components/BannerVar.spec.ts
@@ -0,0 +1,156 @@
+import { afterEach, beforeEach, describe, test, vi } from 'vitest';
+import { mount, VueWrapper } from '@vue/test-utils';
+import Banner from '../../../../banners/mobile/components/BannerVar.vue';
+import { BannerStates } from '@src/components/BannerConductor/StateMachine/BannerStates';
+import { PageScroller } from '@src/utils/PageScroller/PageScroller';
+import { useOfFundsContent } from '@test/banners/useOfFundsContent';
+import { newDynamicContent } from '@test/banners/dynamicCampaignContent';
+import { CurrencyEn } from '@src/utils/DynamicContent/formatters/CurrencyEn';
+import { formItems } from '@test/banners/formItems';
+import { softCloseFeatures } from '@test/features/SoftCloseMobile';
+import { useOfFundsFeatures, useOfFundsScrollFeatures } from '@test/features/UseOfFunds';
+import { miniBannerFeatures } from '@test/features/MiniBanner';
+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 { bannerContentAnimatedTextFeatures } from '@test/features/BannerContent';
+import { fullPageBannerFeatures } from '@test/features/FullPageBanner';
+import { miniBannerPreselectFeatures } from '@test/features/MiniBannerPreselect';
+import { Tracker } from '@src/tracking/Tracker';
+
+let pageScroller: PageScroller;
+let tracker: Tracker;
+const formModel = useFormModel();
+const translator = ( key: string ): string => key;
+describe( 'BannerVar.vue', () => {
+
+ let wrapper: VueWrapper;
+ beforeEach( () => {
+ resetFormModel( formModel );
+
+ pageScroller = {
+ scrollIntoView: vi.fn(),
+ scrollToTop: vi.fn()
+ };
+
+ tracker = {
+ trackEvent: vi.fn()
+ };
+ } );
+
+ const getWrapper = ( dynamicContent: DynamicContent = null ): VueWrapper => {
+ // attachTo the document body to fix an issue with Vue Test Utils where
+ // clicking a submit button in a form does not fire the submit event
+ wrapper = mount( Banner, {
+ attachTo: document.body,
+ props: {
+ bannerState: BannerStates.Pending,
+ useOfFundsContent,
+ pageScroller
+ },
+ global: {
+ mocks: {
+ $translate: translator
+ },
+ provide: {
+ translator: { translate: translator },
+ dynamicCampaignText: dynamicContent ?? newDynamicContent(),
+ formActions: { donateWithAddressAction: 'https://example.com', donateWithoutAddressAction: 'https://example.com' },
+ currencyFormatter: new CurrencyEn(),
+ formItems,
+ tracker
+ }
+ }
+ } );
+
+ return wrapper;
+ };
+
+ afterEach( () => {
+ wrapper.unmount();
+ } );
+
+ // skipped because the sentence is not part of the current test
+ describe.skip( 'Content', () => {
+ test.each( [
+ [ 'expectHidesAnimatedVisitorsVsDonorsSentenceInMessage' ],
+ [ 'expectShowsAnimatedVisitorsVsDonorsSentenceInMessage' ],
+ [ 'expectHidesAnimatedVisitorsVsDonorsSentenceInSlideShow' ],
+ [ 'expectShowsAnimatedVisitorsVsDonorsSentenceInSlideShow' ]
+ ] )( '%s', async ( testName: string ) => {
+ await bannerContentAnimatedTextFeatures[ testName ]( getWrapper );
+ } );
+ } );
+
+ describe( 'Donation Form Happy Paths', () => {
+ test.each( [
+ [ 'expectMainDonationFormSubmitsWhenSofortIsSelected' ],
+ [ 'expectMainDonationFormSubmitsWhenYearlyIsSelected' ],
+ [ 'expectMainDonationFormGoesToUpgrade' ],
+ [ 'expectUpgradeToYearlyFormSubmitsUpgrade' ],
+ [ 'expectUpgradeToYearlyFormSubmitsDontUpgrade' ],
+ [ 'expectUpgradeToYearlyFormGoesToMainDonation' ]
+ ] )( '%s', async ( testName: string ) => {
+ await donationFormFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Soft Close', () => {
+ test.each( [
+ [ 'expectShowsSoftCloseOnMiniBannerClose' ],
+ [ 'expectDoesNotShowSoftCloseOnFullBannerClose' ],
+ [ 'expectEmitsSoftCloseCloseEvent' ],
+ [ 'expectEmitsSoftCloseMaybeLaterEvent' ],
+ [ 'expectEmitsSoftCloseTimeOutEvent' ],
+ [ 'expectEmitsBannerContentChangedOnSoftClose' ]
+ ] )( '%s', async ( testName: string ) => {
+ await softCloseFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+ describe( 'Use of Funds', () => {
+ test.each( [
+ [ 'expectShowsUseOfFunds' ],
+ [ 'expectHidesUseOfFunds' ]
+ ] )( '%s', async ( testName: string ) => {
+ await useOfFundsFeatures[ testName ]( getWrapper() );
+ } );
+
+ test.each( [
+ [ 'expectScrollsToFormWhenCallToActionIsClicked' ],
+ [ 'expectScrollsToLinkWhenCloseIsClicked' ]
+ ] )( '%s', async ( testName: string ) => {
+ await useOfFundsScrollFeatures[ testName ]( getWrapper(), pageScroller );
+ } );
+ } );
+
+ describe( 'Mini Banner', () => {
+ test.each( [
+ [ 'expectSlideShowPlaysWhenMiniBannerBecomesVisible' ],
+ [ 'expectSlideShowStopsWhenFullBannerBecomesVisible' ],
+ [ 'expectShowsFullPageWhenCallToActionIsClicked' ],
+ [ 'expectEmitsBannerContentChangedEventWhenCallToActionIsClicked' ]
+ ] )( '%s', async ( testName: string ) => {
+ await miniBannerFeatures[ testName ]( getWrapper() );
+ } );
+
+ test.each( [
+ [ 'expectShowsFullPageWhenPreselectIsClicked' ],
+ [ 'expectPreselectsAmountWhenPreselectIsClicked' ],
+ [ 'expectTrackingEventIsFiredWhenPreselectIsClicked' ]
+ ] )( '%s', async ( testName: string ) => {
+ await miniBannerPreselectFeatures[ testName ]( getWrapper(), tracker );
+ } );
+
+ } );
+
+ describe( 'Full Page Banner', () => {
+ test.each( [
+ [ 'expectEmitsCloseEvent' ]
+ ] )( '%s', async ( testName: string ) => {
+ await fullPageBannerFeatures[ testName ]( getWrapper() );
+ } );
+ } );
+
+} );
diff --git a/test/features/MiniBannerPreselect.ts b/test/features/MiniBannerPreselect.ts
new file mode 100644
index 000000000..b56322fc8
--- /dev/null
+++ b/test/features/MiniBannerPreselect.ts
@@ -0,0 +1,29 @@
+import { VueWrapper } from '@vue/test-utils';
+import { expect } from 'vitest';
+import { Tracker } from '@src/tracking/Tracker';
+import { MobileMiniBannerExpandedEvent } from '@src/tracking/events/MobileMiniBannerExpandedEvent';
+
+const expectShowsFullPageWhenPreselectIsClicked = async ( wrapper: VueWrapper ): Promise => {
+ await wrapper.find( '.wmde-banner-mini-button-preselect' ).trigger( 'click' );
+
+ expect( wrapper.classes() ).toContain( 'wmde-banner-wrapper--full-page' );
+};
+
+const expectPreselectsAmountWhenPreselectIsClicked = async ( wrapper: VueWrapper ): Promise => {
+ await wrapper.find( '.wmde-banner-mini-button-preselect' ).trigger( 'click' );
+
+ expect( wrapper.find( '.wmde-banner-select-group-input:checked' ).element.value ).toStrictEqual( '5' );
+};
+
+const expectTrackingEventIsFiredWhenPreselectIsClicked = async ( wrapper: VueWrapper, tracker: Tracker ): Promise => {
+ await wrapper.find( '.wmde-banner-mini-button-preselect' ).trigger( 'click' );
+
+ expect( tracker.trackEvent ).toHaveBeenCalledOnce();
+ expect( tracker.trackEvent ).toHaveBeenCalledWith( new MobileMiniBannerExpandedEvent( 'preselected' ) );
+};
+
+export const miniBannerPreselectFeatures: Record, tracker: Tracker ) => Promise> = {
+ expectShowsFullPageWhenPreselectIsClicked,
+ expectPreselectsAmountWhenPreselectIsClicked,
+ expectTrackingEventIsFiredWhenPreselectIsClicked
+};