Skip to content

Commit cd2450d

Browse files
Merge branch 'latest' into dependabot/npm_and_yarn/react-ad1875d9fa
2 parents bf3117c + 868fe73 commit cd2450d

File tree

18 files changed

+543
-27
lines changed

18 files changed

+543
-27
lines changed

envConfig/live.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ SIMORGH_WEBVITALS_DEFAULT_SAMPLING_RATE=20
3232
SIMORGH_CSP_REPORTING_ENDPOINT=https://ws.bbc-reporting-api.app/report-endpoint
3333

3434
SIMORGH_OPTIMIZELY_SDK_KEY=4Rje1JY7YY1FhaiHJ88Zi
35+
36+
NEXT_TELEMETRY_DISABLED=1

envConfig/local.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,5 @@ SIMORGH_WEBVITALS_DEFAULT_SAMPLING_RATE=100
3434
SIMORGH_CSP_REPORTING_ENDPOINT=https://ws.bbc-reporting-api.app/report-endpoint
3535

3636
SIMORGH_OPTIMIZELY_SDK_KEY=LptPKDnHyAFu9V12s5xCz
37+
38+
NEXT_TELEMETRY_DISABLED=1

envConfig/preview1.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ SIMORGH_WEBVITALS_DEFAULT_SAMPLING_RATE=100
3333
SIMORGH_CSP_REPORTING_ENDPOINT=https://ws.bbc-reporting-api.app/report-endpoint
3434

3535
SIMORGH_OPTIMIZELY_SDK_KEY=LptPKDnHyAFu9V12s5xCz
36+
37+
NEXT_TELEMETRY_DISABLED=1

envConfig/preview2.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ SIMORGH_WEBVITALS_DEFAULT_SAMPLING_RATE=100
3333
SIMORGH_CSP_REPORTING_ENDPOINT=https://ws.bbc-reporting-api.app/report-endpoint
3434

3535
SIMORGH_OPTIMIZELY_SDK_KEY=LptPKDnHyAFu9V12s5xCz
36+
37+
NEXT_TELEMETRY_DISABLED=1

envConfig/test.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ SIMORGH_WEBVITALS_DEFAULT_SAMPLING_RATE=100
3333
SIMORGH_CSP_REPORTING_ENDPOINT=https://ws.bbc-reporting-api.app/report-endpoint
3434

