@times-components/ts-components 1.112.0 → 1.112.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/dist/components/article-audio/ArticleAudio.d.ts +6 -0
- package/dist/components/article-audio/ArticleAudio.js +51 -0
- package/dist/components/article-audio/ArticleAudio.stories.d.ts +1 -0
- package/dist/components/article-audio/ArticleAudio.stories.js +8 -0
- package/dist/components/article-audio/__tests__/ArticleAudio.test.d.ts +1 -0
- package/dist/components/article-audio/__tests__/ArticleAudio.test.js +129 -0
- package/dist/components/article-audio/__tests__/Styles.test.d.ts +2 -0
- package/dist/components/article-audio/__tests__/Styles.test.js +45 -0
- package/dist/components/article-audio/styles.d.ts +1 -0
- package/dist/components/article-audio/styles.js +26 -0
- package/dist/components/audio-player-components/AudioPlayer.d.ts +3 -0
- package/dist/components/audio-player-components/AudioPlayer.js +180 -0
- package/dist/components/audio-player-components/AudioPlayer.stories.d.ts +1 -0
- package/dist/components/audio-player-components/AudioPlayer.stories.js +204 -0
- package/dist/components/audio-player-components/CollapseIcon.d.ts +3 -0
- package/dist/components/audio-player-components/CollapseIcon.js +12 -0
- package/dist/components/audio-player-components/PlaybackControls.d.ts +3 -0
- package/dist/components/audio-player-components/PlaybackControls.js +32 -0
- package/dist/components/audio-player-components/SeekBar.d.ts +3 -0
- package/dist/components/audio-player-components/SeekBar.js +8 -0
- package/dist/components/audio-player-components/TabletDesktopPlayer.d.ts +3 -0
- package/dist/components/audio-player-components/TabletDesktopPlayer.js +37 -0
- package/dist/components/audio-player-components/TimeDisplay.d.ts +3 -0
- package/dist/components/audio-player-components/TimeDisplay.js +10 -0
- package/dist/components/audio-player-components/TitleScroller.d.ts +3 -0
- package/dist/components/audio-player-components/TitleScroller.js +8 -0
- package/dist/components/audio-player-components/__tests__/AudioPlayer.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/AudioPlayer.test.js +196 -0
- package/dist/components/audio-player-components/__tests__/CollapseIcon.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/CollapseIcon.test.js +44 -0
- package/dist/components/audio-player-components/__tests__/PlaybackControls.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/PlaybackControls.test.js +129 -0
- package/dist/components/audio-player-components/__tests__/SeekBar.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/SeekBar.test.js +60 -0
- package/dist/components/audio-player-components/__tests__/Styles.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/Styles.test.js +510 -0
- package/dist/components/audio-player-components/__tests__/TabletDesktopPlayer.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/TabletDesktopPlayer.test.js +199 -0
- package/dist/components/audio-player-components/__tests__/TimeDisplay.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/TimeDisplay.test.js +71 -0
- package/dist/components/audio-player-components/__tests__/TitleScroller.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/TitleScroller.test.js +47 -0
- package/dist/components/audio-player-components/__tests__/Utils.test.d.ts +1 -0
- package/dist/components/audio-player-components/__tests__/Utils.test.js +31 -0
- package/dist/components/audio-player-components/styles.d.ts +49 -0
- package/dist/components/audio-player-components/styles.js +568 -0
- package/dist/components/audio-player-components/types.d.ts +83 -0
- package/dist/components/audio-player-components/types.js +2 -0
- package/dist/components/audio-player-components/utils.d.ts +1 -0
- package/dist/components/audio-player-components/utils.js +6 -0
- package/dist/fixtures/analytics-actions/__tests__/AnalyticsActions.test.d.ts +1 -0
- package/dist/fixtures/analytics-actions/__tests__/AnalyticsActions.test.js +49 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/jest.config.js +3 -3
- package/package.json +3 -3
- package/rnw.js +1 -1
- package/src/components/article-audio/ArticleAudio.stories.tsx +14 -0
- package/src/components/article-audio/ArticleAudio.tsx +93 -0
- package/src/components/article-audio/__tests__/ArticleAudio.test.tsx +192 -0
- package/src/components/article-audio/__tests__/Styles.test.tsx +61 -0
- package/src/components/article-audio/styles.ts +26 -0
- package/src/components/audio-player-components/AudioPlayer.stories.tsx +209 -0
- package/src/components/audio-player-components/AudioPlayer.tsx +324 -0
- package/src/components/audio-player-components/CollapseIcon.tsx +25 -0
- package/src/components/audio-player-components/PlaybackControls.tsx +104 -0
- package/src/components/audio-player-components/SeekBar.tsx +27 -0
- package/src/components/audio-player-components/TabletDesktopPlayer.tsx +157 -0
- package/src/components/audio-player-components/TimeDisplay.tsx +18 -0
- package/src/components/audio-player-components/TitleScroller.tsx +13 -0
- package/src/components/audio-player-components/__tests__/AudioPlayer.test.tsx +267 -0
- package/src/components/audio-player-components/__tests__/CollapseIcon.test.tsx +89 -0
- package/src/components/audio-player-components/__tests__/PlaybackControls.test.tsx +330 -0
- package/src/components/audio-player-components/__tests__/SeekBar.test.tsx +96 -0
- package/src/components/audio-player-components/__tests__/Styles.test.tsx +777 -0
- package/src/components/audio-player-components/__tests__/TabletDesktopPlayer.test.tsx +304 -0
- package/src/components/audio-player-components/__tests__/TimeDisplay.test.tsx +103 -0
- package/src/components/audio-player-components/__tests__/TitleScroller.test.tsx +60 -0
- package/src/components/audio-player-components/__tests__/Utils.test.tsx +37 -0
- package/src/components/audio-player-components/__tests__/__snapshots__/Styles.test.tsx.snap +3 -0
- package/src/components/audio-player-components/__tests__/__snapshots__/TitleScroller.test.tsx.snap +19 -0
- package/src/components/audio-player-components/styles.ts +631 -0
- package/src/components/audio-player-components/types.ts +90 -0
- package/src/components/audio-player-components/utils.ts +5 -0
- package/src/fixtures/analytics-actions/__tests__/AnalyticsActions.test.tsx +62 -0
- package/src/index.ts +1 -0
- package/src/types/externs.d.ts +9 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { storiesOf } from '@storybook/react';
|
|
3
|
+
import { ArticleAudio } from './ArticleAudio';
|
|
4
|
+
|
|
5
|
+
storiesOf('Typescript Component/Article Audio', module).add(
|
|
6
|
+
'Article Audio',
|
|
7
|
+
() => {
|
|
8
|
+
return (
|
|
9
|
+
<div style={{ padding: '10px' }}>
|
|
10
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import React, { FC, useState, useRef } from 'react';
|
|
2
|
+
import { AudioButton } from './styles';
|
|
3
|
+
import { AudioPlayer } from '../audio-player-components/AudioPlayer';
|
|
4
|
+
import { PlayIcon, PauseIcon } from '@times-components/icons';
|
|
5
|
+
export interface ArticleAudioProps {
|
|
6
|
+
audioSrc: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const ArticleAudio: FC<ArticleAudioProps> = ({ audioSrc }) => {
|
|
10
|
+
const [audioState, setAudioState] = useState<
|
|
11
|
+
'not-started' | 'playing' | 'paused'
|
|
12
|
+
>('not-started');
|
|
13
|
+
const [isAudioPlayerVisible, setisAudioPlayerVisible] = useState<boolean>(
|
|
14
|
+
false
|
|
15
|
+
);
|
|
16
|
+
const [duration, setDuration] = useState<string | null>(null);
|
|
17
|
+
|
|
18
|
+
const audioRef = useRef<HTMLAudioElement | null>(null);
|
|
19
|
+
|
|
20
|
+
const handleLoadedMetadata = () => {
|
|
21
|
+
if (audioRef.current) {
|
|
22
|
+
const totalSeconds = Math.floor(audioRef.current.duration);
|
|
23
|
+
const minutes = Math.floor(totalSeconds / 60) + 1;
|
|
24
|
+
setDuration(`${minutes}`);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const handlePlayPause = () => {
|
|
29
|
+
setisAudioPlayerVisible(true);
|
|
30
|
+
|
|
31
|
+
if (audioState === 'playing') {
|
|
32
|
+
setAudioState('paused');
|
|
33
|
+
} else {
|
|
34
|
+
setAudioState('playing');
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const hidePlayer = () => {
|
|
39
|
+
setisAudioPlayerVisible(false);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div>
|
|
44
|
+
<audio
|
|
45
|
+
ref={audioRef}
|
|
46
|
+
src={audioSrc}
|
|
47
|
+
onLoadedMetadata={handleLoadedMetadata}
|
|
48
|
+
preload="metadata"
|
|
49
|
+
/>
|
|
50
|
+
<AudioButton
|
|
51
|
+
onClick={handlePlayPause}
|
|
52
|
+
style={{
|
|
53
|
+
backgroundColor: audioState !== 'not-started' ? '#1D1D1B' : 'unset',
|
|
54
|
+
color: audioState === 'not-started' ? '#333' : '#fff'
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
{audioState === 'playing' ? (
|
|
58
|
+
<>
|
|
59
|
+
<PauseIcon width={16} height={16} fill="#fff" /> Playing
|
|
60
|
+
</>
|
|
61
|
+
) : audioState === 'paused' ? (
|
|
62
|
+
<>
|
|
63
|
+
<PlayIcon width={16} height={16} fill="#fff" /> Paused
|
|
64
|
+
</>
|
|
65
|
+
) : (
|
|
66
|
+
<>
|
|
67
|
+
<PlayIcon width={16} height={16} /> Listen
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
<span
|
|
71
|
+
style={{
|
|
72
|
+
color: audioState === 'not-started' ? '#696969' : '#fff'
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
{' '}
|
|
76
|
+
{duration} min
|
|
77
|
+
</span>
|
|
78
|
+
</AudioButton>
|
|
79
|
+
{isAudioPlayerVisible && (
|
|
80
|
+
<AudioPlayer
|
|
81
|
+
src={audioSrc}
|
|
82
|
+
isPlayingProp={audioState === 'playing'}
|
|
83
|
+
onPlay={() => setAudioState('playing')}
|
|
84
|
+
onPause={() => setAudioState('paused')}
|
|
85
|
+
onEnded={() => setAudioState('not-started')}
|
|
86
|
+
onClose={() => hidePlayer()}
|
|
87
|
+
/>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default ArticleAudio;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, act } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import { ArticleAudio } from '../ArticleAudio';
|
|
5
|
+
|
|
6
|
+
jest.mock('../styles', () => ({
|
|
7
|
+
AudioButton: ({ children, onClick, style }: any) => (
|
|
8
|
+
<button data-testid="audio-button" onClick={onClick} style={style}>
|
|
9
|
+
{children}
|
|
10
|
+
</button>
|
|
11
|
+
)
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
jest.mock('@times-components/icons', () => ({
|
|
15
|
+
__esModule: true,
|
|
16
|
+
PlayIcon: ({ color }: any) => (
|
|
17
|
+
<svg data-testid="play-icon" style={{ color: color || '#333' }} />
|
|
18
|
+
),
|
|
19
|
+
PauseIcon: ({ color }: any) => (
|
|
20
|
+
<svg data-testid="pause-icon" style={{ color: color || '#333' }} />
|
|
21
|
+
)
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
jest.mock('../../audio-player-components/AudioPlayer', () => ({
|
|
25
|
+
AudioPlayer: ({ onPlay, onPause, onEnded, onClose }: any) => (
|
|
26
|
+
<div data-testid="audio-player">
|
|
27
|
+
<button onClick={onPlay}>Play</button>
|
|
28
|
+
<button onClick={onPause}>Pause</button>
|
|
29
|
+
<button onClick={onEnded}>Ended</button>
|
|
30
|
+
<button onClick={onClose}>Close</button>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
describe('ArticleAudio', () => {
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
// Mock the duration of the audio element
|
|
38
|
+
Object.defineProperty(HTMLMediaElement.prototype, 'duration', {
|
|
39
|
+
get(): number {
|
|
40
|
+
return 120; // 2 minutes
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
jest.clearAllMocks();
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('renders audio button with correct initial state', () => {
|
|
50
|
+
const { getByTestId, getByText, container } = render(
|
|
51
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Trigger the 'loadedmetadata' event to set the duration
|
|
55
|
+
const audio = container.querySelector('audio') as HTMLAudioElement;
|
|
56
|
+
act(() => {
|
|
57
|
+
fireEvent.loadedMetadata(audio);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const audioButton = getByTestId('audio-button');
|
|
61
|
+
expect(audioButton).toBeInTheDocument();
|
|
62
|
+
|
|
63
|
+
expect(audioButton.style.backgroundColor).toBe('');
|
|
64
|
+
expect(audioButton).toHaveStyle('color: #333');
|
|
65
|
+
|
|
66
|
+
// The initial state should display 'Listen' and the duration
|
|
67
|
+
expect(getByText('Listen')).toBeInTheDocument();
|
|
68
|
+
expect(getByText('3 min')).toBeInTheDocument();
|
|
69
|
+
|
|
70
|
+
// Since audioState is 'not-started', duration color should be '#696969'
|
|
71
|
+
const durationSpan = getByText('3 min');
|
|
72
|
+
expect(durationSpan).toHaveStyle('color: #696969');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('hides AudioPlayer when close button is clicked (using mocked AudioPlayer)', () => {
|
|
76
|
+
const { getByTestId, queryByTestId, container, getByText } = render(
|
|
77
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Trigger the 'loadedmetadata' event to set the duration
|
|
81
|
+
const audio = container.querySelector('audio') as HTMLAudioElement;
|
|
82
|
+
act(() => {
|
|
83
|
+
fireEvent.loadedMetadata(audio);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Initially, the AudioPlayer should not be visible
|
|
87
|
+
expect(queryByTestId('audio-player')).not.toBeInTheDocument();
|
|
88
|
+
|
|
89
|
+
// Click the audio button to start playback
|
|
90
|
+
const audioButton = getByTestId('audio-button');
|
|
91
|
+
fireEvent.click(audioButton);
|
|
92
|
+
|
|
93
|
+
// The mocked AudioPlayer should now be visible
|
|
94
|
+
expect(getByTestId('audio-player')).toBeInTheDocument();
|
|
95
|
+
|
|
96
|
+
// Use the mocked Close button inside the AudioPlayer to close it
|
|
97
|
+
const closeButton = getByText('Close');
|
|
98
|
+
fireEvent.click(closeButton);
|
|
99
|
+
|
|
100
|
+
// The AudioPlayer should no longer be visible
|
|
101
|
+
expect(queryByTestId('audio-player')).not.toBeInTheDocument();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('handles play and pause', () => {
|
|
105
|
+
const { getByTestId, getByText, container } = render(
|
|
106
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
// Trigger the 'loadedmetadata' event to set the duration
|
|
110
|
+
const audio = container.querySelector('audio') as HTMLAudioElement;
|
|
111
|
+
act(() => {
|
|
112
|
+
fireEvent.loadedMetadata(audio);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const audioButton = getByTestId('audio-button');
|
|
116
|
+
|
|
117
|
+
// Simulate clicking the play button
|
|
118
|
+
fireEvent.click(audioButton);
|
|
119
|
+
|
|
120
|
+
// Now, audioState should be 'playing'
|
|
121
|
+
expect(audioButton).toHaveStyle('background-color: #1D1D1B');
|
|
122
|
+
expect(audioButton).toHaveStyle('color: #fff');
|
|
123
|
+
expect(getByText('Playing')).toBeInTheDocument();
|
|
124
|
+
|
|
125
|
+
// Since audioState is 'playing', duration color should be '#fff'
|
|
126
|
+
const durationSpan = getByText('3 min');
|
|
127
|
+
expect(durationSpan).toHaveStyle('color: #fff');
|
|
128
|
+
|
|
129
|
+
// Simulate clicking the pause button
|
|
130
|
+
fireEvent.click(audioButton);
|
|
131
|
+
|
|
132
|
+
expect(getByText('Paused')).toBeInTheDocument();
|
|
133
|
+
expect(audioButton).toHaveStyle('background-color: #1D1D1B');
|
|
134
|
+
expect(audioButton).toHaveStyle('color: #fff');
|
|
135
|
+
|
|
136
|
+
// Simulate clicking the play button again
|
|
137
|
+
fireEvent.click(audioButton);
|
|
138
|
+
|
|
139
|
+
expect(getByText('Playing')).toBeInTheDocument();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test('shows AudioPlayer when audio is played', () => {
|
|
143
|
+
const { getByTestId, queryByTestId, container } = render(
|
|
144
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
// Trigger the 'loadedmetadata' event to set the duration
|
|
148
|
+
const audio = container.querySelector('audio') as HTMLAudioElement;
|
|
149
|
+
act(() => {
|
|
150
|
+
fireEvent.loadedMetadata(audio);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(queryByTestId('audio-player')).not.toBeInTheDocument();
|
|
154
|
+
|
|
155
|
+
const audioButton = getByTestId('audio-button');
|
|
156
|
+
fireEvent.click(audioButton);
|
|
157
|
+
|
|
158
|
+
expect(getByTestId('audio-player')).toBeInTheDocument();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('updates audioState based on AudioPlayer callbacks', () => {
|
|
162
|
+
const { getByTestId, getByText, container } = render(
|
|
163
|
+
<ArticleAudio audioSrc="https://www.kozco.com/tech/LRMonoPhase4.mp3" />
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Trigger the 'loadedmetadata' event to set the duration
|
|
167
|
+
const audio = container.querySelector('audio') as HTMLAudioElement;
|
|
168
|
+
act(() => {
|
|
169
|
+
fireEvent.loadedMetadata(audio);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const audioButton = getByTestId('audio-button');
|
|
173
|
+
fireEvent.click(audioButton); // Start playing
|
|
174
|
+
|
|
175
|
+
expect(getByText('Playing')).toBeInTheDocument();
|
|
176
|
+
|
|
177
|
+
const pauseButton = getByText('Pause');
|
|
178
|
+
fireEvent.click(pauseButton);
|
|
179
|
+
|
|
180
|
+
expect(getByText('Paused')).toBeInTheDocument();
|
|
181
|
+
|
|
182
|
+
const playButton = getByText('Play');
|
|
183
|
+
fireEvent.click(playButton);
|
|
184
|
+
|
|
185
|
+
expect(getByText('Playing')).toBeInTheDocument();
|
|
186
|
+
|
|
187
|
+
const endedButton = getByText('Ended');
|
|
188
|
+
fireEvent.click(endedButton);
|
|
189
|
+
|
|
190
|
+
expect(getByText('Listen')).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom/extend-expect';
|
|
4
|
+
import 'jest-styled-components';
|
|
5
|
+
import { AudioButton } from '../styles';
|
|
6
|
+
|
|
7
|
+
describe('AudioButton', () => {
|
|
8
|
+
test('renders correctly with default styles', () => {
|
|
9
|
+
const { getByTestId } = render(
|
|
10
|
+
<AudioButton data-testid="audio-button">Test Button</AudioButton>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
const button = getByTestId('audio-button');
|
|
14
|
+
|
|
15
|
+
expect(button).toHaveStyleRule('background-color', 'unset');
|
|
16
|
+
expect(button).toHaveStyleRule('border-radius', '0');
|
|
17
|
+
expect(button).toHaveStyleRule('padding', '7px 11px');
|
|
18
|
+
expect(button).toHaveStyleRule('border', '1px solid #333333');
|
|
19
|
+
expect(button).toHaveStyleRule('display', 'flex');
|
|
20
|
+
expect(button).toHaveStyleRule('align-items', 'center');
|
|
21
|
+
expect(button).toHaveStyleRule('color', '#333333');
|
|
22
|
+
expect(button).toHaveStyleRule('font-family', 'Roboto');
|
|
23
|
+
expect(button).toHaveStyleRule('font-weight', '500');
|
|
24
|
+
expect(button).toHaveStyleRule('font-size', '14px');
|
|
25
|
+
expect(button).toHaveStyleRule('line-height', '18px');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test('renders svg child with correct styles', () => {
|
|
29
|
+
const { getByTestId } = render(
|
|
30
|
+
<AudioButton data-testid="audio-button">
|
|
31
|
+
<svg data-testid="icon" />
|
|
32
|
+
</AudioButton>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const button = getByTestId('audio-button');
|
|
36
|
+
|
|
37
|
+
expect(button).toHaveStyleRule('margin-right', '8px', {
|
|
38
|
+
modifier: 'svg'
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('renders span child with correct styles', () => {
|
|
43
|
+
const { getByTestId } = render(
|
|
44
|
+
<AudioButton data-testid="audio-button">
|
|
45
|
+
<span data-testid="span">Test Span</span>
|
|
46
|
+
</AudioButton>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const button = getByTestId('audio-button');
|
|
50
|
+
|
|
51
|
+
expect(button).toHaveStyleRule('margin-left', '4px', {
|
|
52
|
+
modifier: 'span'
|
|
53
|
+
});
|
|
54
|
+
expect(button).toHaveStyleRule('font-size', '12px', {
|
|
55
|
+
modifier: 'span'
|
|
56
|
+
});
|
|
57
|
+
expect(button).toHaveStyleRule('color', '#696969', {
|
|
58
|
+
modifier: 'span'
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
import { colours } from '@times-components/ts-styleguide';
|
|
3
|
+
|
|
4
|
+
export const AudioButton = styled.button`
|
|
5
|
+
background-color: unset;
|
|
6
|
+
border-radius: 0;
|
|
7
|
+
padding: 7px 11px;
|
|
8
|
+
border: 1px solid ${colours.functional.primary};
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
color: ${colours.functional.primary};
|
|
12
|
+
font-family: Roboto;
|
|
13
|
+
font-weight: 500;
|
|
14
|
+
font-size: 14px;
|
|
15
|
+
line-height: 18px;
|
|
16
|
+
|
|
17
|
+
svg {
|
|
18
|
+
margin-right: 8px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
span {
|
|
22
|
+
margin-left: 4px;
|
|
23
|
+
font-size: 12px;
|
|
24
|
+
color: ${colours.functional.secondary};
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { storiesOf } from '@storybook/react';
|
|
3
|
+
import { AudioPlayer } from './AudioPlayer';
|
|
4
|
+
import { withKnobs, boolean, number, text } from '@storybook/addon-knobs';
|
|
5
|
+
|
|
6
|
+
storiesOf('Typescript Component/Audio Player Components', module)
|
|
7
|
+
.addDecorator(withKnobs)
|
|
8
|
+
.addParameters({
|
|
9
|
+
component: AudioPlayer,
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: `
|
|
13
|
+
|
|
14
|
+
A customizable audio player component with various controls.
|
|
15
|
+
|
|
16
|
+
## Props
|
|
17
|
+
|
|
18
|
+
### \`src\` (Required)
|
|
19
|
+
|
|
20
|
+
- **Type**: \`string\`
|
|
21
|
+
- **Description**: The URL of the audio source file to be played.
|
|
22
|
+
|
|
23
|
+
### \`title\`
|
|
24
|
+
|
|
25
|
+
- **Type**: \`string\`
|
|
26
|
+
- **Default**: \`'Audio Title'\`
|
|
27
|
+
- **Description**: The title of the audio track displayed in the player.
|
|
28
|
+
|
|
29
|
+
### \`autoPlay\`
|
|
30
|
+
|
|
31
|
+
- **Type**: \`boolean\`
|
|
32
|
+
- **Default**: \`false\`
|
|
33
|
+
- **Description**: Determines whether the audio should start playing automatically when the component mounts.
|
|
34
|
+
|
|
35
|
+
### \`initialVolume\`
|
|
36
|
+
|
|
37
|
+
- **Type**: \`number\` (Range between \`0\` and \`1\`)
|
|
38
|
+
- **Default**: \`1\`
|
|
39
|
+
- **Description**: Sets the initial volume level of the audio player.
|
|
40
|
+
|
|
41
|
+
### \`playbackRate\`
|
|
42
|
+
|
|
43
|
+
- **Type**: \`number\`
|
|
44
|
+
- **Default**: \`1\`
|
|
45
|
+
- **Description**: Sets the initial playback speed of the audio.
|
|
46
|
+
|
|
47
|
+
### \`isPlayingProp\`
|
|
48
|
+
|
|
49
|
+
- **Type**: \`boolean\`
|
|
50
|
+
- **Description**: Controls the play/pause state externally. When provided, it overrides the internal state.
|
|
51
|
+
|
|
52
|
+
### \`isExpandedProp\`
|
|
53
|
+
|
|
54
|
+
- **Type**: \`boolean\`
|
|
55
|
+
- **Description**: Controls the expanded/collapsed state externally. When provided, it overrides the internal state.
|
|
56
|
+
|
|
57
|
+
### \`allowTogglePlay\`
|
|
58
|
+
|
|
59
|
+
- **Type**: \`boolean\`
|
|
60
|
+
- **Default**: \`true\`
|
|
61
|
+
- **Description**: Enables or disables the play/pause functionality.
|
|
62
|
+
|
|
63
|
+
### \`allowSeek\`
|
|
64
|
+
|
|
65
|
+
- **Type**: \`boolean\`
|
|
66
|
+
- **Default**: \`true\`
|
|
67
|
+
- **Description**: Enables or disables the ability to seek through the audio track.
|
|
68
|
+
|
|
69
|
+
### \`allowVolumeChange\`
|
|
70
|
+
|
|
71
|
+
- **Type**: \`boolean\`
|
|
72
|
+
- **Default**: \`true\`
|
|
73
|
+
- **Description**: Enables or disables the volume control. Volume control is available on tablet and desktop views.
|
|
74
|
+
|
|
75
|
+
### \`allowPlaybackRateChange\`
|
|
76
|
+
|
|
77
|
+
- **Type**: \`boolean\`
|
|
78
|
+
- **Default**: \`true\`
|
|
79
|
+
- **Description**: Enables or disables the ability to change the playback speed.
|
|
80
|
+
|
|
81
|
+
### \`allowExpandCollapse\`
|
|
82
|
+
|
|
83
|
+
- **Type**: \`boolean\`
|
|
84
|
+
- **Default**: \`true\`
|
|
85
|
+
- **Description**: Enables or disables the ability to expand or collapse the audio player interface.
|
|
86
|
+
|
|
87
|
+
### \`onPlay\`
|
|
88
|
+
|
|
89
|
+
- **Type**: \`() => void\`
|
|
90
|
+
- **Description**: Callback function invoked when the audio starts playing.
|
|
91
|
+
|
|
92
|
+
### \`onPause\`
|
|
93
|
+
|
|
94
|
+
- **Type**: \`() => void\`
|
|
95
|
+
- **Description**: Callback function invoked when the audio is paused.
|
|
96
|
+
|
|
97
|
+
### \`onEnded\`
|
|
98
|
+
|
|
99
|
+
- **Type**: \`() => void\`
|
|
100
|
+
- **Description**: Callback function invoked when the audio playback ends.
|
|
101
|
+
|
|
102
|
+
### \`onTimeUpdate\`
|
|
103
|
+
|
|
104
|
+
- **Type**: \`(currentTime: number) => void\`
|
|
105
|
+
- **Description**: Callback function invoked when the current playback time updates.
|
|
106
|
+
|
|
107
|
+
### \`onVolumeChange\`
|
|
108
|
+
|
|
109
|
+
- **Type**: \`(volume: number) => void\`
|
|
110
|
+
- **Description**: Callback function invoked when the volume level changes.
|
|
111
|
+
|
|
112
|
+
### \`onPlaybackRateChange\`
|
|
113
|
+
|
|
114
|
+
- **Type**: \`(rate: number) => void\`
|
|
115
|
+
- **Description**: Callback function invoked when the playback rate changes.
|
|
116
|
+
|
|
117
|
+
### \`onSeek\`
|
|
118
|
+
|
|
119
|
+
- **Type**: \`(time: number) => void\`
|
|
120
|
+
- **Description**: Callback function invoked when the playback position changes due to seeking.
|
|
121
|
+
|
|
122
|
+
### \`onClose\`
|
|
123
|
+
|
|
124
|
+
- **Type**: \`() => void\`
|
|
125
|
+
- **Description**: Callback function invoked when the audio player is closed.
|
|
126
|
+
|
|
127
|
+
## Usage Examples
|
|
128
|
+
|
|
129
|
+
### Basic Usage
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
\`\`\`jsx
|
|
133
|
+
<AudioPlayer src="https://example.com/audio-file.mp3" />
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
### With Custom Title and AutoPlay
|
|
137
|
+
|
|
138
|
+
\`\`\`jsx
|
|
139
|
+
<AudioPlayer
|
|
140
|
+
src="https://example.com/audio-file.mp3"
|
|
141
|
+
title="Episode 1: The Beginning"
|
|
142
|
+
autoPlay={true}
|
|
143
|
+
/>
|
|
144
|
+
\`\`\`
|
|
145
|
+
|
|
146
|
+
### Handling Events
|
|
147
|
+
|
|
148
|
+
\`\`\`jsx
|
|
149
|
+
<AudioPlayer
|
|
150
|
+
src="https://example.com/audio-file.mp3"
|
|
151
|
+
onPlay={() => console.log('Playing')}
|
|
152
|
+
onPause={() => console.log('Paused')}
|
|
153
|
+
onEnded={() => console.log('Ended')}
|
|
154
|
+
onTimeUpdate={(currentTime) => console.log('Current Time:', currentTime)}
|
|
155
|
+
onVolumeChange={(volume) => console.log('Volume:', volume)}
|
|
156
|
+
onPlaybackRateChange={(rate) => console.log('Playback Rate:', rate)}
|
|
157
|
+
onSeek={(time) => console.log('Seeked to:', time)}
|
|
158
|
+
onClose={() => console.log('Player Closed')}
|
|
159
|
+
/>
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
`
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
.add('Default Audio Player', () => {
|
|
167
|
+
const src = text(
|
|
168
|
+
'src',
|
|
169
|
+
'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3'
|
|
170
|
+
);
|
|
171
|
+
const title = text('title', 'Sample Audio Title for Testing');
|
|
172
|
+
const autoPlay = boolean('autoPlay', false);
|
|
173
|
+
const initialVolume = number('initialVolume', 0.5, {
|
|
174
|
+
range: true,
|
|
175
|
+
min: 0,
|
|
176
|
+
max: 1,
|
|
177
|
+
step: 0.1
|
|
178
|
+
});
|
|
179
|
+
const playbackRate = number('playbackRate', 1, {
|
|
180
|
+
range: true,
|
|
181
|
+
min: 0.5,
|
|
182
|
+
max: 2,
|
|
183
|
+
step: 0.1
|
|
184
|
+
});
|
|
185
|
+
const isPlayingProp = boolean('isPlayingProp', false);
|
|
186
|
+
const isExpandedProp = boolean('isExpandedProp', true);
|
|
187
|
+
const allowTogglePlay = boolean('allowTogglePlay', true);
|
|
188
|
+
const allowSeek = boolean('allowSeek', true);
|
|
189
|
+
const allowVolumeChange = boolean('allowVolumeChange', true);
|
|
190
|
+
const allowPlaybackRateChange = boolean('allowPlaybackRateChange', true);
|
|
191
|
+
const allowExpandCollapse = boolean('allowExpandCollapse', true);
|
|
192
|
+
|
|
193
|
+
const mockProps = {
|
|
194
|
+
src,
|
|
195
|
+
title,
|
|
196
|
+
autoPlay,
|
|
197
|
+
initialVolume,
|
|
198
|
+
playbackRate,
|
|
199
|
+
isPlayingProp,
|
|
200
|
+
isExpandedProp,
|
|
201
|
+
allowTogglePlay,
|
|
202
|
+
allowSeek,
|
|
203
|
+
allowVolumeChange,
|
|
204
|
+
allowPlaybackRateChange,
|
|
205
|
+
allowExpandCollapse
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
return <AudioPlayer {...mockProps} />;
|
|
209
|
+
});
|