Skip to content

Commit 5378ccb

Browse files
committed
feat: refactor banner, update prop api and types
1 parent e240ea9 commit 5378ccb

File tree

5 files changed

+343
-353
lines changed

5 files changed

+343
-353
lines changed

src/components/ParallaxBanner/__snapshots__/index.test.tsx.snap

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,13 @@
33
exports[`given a <ParallaxBanner> component with all props then it will render banners correctly 1`] = `
44
<DocumentFragment>
55
<div
6-
class="parallax-banner test-class"
7-
style="position: relative; overflow: hidden; width: 100%; height: 50vh; background-color: blue; border: 1px solid red;"
6+
class="test-class"
7+
style="position: relative; overflow: hidden; width: 100%; background-color: blue; border: 1px solid red;"
88
>
99
<div
10-
style="position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; will-change: transform; transform: translateY(-20px);"
11-
>
12-
<div
13-
class="parallax-banner-layer-0"
14-
data-testid="layer-0"
15-
style="background-image: url(https://foo.com/bar.jpg); background-position: center; background-size: cover; position: absolute; top: -20px; right: 0px; bottom: -20px; left: 0px;"
16-
/>
17-
</div>
10+
data-testid="layer-0"
11+
style="background-image: url(https://foo.com/bar.jpg); background-position: center; background-size: cover; position: absolute; top: -20px; left: 0px; right: 0px; bottom: -20px; will-change: transform; transform: translateY(-20px);"
12+
/>
1813
<div>
1914
<h1>
2015
Foo Bar

src/components/ParallaxBanner/index.test.tsx

Lines changed: 71 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,8 @@ describe('given a <ParallaxBanner> component', () => {
1111
<ParallaxBanner
1212
className="test-class"
1313
disabled={false}
14-
layers={[
15-
{
16-
image: 'https://foo.com/bar.jpg',
17-
speed: 2,
18-
},
19-
]}
20-
style={{
21-
backgroundColor: 'blue',
22-
border: '1px solid red',
23-
}}
14+
layers={[{ image: 'https://foo.com/bar.jpg', speed: 2 }]}
15+
style={{ backgroundColor: 'blue', border: '1px solid red' }}
2416
>
2517
<div>
2618
<h1>Foo Bar</h1>
@@ -31,21 +23,26 @@ describe('given a <ParallaxBanner> component', () => {
3123
expect(asFragment()).toMatchSnapshot();
3224
});
3325
});
34-
35-
describe('with custom defined layer children', () => {
26+
describe('when children are defined', () => {
27+
it('then it will render the children', () => {
28+
const { getByTestId } = render(
29+
<ParallaxProvider>
30+
<ParallaxBanner>
31+
<div data-testid="children" />
32+
</ParallaxBanner>
33+
</ParallaxProvider>
34+
);
35+
expect(getByTestId('children')).toBeInTheDocument();
36+
});
37+
});
38+
describe('when custom defined layer children are defined', () => {
3639
it('then it will render each layer child', () => {
3740
const { getByTestId } = render(
3841
<ParallaxProvider>
3942
<ParallaxBanner
4043
layers={[
41-
{
42-
children: <div data-testid="foo">foo</div>,
43-
speed: 2,
44-
},
45-
{
46-
children: <div data-testid="bar">bar</div>,
47-
speed: 4,
48-
},
44+
{ children: <div data-testid="foo">foo</div> },
45+
{ children: <div data-testid="bar">bar</div> },
4946
]}
5047
/>
5148
</ParallaxProvider>
@@ -55,18 +52,11 @@ describe('given a <ParallaxBanner> component', () => {
5552
});
5653
});
5754

58-
describe('with layer expanded false', () => {
55+
describe('when the layer expanded option is false', () => {
5956
it('then it will render without expanded styles', () => {
6057
const { getByTestId } = render(
6158
<ParallaxProvider>
62-
<ParallaxBanner
63-
layers={[
64-
{
65-
speed: 2,
66-
expanded: false,
67-
},
68-
]}
69-
/>
59+
<ParallaxBanner layers={[{ speed: 2, expanded: false }]} />
7060
</ParallaxProvider>
7161
);
7262
expect(getByTestId('layer-0').style.top).toBe('0px');
@@ -80,61 +70,85 @@ describe('given a <ParallaxBanner> component', () => {
8070
describe('with layer expanded', () => {
8171
it('then it will render with expanded styles based on speed', () => {
8272
const { getByTestId } = render(
73+
<ParallaxProvider>
74+
<ParallaxBanner layers={[{ speed: 2 }]} />
75+
</ParallaxProvider>
76+
);
77+
expect(getByTestId('layer-0').style.top).toBe('-20px');
78+
expect(getByTestId('layer-0').style.right).toBe('0px');
79+
expect(getByTestId('layer-0').style.left).toBe('0px');
80+
expect(getByTestId('layer-0').style.bottom).toBe('-20px');
81+
expect(getByTestId('layer-0').style.position).toBe('absolute');
82+
});
83+
});
84+
describe('with custom props', () => {
85+
it('then it will render children', () => {
86+
const { container, getByTestId } = render(
8387
<ParallaxProvider>
8488
<ParallaxBanner
8589
layers={[
8690
{
8791
speed: 2,
92+
style: {
93+
backgroundColor: 'red',
94+
},
95+
className: 'my-custom-class',
8896
},
8997
]}
9098
/>
9199
</ParallaxProvider>
92100
);
93-
expect(getByTestId('layer-0').style.top).toBe('-20px');
94-
expect(getByTestId('layer-0').style.right).toBe('0px');
95-
expect(getByTestId('layer-0').style.left).toBe('0px');
96-
expect(getByTestId('layer-0').style.bottom).toBe('-20px');
97-
expect(getByTestId('layer-0').style.position).toBe('absolute');
101+
expect(container.querySelector('.my-custom-class')).toBeInTheDocument();
102+
expect(getByTestId('layer-0').style.background).toBe('red');
98103
});
99104
});
100-
101-
describe('with children', () => {
102-
it('then it will render children', () => {
103-
const { getByTestId } = render(
105+
describe('when custom html props are given', () => {
106+
it('then it adds them to the returned div', () => {
107+
const { container, getByTestId } = render(
104108
<ParallaxProvider>
105-
<ParallaxBanner layers={[]}>
106-
<div data-testid="child" />
107-
</ParallaxBanner>
109+
<ParallaxBanner
110+
style={{ background: 'red' }}
111+
className="my-class"
112+
id="test-id"
113+
data-testid="data-test-id"
114+
data-foo="bar"
115+
aria-label="Cool"
116+
/>
108117
</ParallaxProvider>
109118
);
110-
expect(getByTestId('child')).toBeInTheDocument();
119+
expect(getByTestId('data-test-id')).toBeInTheDocument();
120+
expect(container.querySelector('.my-class')).toBeInTheDocument();
121+
expect(container.querySelector('#test-id')).toBeInTheDocument();
122+
expect(getByTestId('data-test-id')).toHaveAttribute('aria-label', 'Cool');
123+
expect(getByTestId('data-test-id')).toHaveAttribute('data-foo', 'bar');
124+
expect(getByTestId('data-test-id').style.background).toBe('red');
111125
});
112126
});
113-
114-
describe('with custom props', () => {
115-
it('then it will render children', () => {
127+
describe('with custom props are defined in the layer', () => {
128+
it('then it adds them to the layer div', () => {
116129
const { container, getByTestId } = render(
117130
<ParallaxProvider>
118131
<ParallaxBanner
119132
layers={[
120133
{
121-
speed: 2,
122-
props: {
123-
style: {
124-
backgroundColor: 'red',
125-
},
126-
className: 'my-custom-class',
127-
id: 'my-id',
128-
},
134+
style: { background: 'red' },
135+
className: 'my-class',
136+
id: 'test-id',
137+
// @ts-expect-error
138+
'data-testid': 'data-test-id',
139+
'data-foo': 'bar',
140+
'aria-label': 'Cool',
129141
},
130142
]}
131143
/>
132144
</ParallaxProvider>
133145
);
134-
expect(container.querySelector('.my-custom-class')).toBeInTheDocument();
135-
expect(container.querySelector('#my-id')).toBeInTheDocument();
136-
137-
expect(getByTestId('layer-0').style.background).toBe('red');
146+
expect(getByTestId('data-test-id')).toBeInTheDocument();
147+
expect(container.querySelector('.my-class')).toBeInTheDocument();
148+
expect(container.querySelector('#test-id')).toBeInTheDocument();
149+
expect(getByTestId('data-test-id')).toHaveAttribute('aria-label', 'Cool');
150+
expect(getByTestId('data-test-id')).toHaveAttribute('data-foo', 'bar');
151+
expect(getByTestId('data-test-id').style.background).toBe('red');
138152
});
139153
});
140154
});
Lines changed: 65 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,88 @@
11
import React, { PropsWithChildren } from 'react';
22
import { CSSProperties } from 'react';
3-
import { Parallax } from '../Parallax';
4-
import { ParallaxBannerProps } from './types';
3+
import { useParallax } from '../..';
4+
import { getIsolatedParallaxProps } from '../../helpers/getIsolatedParallaxProps';
5+
import { BannerLayer, ParallaxBannerProps } from './types';
56

67
const containerStyle: CSSProperties = {
78
position: 'relative',
89
overflow: 'hidden',
910
width: '100%',
10-
height: '50vh',
1111
};
1212

1313
const absoluteStyle: CSSProperties = {
1414
position: 'absolute',
1515
top: 0,
16+
left: 0,
1617
right: 0,
1718
bottom: 0,
18-
left: 0,
1919
};
2020

21-
export const ParallaxBanner = ({
22-
children,
23-
className,
24-
layers,
25-
style,
26-
disabled,
27-
}: PropsWithChildren<ParallaxBannerProps>) => {
28-
return (
29-
<div
30-
style={{ ...containerStyle, ...style }}
31-
className={'parallax-banner' + (className ? ` ${className}` : '')}
32-
>
33-
{layers.map(
34-
(
35-
{
36-
speed,
37-
children: layerChildren,
38-
expanded = true,
39-
image,
40-
props = {},
41-
},
42-
i
43-
) => {
44-
// save props to be merged
45-
const layerStyle = props.style || {};
46-
const layerClass = props.className || '';
47-
48-
// remove from pass through props
49-
delete props.style;
50-
delete props.className;
51-
52-
const layerClassMerged = `parallax-banner-layer-${i}${
53-
layerClass ? ` ${layerClass}` : ''
54-
}`;
21+
function getExpandedStyle(expanded: boolean, layer: BannerLayer) {
22+
const speed = layer.speed || 0;
23+
return expanded
24+
? {
25+
top: Math.abs(speed) * 10 * -1 + 'px',
26+
bottom: Math.abs(speed) * 10 * -1 + 'px',
27+
}
28+
: {};
29+
}
5530

56-
// if this is an expanded layer overwrite the top/bottom styles with negative margins
57-
const expandedStyle = expanded
58-
? {
59-
top: Math.abs(speed) * 10 * -1 + 'px',
60-
bottom: Math.abs(speed) * 10 * -1 + 'px',
61-
}
62-
: {};
31+
function getImageStyle(layer: BannerLayer) {
32+
return layer.image
33+
? {
34+
backgroundImage: `url(${layer.image})`,
35+
backgroundPosition: 'center',
36+
backgroundSize: 'cover',
37+
}
38+
: {};
39+
}
6340

64-
// optional image styles
65-
const imageStyle = image
66-
? {
67-
backgroundImage: `url(${image})`,
68-
backgroundPosition: 'center',
69-
backgroundSize: 'cover',
70-
}
71-
: {};
41+
export const ParallaxBanner = (
42+
props: PropsWithChildren<ParallaxBannerProps>
43+
) => {
44+
const {
45+
disabled: disableAllLayers,
46+
style: rootStyle,
47+
layers = [],
48+
...rootRest
49+
} = props;
50+
return (
51+
<div style={{ ...containerStyle, ...rootStyle }} {...rootRest}>
52+
{layers.map((layer, i) => {
53+
const { parallaxProps, rest } = getIsolatedParallaxProps(layer);
54+
const {
55+
children,
56+
disabled,
57+
style,
58+
expanded = true,
59+
image,
60+
...divProps
61+
} = rest;
7262

73-
const key = `layer-${i}`;
63+
const key = `layer-${i}`;
64+
const imageStyle = getImageStyle(layer);
65+
const expandedStyle = getExpandedStyle(expanded, layer);
66+
const parallax = useParallax<HTMLDivElement>(parallaxProps);
7467

75-
return (
76-
<Parallax
77-
key={key}
78-
speed={speed}
79-
style={absoluteStyle}
80-
disabled={disabled}
81-
>
82-
<div
83-
data-testid={key}
84-
className={layerClassMerged}
85-
style={{
86-
...imageStyle,
87-
...absoluteStyle,
88-
...expandedStyle,
89-
...layerStyle,
90-
}}
91-
{...props}
92-
>
93-
{layerChildren}
94-
</div>
95-
</Parallax>
96-
);
97-
}
98-
)}
99-
{children}
68+
return (
69+
<div
70+
data-testid={key}
71+
key={key}
72+
ref={parallax.ref}
73+
style={{
74+
...imageStyle,
75+
...absoluteStyle,
76+
...expandedStyle,
77+
...style,
78+
}}
79+
{...divProps}
80+
>
81+
{rest.children}
82+
</div>
83+
);
84+
})}
85+
{props.children}
10086
</div>
10187
);
10288
};

0 commit comments

Comments
 (0)