3535
SIMORGH_OPTIMIZELY_SDK_KEY=LptPKDnHyAFu9V12s5xCz
36+
37+
NEXT_TELEMETRY_DISABLED=1
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# PromotionalBanner Component
2+
3+
This component displays a promotional banner with configurable title, description, and action buttons. It is designed to be service-aware and supports customisation via service config.
4+
5+
## Usage
6+
7+
Import and use the component in your page or story:
8+
9+
```tsx
10+
import PromotionalBanner from './PromotionallBanner';
11+
```
12+
13+
## Props
14+
15+
- `title`: string
16+
- `description`: string
17+
- `orText`: string
18+
- `primaryButton`: { text: string, longText?: string, onClick: () => void }
19+
- `secondaryButton`: { text: string, longText?: string, onClick: () => void }
20+
- `isDismissible?`: boolean
21+
- `handleClose`: () => void
22+
23+
## Service Config
24+
25+
The banner can be configured via the service config file (`src/app/lib/config/services/<service>.ts`).
26+
27+
## Storybook
28+
29+
See `index.stories.tsx` for usage examples in Storybook.
30+
31+
---
32+
33+
For more details, see the code and service config documentation.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react';
2+
import { ServiceContextProvider } from '#app/contexts/ServiceContext';
3+
import PromotionalBannerComponent from '.';
4+
import services from '#server/utilities/serviceConfigs';
5+
import { StoryProps } from '../../models/types/storybook';
6+
import metadata from './metadata.json';
7+
import readme from './README.md';
8+
9+
const ComponentWithContext = ({
10+
service = 'mundo',
11+
variant = 'default',
12+
}: StoryProps) => {
13+
const bannerConfig = services[service]?.[variant]?.promotionalBanner;
14+
const { title, description, primaryButton, secondaryButton } = bannerConfig;
15+
return (
16+
<ServiceContextProvider service={service} variant={variant}>
17+
<PromotionalBannerComponent
18+
title={title}
19+
description={description}
20+
primaryButton={{
21+
text: primaryButton?.text,
22+
longText: primaryButton?.longText,
23+
onClick: () => console.log('Primary clicked'),
24+
}}
25+
orText={bannerConfig.orText}
26+
secondaryButton={{
27+
text: secondaryButton?.text,
28+
onClick: () => console.log('Secondary clicked'),
29+
}}
30+
isDismissible
31+
handleClose={() => console.log('Banner closed')}
32+
/>
33+
</ServiceContextProvider>
34+
);
35+
};
36+
37+
export default {
38+
title: 'Components/PromotionalBanner',
39+
component: ComponentWithContext,
40+
parameters: {
41+
metadata,
42+
docs: { readme },
43+
},
44+
};
45+
46+
export const PromotionalBanner = ComponentWithContext;
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import pixelsToRem from '#app/utilities/pixelsToRem';
2+
import { css, Theme } from '@emotion/react';
3+
4+
const styles = {
5+
banner: ({ palette }: Theme) =>
6+
css({
7+
margin: '0 auto',
8+
display: 'flex',
9+
alignItems: 'center',
10+
justifyContent: 'center',
11+
background: `linear-gradient(to bottom left, ${palette.POSTBOX} 0%, ${palette.BLACK} 50%, ${palette.POSTBOX} 100%)`,
12+
width: '100%',
13+
}),
14+
15+
innerContainer: () =>
16+
css({
17+
width: '100%',
18+
position: 'relative',
19+
maxWidth: `${pixelsToRem(1008)}rem`,
20+
}),
21+
22+
content: ({ spacings, mq }) =>
23+
css({
24+
display: 'flex',
25+
flexDirection: 'column',
26+
position: 'relative',
27+
width: '100%',
28+
29+
paddingTop: `${spacings.TRIPLE}rem`,
30+
paddingBottom: `${spacings.DOUBLE}rem`,
31+
paddingInlineStart: `${spacings.FULL}rem`,
32+
paddingInlineEnd: `${pixelsToRem(52)}rem`,
33+
34+
[mq.GROUP_2_MIN_WIDTH]: {
35+
paddingInlineStart: `${spacings.DOUBLE}rem`,
36+
},
37+
}),
38+
39+
textContainer: () =>
40+
css({
41+
display: 'flex',
42+
flexDirection: 'column',
43+
}),
44+
title: ({ palette }: Theme) =>
45+
css({
46+
color: palette.WHITE,
47+
margin: 0,
48+
}),
49+
50+
description: ({ spacings, mq, palette }: Theme) =>
51+
css({
52+
color: palette.GREY_2,
53+
marginTop: `${spacings.FULL}rem`,
54+
[mq.GROUP_0_MAX_WIDTH]: {
55+
display: 'none',
56+
},
57+
}),
58+
59+
actionsContainer: ({ mq, spacings }: Theme) =>
60+
css({
61+
display: 'flex',
62+
alignItems: 'center',
63+
justifyContent: 'flex-start',
64+
gap: `${spacings.FULL}rem`,
65+
marginTop: `${spacings.DOUBLE}rem`,
66+
flexWrap: 'wrap',
67+
[mq.GROUP_1_MAX_WIDTH]: {
68+
flexDirection: 'row',
69+
},
70+
}),
71+
72+
primaryButton: ({ mq, spacings, palette }: Theme) =>
73+
css({
74+
color: palette.BLACK,
75+
backgroundColor: palette.WHITE,
76+
border: 'none',
77+
height: `${pixelsToRem(44)}rem`,
78+
padding: `${spacings.FULL}rem`,
79+
cursor: 'pointer',
80+
whiteSpace: 'nowrap',
81+
position: 'relative',
82+
'&:focus, &:hover': {
83+
color: palette.BLACK,
84+
textDecoration: 'underline',
85+
},
86+
87+
'& .short-text': {
88+
display: 'inline',
89+
[mq.GROUP_2_MIN_WIDTH]: {
90+
display: 'none',
91+
},
92+
},
93+
94+
'& .long-text': {
95+
display: 'none',
96+
[mq.GROUP_2_MIN_WIDTH]: {
97+
display: 'inline',
98+
},
99+
},
100+
}),
101+
102+
dividerText: ({ palette }: Theme) =>
103+
css({
104+
color: palette.WHITE,
105+
}),
106+
107+
secondaryButton: ({ spacings, palette }: Theme) =>
108+
css({
109+
color: palette.WHITE,
110+
backgroundColor: 'transparent',
111+
border: 'none',
112+
textDecoration: 'underline',
113+
height: `${pixelsToRem(44)}rem`,
114+
padding: `${spacings.HALF}rem`,
115+
cursor: 'pointer',
116+
whiteSpace: 'nowrap',
117+
'&:focus, &:hover': {
118+
backgroundColor: palette.WHITE,
119+
color: palette.BLACK,
120+
},
121+
}),
122+
123+
closeButton: ({ palette }: Theme) =>
124+
css({
125+
position: 'absolute',
126+
top: 0,
127+
insetInlineEnd: 0,
128+
border: 'none',
129+
background: 'transparent',
130+
padding: 0,
131+
cursor: 'pointer',
132+
display: 'flex',
133+
alignItems: 'center',
134+
justifyContent: 'center',
135+
width: `${pixelsToRem(44)}rem`,
136+
height: `${pixelsToRem(44)}rem`,
137+
color: palette.WHITE,
138+
'&:hover, &:focus': {
139+
outlineOffset: `${pixelsToRem(2)}rem`,
140+
backgroundColor: palette.POSTBOX,
141+
fill: palette.WHITE,
142+
color: palette.WHITE,
143+
},
144+
}),
145+
146+
closeButtonIcon: ({ mq, palette }: Theme) =>
147+
css({
148+
position: 'absolute',
149+
color: palette.WHITE,
150+
fill: 'currentColor',
151+
width: `${pixelsToRem(14)}rem`,
152+
height: `${pixelsToRem(14)}rem`,
153+
pointerEvents: 'none',
154+
[mq.FORCED_COLOURS]: {
155+
forcedColorAdjust: 'none',
156+
color: 'ButtonText',
157+
fill: 'ButtonText',
158+
},
159+
}),
160+
};
161+
162+
export default styles;

0 commit comments

Comments
 (0)