Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Country wide targetting #674

Merged
merged 20 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/models/BannerTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ case class BannerTest(
nickname: Option[String],
userCohort: UserCohort,
locations: List[Region] = Nil,
regionTargeting: Option[RegionTargeting]= None,
contextTargeting: PageContextTargeting = PageContextTargeting(Nil,Nil,Nil,Nil),
variants: List[BannerVariant],
articlesViewedSettings: Option[ArticlesViewedSettings] = None,
Expand Down
1 change: 1 addition & 0 deletions app/models/EpicTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ case class EpicTest(
priority: Option[Int],
nickname: Option[String],
locations: List[Region] = Nil,
regionTargeting: Option[RegionTargeting]=None,
tagIds: List[String] = Nil,
sections: List[String] = Nil,
excludedTagIds: List[String] = Nil,
Expand Down
1 change: 1 addition & 0 deletions app/models/HeaderTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ case class HeaderTest(
priority: Option[Int],
nickname: Option[String],
locations: List[Region] = Nil,
regionTargeting: Option[RegionTargeting]=None,
userCohort: Option[UserCohort] = None,
variants: List[HeaderVariant],
controlProportionSettings: Option[ControlProportionSettings] = None,
Expand Down
6 changes: 6 additions & 0 deletions app/models/RegionTargeting.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package models

case class RegionTargeting(
targetedCountryGroups: List[Region]= Nil,
targetedCountryCodes: Option[List[String]] = None,
)
104 changes: 104 additions & 0 deletions public/src/components/channelManagement/MultiSelectCountryEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React from 'react';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';
import { makeStyles } from '@mui/styles';
import { grey } from '@mui/material/colors';
import { Theme } from '@mui/material/styles';
import { countries } from '../../utils/models';
import { RegionTargeting } from './helpers/shared';

const useStyles = makeStyles(({ spacing }: Theme) => ({
container: {
width: '100%',
'& > * + *': {
marginTop: spacing(3),
},
borderColor: `2px solid ${{ color: grey[700] }}`,
borderRadius: '2px',
padding: spacing(2),
},
}));

interface Option {
label: string;
value: string;
}

const options: Option[] = Object.entries(countries).map(([value, label]) => ({ value, label }));

interface MultiselectAutocompleteProps {
disabled: boolean;
regionTargeting: RegionTargeting;
onRegionTargetingUpdate: (regionTargeting: RegionTargeting) => void;
}

const MultiselectAutocomplete: React.FC<MultiselectAutocompleteProps> = ({
disabled,
regionTargeting,
onRegionTargetingUpdate,
}: MultiselectAutocompleteProps) => {
const classes = useStyles();

const [inputValue, setInputValue] = React.useState<string>('');

return (
<div className={classes.container}>
<span style={{ fontSize: '1rem', fontWeight: 'normal' }}>
Additionally if you want to target by countries select from the list
</span>
<Autocomplete
id={'multi-select-country'}
multiple
disabled={disabled}
options={options}
getOptionLabel={option => option.label}
value={regionTargeting.targetedCountryCodes?.map(country => {
const option = options.find(option => option.value === country);
return option ?? { label: country, value: country };
})}
inputValue={inputValue}
componentsProps={{
popper: {
modifiers: [
{
name: 'flip',
enabled: false,
},
{
name: 'preventOverflow',
enabled: false,
},
],
},
}}
onInputChange={(event, newInputValue, reason): void => {
if (reason === 'input') {
setInputValue(newInputValue);
}
}}
renderInput={params => (
<TextField
{...params}
variant="outlined"
label="Choose options"
placeholder="Select multiple options "
/>
)}
renderOption={(props, option): JSX.Element => {
return <li {...props}>{option.label ? option.label : option.value}</li>;
}}
onChange={(event, values: Option[], reason): void => {
if (reason === 'selectOption' || reason === 'removeOption') {
onRegionTargetingUpdate({
...regionTargeting,
targetedCountryCodes: values.map(value => value.value),
});
setInputValue('');
}
}}
/>
</div>
);
};

export default MultiselectAutocomplete;
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useEffect, useState } from 'react';
import { Region } from '../../../utils/models';
import {
ArticlesViewedSettings,
ConsentStatus,
DeviceType,
Methodology,
RegionTargeting,
SignedInStatus,
UserCohort,
} from '../helpers/shared';
Expand Down Expand Up @@ -131,8 +131,12 @@ const BannerTestEditor: React.FC<ValidatedTestEditorProps<BannerTest>> = ({
onVariantsChange(test.variants.filter(variant => variant.name !== deletedVariantName));
};

const onRegionsChange = (updatedRegions: Region[]): void => {
updateTest({ ...test, locations: updatedRegions });
const onRegionTargetingChange = (updatedRegionTargeting: RegionTargeting): void => {
updateTest({
...test,
regionTargeting: updatedRegionTargeting,
locations: [], // deprecated
});
};

const onCohortChange = (updatedCohort: UserCohort): void => {
Expand Down Expand Up @@ -295,8 +299,14 @@ const BannerTestEditor: React.FC<ValidatedTestEditorProps<BannerTest>> = ({
</Typography>

<TestEditorTargetAudienceSelector
selectedRegions={test.locations}
onRegionsUpdate={onRegionsChange}
regionTargeting={
test.regionTargeting ?? {
// For backwards compatibility with the deprecated locations field
targetedCountryGroups: test.locations,
targetedCountryCodes: [],
}
}
onRegionTargetingUpdate={onRegionTargetingChange}
selectedCohort={test.userCohort}
onCohortChange={onCohortChange}
selectedDeviceType={test.deviceType ?? 'All'}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cta, UserCohort } from '../../helpers/shared';
import { Cta, RegionTargeting, UserCohort } from '../../helpers/shared';
import { BannerTest, BannerVariant } from '../../../../models/banner';

import { getStage } from '../../../../utils/stage';
Expand Down Expand Up @@ -44,12 +44,18 @@ export const getDefaultVariant = (): BannerVariant => {
return PROD_DEFAULT_VARIANT;
};

export const DEFAULT_REGION_TARGETING: RegionTargeting = {
targetedCountryGroups: [],
targetedCountryCodes: [],
};

const DEV_AND_CODE_DEFAULT_BANNER_TEST: BannerTest = {
name: 'TEST',
nickname: 'TEST',
status: 'Draft',
userCohort: UserCohort.AllNonSupporters,
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
variants: [DEV_AND_CODE_DEFAULT_VARIANT],
articlesViewedSettings: undefined,
contextTargeting: { tagIds: [], sectionIds: [], excludedTagIds: [], excludedSectionIds: [] },
Expand All @@ -62,6 +68,7 @@ const PROD_DEFAULT_BANNER: BannerTest = {
status: 'Draft',
userCohort: UserCohort.AllNonSupporters,
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
variants: [],
articlesViewedSettings: undefined,
contextTargeting: { tagIds: [], sectionIds: [], excludedTagIds: [], excludedSectionIds: [] },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react';
import { Region } from '../../../utils/models';
import { EpicTest, EpicVariant, MaxEpicViews } from '../../../models/epic';
import {
ArticlesViewedSettings,
Expand All @@ -10,6 +9,7 @@ import {
PageContextTargeting,
ConsentStatus,
Methodology,
RegionTargeting,
} from '../helpers/shared';
import { FormControlLabel, Switch, Typography } from '@mui/material';
import CampaignSelector from '../CampaignSelector';
Expand Down Expand Up @@ -150,8 +150,12 @@ export const getEpicTestEditor = (
});
};

const onRegionsChange = (updatedRegions: Region[]): void => {
updateTest({ ...test, locations: updatedRegions });
const onRegionTargetingChange = (updatedRegionTargeting: RegionTargeting): void => {
updateTest({
...test,
regionTargeting: updatedRegionTargeting,
locations: [], // deprecated
});
};

const onCohortChange = (updatedCohort: UserCohort): void => {
Expand Down Expand Up @@ -348,8 +352,14 @@ export const getEpicTestEditor = (
</Typography>

<TestEditorTargetAudienceSelector
selectedRegions={test.locations}
onRegionsUpdate={onRegionsChange}
regionTargeting={
test.regionTargeting ?? {
// For backwards compatibility with the deprecated locations field
targetedCountryGroups: test.locations,
targetedCountryCodes: [],
}
}
onRegionTargetingUpdate={onRegionTargetingChange}
selectedCohort={test.userCohort}
onCohortChange={onCohortChange}
supportedRegions={epicEditorConfig.supportedRegions}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cta, SecondaryCtaType, UserCohort } from '../../helpers/shared';
import { Cta, RegionTargeting, SecondaryCtaType, UserCohort } from '../../helpers/shared';

import { getStage } from '../../../../utils/stage';
import { EpicTest, EpicVariant, MaxEpicViews } from '../../../../models/epic';
Expand Down Expand Up @@ -56,11 +56,17 @@ export const getDefaultVariant = (): EpicVariant => {
return PROD_DEFAULT_VARIANT;
};

export const DEFAULT_REGION_TARGETING: RegionTargeting = {
targetedCountryGroups: [],
targetedCountryCodes: [],
};

const DEV_AND_CODE_DEFAULT_TEST: EpicTest = {
name: 'TEST',
nickname: 'TEST',
status: 'Draft',
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
tagIds: [],
sections: [],
excludedTagIds: [],
Expand All @@ -80,6 +86,7 @@ const PROD_DEFAULT_TEST: EpicTest = {
nickname: 'TEST',
status: 'Draft',
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
tagIds: [],
sections: [],
excludedTagIds: [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from 'react';
import { GutterTest, GutterVariant } from '../../../models/gutter';
import { Region } from '../../../utils/models';
import {
ConsentStatus,
DeviceType,
PageContextTargeting,
RegionTargeting,
SignedInStatus,
UserCohort,
} from '../helpers/shared';
Expand Down Expand Up @@ -75,8 +75,8 @@ const GutterTestEditor: React.FC<ValidatedTestEditorProps<GutterTest>> = ({
});
};

const onRegionsChange = (updatedRegions: Region[]): void => {
updateTest({ ...test, locations: updatedRegions });
const onRegionTargetingChange = (updatedRegionTargeting: RegionTargeting): void => {
updateTest({ ...test, regionTargeting: updatedRegionTargeting, locations: [] });
};

const onCohortChange = (updatedCohort: UserCohort): void => {
Expand Down Expand Up @@ -211,8 +211,14 @@ const GutterTestEditor: React.FC<ValidatedTestEditorProps<GutterTest>> = ({
</Typography>

<TestEditorTargetAudienceSelector
selectedRegions={test.locations}
onRegionsUpdate={onRegionsChange}
regionTargeting={
test.regionTargeting ?? {
// For backwards compatibility with the deprecated locations field
targetedCountryGroups: test.locations,
targetedCountryCodes: [],
}
}
onRegionTargetingUpdate={onRegionTargetingChange}
selectedCohort={test.userCohort}
onCohortChange={onCohortChange}
showDeviceTypeSelector={false}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { GutterTest, GutterVariant } from '../../../../models/gutter';
import { getStage } from '../../../../utils/stage';
import { Cta, UserCohort } from '../../helpers/shared';
import { Cta, RegionTargeting, UserCohort } from '../../helpers/shared';

export const DEFAULT_PRIMARY_CTA: Cta = {
text: 'Support us',
Expand Down Expand Up @@ -46,12 +46,18 @@ export const getDefaultVariant = (): GutterVariant => {
return PROD_DEFAULT_VARIANT;
};

export const DEFAULT_REGION_TARGETING: RegionTargeting = {
targetedCountryGroups: [],
targetedCountryCodes: [],
};

const DEV_AND_CODE_DEFAULT_GUTTER_TEST: GutterTest = {
name: 'TEST',
nickname: 'TEST',
status: 'Draft',
userCohort: UserCohort.AllNonSupporters,
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
variants: [DEV_AND_CODE_DEFAULT_VARIANT],
contextTargeting: { tagIds: [], sectionIds: [], excludedTagIds: [], excludedSectionIds: [] },
methodologies: [{ name: 'ABTest' }],
Expand All @@ -65,6 +71,7 @@ const PROD_DEFAULT_GUTTER_TEST: GutterTest = {
status: 'Draft',
userCohort: UserCohort.AllNonSupporters,
locations: [],
regionTargeting: DEFAULT_REGION_TARGETING,
variants: [PROD_DEFAULT_VARIANT],
contextTargeting: { tagIds: [], sectionIds: [], excludedTagIds: [], excludedSectionIds: [] },
methodologies: [{ name: 'ABTest' }],
Expand Down
Loading