Skip to content

Commit 4f28fa9

Browse files
authored
Add audio waveform design to Listen To Article button (#14869)
* remove ab testing from ListenToArticle * Add waveform container to ListenToAudio button * fix soundwave * Use podcast wave component * make seed unique * Fix palette for dark * fix ListenToArticle state * Hardcode waveform props * Rename props
1 parent 4a2e8f0 commit 4f28fa9

File tree

5 files changed

+76
-33
lines changed

5 files changed

+76
-33
lines changed

dotcom-rendering/src/components/ListenToArticle.importable.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ export const ListenToArticle = ({ articleId }: Props) => {
8484
showButton && (
8585
<ListenToArticleButton
8686
onClickHandler={listenToArticleHandler}
87+
waveFormSeed={articleId}
8788
audioDuration={
8889
audioDurationSeconds !== undefined
8990
? formatAudioDuration(audioDurationSeconds)

dotcom-rendering/src/components/ListenToArticleButton.stories.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type Story = StoryObj<typeof meta>;
1313
export const ListenToArticleWithDurationButton = {
1414
args: {
1515
onClickHandler: () => undefined,
16-
audioDuration: '3:02',
16+
audioDuration: '5:14',
1717
},
1818
} satisfies Story;
1919

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { css } from '@emotion/react';
22
import { height, space } from '@guardian/source/foundations';
3+
import type { ThemeIcon } from '@guardian/source/react-components';
34
import {
45
Button,
56
SvgMediaControlsPlay,
67
} from '@guardian/source/react-components';
78
import { palette } from '../palette';
9+
import type { WaveFormTheme } from './WaveForm';
10+
import { WaveForm } from './WaveForm';
811

912
const buttonCss = (audioDuration: string | undefined) => css`
1013
display: flex;
@@ -18,12 +21,12 @@ const buttonCss = (audioDuration: string | undefined) => css`
1821
}
1922
margin-bottom: ${space[4]}px;
2023
margin-left: ${space[2]}px;
21-
padding-left: ${space[2]}px;
24+
padding-left: ${space[3]}px;
2225
padding-right: ${audioDuration === undefined ? space[4] : space[3]}px;
2326
padding-bottom: 0px;
2427
font-size: 15px;
25-
height: ${height.ctaSmall}px;
26-
min-height: ${height.ctaSmall}px;
28+
height: ${height.ctaXsmall}px;
29+
min-height: ${height.ctaXsmall}px;
2730
2831
.src-button-space {
2932
width: 0px;
@@ -36,38 +39,73 @@ const dividerCss = css`
3639
opacity: 0.5;
3740
border-left: 1px solid ${palette('--follow-icon-background')};
3841
margin-left: ${space[2]}px;
39-
margin-right: ${space[2]}px;
4042
`;
4143

42-
const durationCss = css`
43-
font-weight: 300;
44-
`;
44+
const themeIcon: ThemeIcon = {
45+
fill: palette('--follow-icon-background'),
46+
};
47+
48+
const waveFormContainerCss = css`
49+
height: ${space[12]}px;
50+
border-top: 1px solid ${palette('--article-meta-lines')};
51+
position: relative;
52+
padding-top: ${space[2]}px;
53+
overflow: hidden;
54+
55+
> svg {
56+
position: absolute;
57+
top: ${space[2]}px;
58+
left: 0;
59+
width: 746px; /* Fixed width - adjust as needed */
60+
height: 100%;
61+
z-index: 0;
62+
}
4563
46-
const baselineCss = css`
47-
padding-bottom: 2px;
64+
> button {
65+
position: relative;
66+
z-index: 1;
67+
}
4868
`;
4969

50-
type ButtonProps = {
70+
type Props = {
5171
onClickHandler: () => void;
5272
audioDuration?: string;
73+
waveFormSeed?: string;
74+
};
75+
76+
const waveTheme: WaveFormTheme = {
77+
wave: palette('--listen-to-article-waveform'),
5378
};
79+
5480
export const ListenToArticleButton = ({
5581
onClickHandler,
5682
audioDuration,
57-
}: ButtonProps) => (
58-
<Button
59-
onClick={onClickHandler}
60-
size="default"
61-
iconSide="left"
62-
cssOverrides={buttonCss(audioDuration)}
63-
icon={<SvgMediaControlsPlay />}
64-
>
65-
{audioDuration === undefined || audioDuration === '' ? null : (
66-
<>
67-
<span css={[durationCss, baselineCss]}>{audioDuration}</span>
68-
<span css={dividerCss}></span>
69-
</>
70-
)}
71-
<span css={baselineCss}>Listen to this article</span>
72-
</Button>
73-
);
83+
waveFormSeed,
84+
}: Props) => {
85+
return (
86+
<div css={waveFormContainerCss}>
87+
<WaveForm
88+
seed={waveFormSeed ?? 'listen to this article'}
89+
height={space[10]}
90+
bars={250}
91+
theme={waveTheme}
92+
gap={1}
93+
barWidth={2}
94+
/>
95+
<Button
96+
onClick={onClickHandler}
97+
size="default"
98+
cssOverrides={buttonCss(audioDuration)}
99+
>
100+
<span>Listen to this article</span>
101+
{audioDuration === undefined || audioDuration === '' ? null : (
102+
<>
103+
<span css={dividerCss}></span>
104+
<SvgMediaControlsPlay size="small" theme={themeIcon} />
105+
<span>{audioDuration}</span>
106+
</>
107+
)}
108+
</Button>
109+
</div>
110+
);
111+
};

dotcom-rendering/src/components/WaveForm.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ function generateWaveform(seed: string, bars: number, height: number) {
6868
return compress(peaks, height * minimumBarHeight);
6969
}
7070

71-
type Theme = {
71+
export type WaveFormTheme = {
7272
progress?: string;
7373
buffer?: string;
7474
wave?: string;
7575
};
7676

77-
const defaultTheme: Theme = {
77+
const defaultTheme: WaveFormTheme = {
7878
progress: 'green',
7979
buffer: 'orange',
8080
wave: 'grey',
8181
};
8282

83-
type Props = {
83+
export type WaveFormProps = {
8484
/**
8585
* The same seed will generate the same waveform. For example, passing the url
8686
* as the seed will ensure the waveform is the same for the same audio file.
@@ -90,7 +90,7 @@ type Props = {
9090
bars: number;
9191
progress?: number;
9292
buffer?: number;
93-
theme?: Theme;
93+
theme?: WaveFormTheme;
9494
gap?: number;
9595
barWidth?: number;
9696
} & React.SVGProps<SVGSVGElement>;
@@ -105,7 +105,7 @@ export const WaveForm = ({
105105
gap = 1,
106106
barWidth = 4,
107107
...props
108-
}: Props) => {
108+
}: WaveFormProps) => {
109109
// memoise the waveform data so they aren't recalculated on every render
110110
const barHeights = useMemo(
111111
() => generateWaveform(seed, bars, height),

dotcom-rendering/src/paletteDeclarations.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7284,6 +7284,10 @@ const paletteColours = {
72847284
light: linkKickerTextLight,
72857285
dark: linkKickerTextDark,
72867286
},
7287+
'--listen-to-article-waveform': {
7288+
light: () => sourcePalette.neutral[86],
7289+
dark: () => sourcePalette.neutral[38],
7290+
},
72877291
'--live-block-border-bottom': {
72887292
light: liveBlockBorderBottomLight,
72897293
dark: liveBlockBorderBottomDark,

0 commit comments

Comments
 (0)