Skip to content

Commit 958d978

Browse files
committed
feat: support expanded styles for translateY effects in ParallaxBanner
1 parent 45ea7c1 commit 958d978

File tree

5 files changed

+201
-83
lines changed

5 files changed

+201
-83
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React, { CSSProperties } from 'react';
2+
import { useParallax } from '../../..';
3+
import { getIsolatedParallaxProps } from '../../../helpers/getIsolatedParallaxProps';
4+
import { getExpandedStyle } from '../helpers/getExpandedStyle';
5+
import { getImageStyle } from '../helpers/getImageStyle';
6+
import { BannerLayer } from '../types';
7+
8+
const absoluteStyle: CSSProperties = {
9+
position: 'absolute',
10+
top: 0,
11+
left: 0,
12+
right: 0,
13+
bottom: 0,
14+
};
15+
16+
export const ParallaxBannerLayer = (
17+
props: BannerLayer & { testId: string }
18+
) => {
19+
const { parallaxProps, rest } = getIsolatedParallaxProps(props);
20+
const {
21+
children,
22+
disabled,
23+
style,
24+
expanded = true,
25+
image,
26+
testId,
27+
...divProps
28+
} = rest;
29+
30+
const imageStyle = getImageStyle(props);
31+
const expandedStyle = getExpandedStyle(expanded, props);
32+
const parallax = useParallax<HTMLDivElement>({
33+
targetElement: props.targetElement,
34+
shouldDisableScalingTranslations: true,
35+
...parallaxProps,
36+
});
37+
38+
return (
39+
<div
40+
data-testid={testId}
41+
ref={parallax.ref}
42+
style={{
43+
...imageStyle,
44+
...absoluteStyle,
45+
...expandedStyle,
46+
...style,
47+
}}
48+
{...divProps}
49+
>
50+
{rest.children}
51+
</div>
52+
);
53+
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { parseValueAndUnit } from 'parallax-controller';
2+
import { BannerLayer } from '../types';
3+
4+
export function getExpandedStyle(expanded: boolean, layer: BannerLayer) {
5+
if (!expanded) {
6+
return {};
7+
}
8+
if (Array.isArray(layer.translateY)) {
9+
const translateYStart = parseValueAndUnit(layer.translateY[0]);
10+
const translateYEnd = parseValueAndUnit(layer.translateY[1]);
11+
if (translateYStart.unit === 'px' && translateYEnd.unit === 'px') {
12+
return {
13+
top: `${Math.abs(translateYEnd.value) * -1}px`,
14+
bottom: `${Math.abs(translateYStart.value) * -1}px`,
15+
};
16+
}
17+
}
18+
if (layer.speed) {
19+
const speed = layer.speed || 0;
20+
21+
return {
22+
top: Math.abs(speed) * 10 * -1 + 'px',
23+
bottom: Math.abs(speed) * 10 * -1 + 'px',
24+
};
25+
}
26+
27+
return {};
28+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { BannerLayer } from '../types';
2+
3+
export function getImageStyle(layer: BannerLayer) {
4+
return layer.image
5+
? {
6+
backgroundImage: `url(${layer.image})`,
7+
backgroundPosition: 'center',
8+
backgroundSize: 'cover',
9+
}
10+
: {};
11+
}

src/components/ParallaxBanner/index.test.tsx

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ describe('given a <ParallaxBanner> component', () => {
2626
expect(asFragment()).toMatchSnapshot();
2727
});
2828
});
29+
2930
describe.each(ALL_PARALLAX_PROPS)('when the prop %s is given', (props) => {
3031
it('then it renders without issue and calls create element with props', () => {
3132
const controller = ParallaxController.init({
@@ -48,7 +49,11 @@ describe('given a <ParallaxBanner> component', () => {
4849
expect(asFragment()).toMatchSnapshot();
4950
expect(controller.createElement).toBeCalledWith({
5051
el: expect.any(HTMLElement),
51-
props: { ...props, shouldDisableScalingTranslations: true },
52+
props: {
53+
...props,
54+
shouldDisableScalingTranslations: true,
55+
targetElement: expect.any(HTMLElement),
56+
},
5257
});
5358
});
5459
});
@@ -73,6 +78,7 @@ describe('given a <ParallaxBanner> component', () => {
7378
el: expect.any(HTMLElement),
7479
props: {
7580
shouldDisableScalingTranslations: true,
81+
targetElement: expect.any(HTMLElement),
7682
},
7783
});
7884
});
@@ -121,18 +127,76 @@ describe('given a <ParallaxBanner> component', () => {
121127
});
122128
});
123129

124-
describe('with layer expanded', () => {
125-
it('then it will render with expanded styles based on speed', () => {
126-
const { getByTestId } = render(
127-
<ParallaxProvider>
128-
<ParallaxBanner layers={[{ speed: 2 }]} />
129-
</ParallaxProvider>
130-
);
131-
expect(getByTestId('layer-0').style.top).toBe('-20px');
132-
expect(getByTestId('layer-0').style.right).toBe('0px');
133-
expect(getByTestId('layer-0').style.left).toBe('0px');
134-
expect(getByTestId('layer-0').style.bottom).toBe('-20px');
135-
expect(getByTestId('layer-0').style.position).toBe('absolute');
130+
describe('when the layer is expanded and', () => {
131+
describe('when the speed prop is set to a positive number', () => {
132+
it('then it will render with expanded styles based on speed', () => {
133+
const { getByTestId } = render(
134+
<ParallaxProvider>
135+
<ParallaxBanner layers={[{ speed: 2 }]} />
136+
</ParallaxProvider>
137+
);
138+
expect(getByTestId('layer-0').style.top).toBe('-20px');
139+
expect(getByTestId('layer-0').style.right).toBe('0px');
140+
expect(getByTestId('layer-0').style.left).toBe('0px');
141+
expect(getByTestId('layer-0').style.bottom).toBe('-20px');
142+
expect(getByTestId('layer-0').style.position).toBe('absolute');
143+
});
144+
});
145+
describe('when the speed prop is set to a negative number', () => {
146+
it('then it will render with expanded styles based on speed', () => {
147+
const { getByTestId } = render(
148+
<ParallaxProvider>
149+
<ParallaxBanner layers={[{ speed: -4 }]} />
150+
</ParallaxProvider>
151+
);
152+
expect(getByTestId('layer-0').style.top).toBe('-40px');
153+
expect(getByTestId('layer-0').style.right).toBe('0px');
154+
expect(getByTestId('layer-0').style.left).toBe('0px');
155+
expect(getByTestId('layer-0').style.bottom).toBe('-40px');
156+
expect(getByTestId('layer-0').style.position).toBe('absolute');
157+
});
158+
});
159+
describe('when the translateY prop is set [0px, 10px]', () => {
160+
it('then it will render with expanded styles based on the translate start end values', () => {
161+
const { getByTestId } = render(
162+
<ParallaxProvider>
163+
<ParallaxBanner layers={[{ translateY: ['0px', '10px'] }]} />
164+
</ParallaxProvider>
165+
);
166+
expect(getByTestId('layer-0').style.top).toBe('-10px');
167+
expect(getByTestId('layer-0').style.right).toBe('0px');
168+
expect(getByTestId('layer-0').style.left).toBe('0px');
169+
expect(getByTestId('layer-0').style.bottom).toBe('0px');
170+
expect(getByTestId('layer-0').style.position).toBe('absolute');
171+
});
172+
});
173+
describe('when the translateY prop is set [-40px, 30px]', () => {
174+
it('then it will render with expanded styles based on the translate start end values', () => {
175+
const { getByTestId } = render(
176+
<ParallaxProvider>
177+
<ParallaxBanner layers={[{ translateY: ['-40px', '30px'] }]} />
178+
</ParallaxProvider>
179+
);
180+
expect(getByTestId('layer-0').style.top).toBe('-30px');
181+
expect(getByTestId('layer-0').style.right).toBe('0px');
182+
expect(getByTestId('layer-0').style.left).toBe('0px');
183+
expect(getByTestId('layer-0').style.bottom).toBe('-40px');
184+
expect(getByTestId('layer-0').style.position).toBe('absolute');
185+
});
186+
});
187+
describe('when the translateY prop is set [0px, 100px]', () => {
188+
it('then it will render with expanded styles based on the translate start end values', () => {
189+
const { getByTestId } = render(
190+
<ParallaxProvider>
191+
<ParallaxBanner layers={[{ translateY: ['0px', '100px'] }]} />
192+
</ParallaxProvider>
193+
);
194+
expect(getByTestId('layer-0').style.top).toBe('-100px');
195+
expect(getByTestId('layer-0').style.right).toBe('0px');
196+
expect(getByTestId('layer-0').style.left).toBe('0px');
197+
expect(getByTestId('layer-0').style.bottom).toBe('0px');
198+
expect(getByTestId('layer-0').style.position).toBe('absolute');
199+
});
136200
});
137201
});
138202
describe('with custom props', () => {

src/components/ParallaxBanner/index.tsx

Lines changed: 32 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,52 @@
1-
import React, { PropsWithChildren } from 'react';
2-
import { CSSProperties } from 'react';
3-
import { useParallax } from '../..';
4-
import { getIsolatedParallaxProps } from '../../helpers/getIsolatedParallaxProps';
5-
import { BannerLayer, ParallaxBannerProps } from './types';
1+
import React, {
2+
PropsWithChildren,
3+
CSSProperties,
4+
useEffect,
5+
useRef,
6+
useState,
7+
} from 'react';
8+
import { ParallaxBannerLayer } from './components/ParallaxBannerLayer';
9+
import { ParallaxBannerProps } from './types';
610

711
const containerStyle: CSSProperties = {
812
position: 'relative',
913
overflow: 'hidden',
1014
width: '100%',
1115
};
1216

13-
const absoluteStyle: CSSProperties = {
14-
position: 'absolute',
15-
top: 0,
16-
left: 0,
17-
right: 0,
18-
bottom: 0,
19-
};
20-
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-
}
30-
31-
function getImageStyle(layer: BannerLayer) {
32-
return layer.image
33-
? {
34-
backgroundImage: `url(${layer.image})`,
35-
backgroundPosition: 'center',
36-
backgroundSize: 'cover',
37-
}
38-
: {};
39-
}
40-
4117
export const ParallaxBanner = (
4218
props: PropsWithChildren<ParallaxBannerProps>
4319
) => {
20+
const [targetElement, setTargetElement] = useState<HTMLDivElement | null>(
21+
null
22+
);
23+
const containerRef = useRef<HTMLDivElement>(null);
24+
useEffect(() => {
25+
setTargetElement(containerRef.current);
26+
}, []);
4427
const {
4528
disabled: disableAllLayers,
4629
style: rootStyle,
4730
layers = [],
4831
...rootRest
4932
} = props;
5033
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;
62-
63-
const key = `layer-${i}`;
64-
const imageStyle = getImageStyle(layer);
65-
const expandedStyle = getExpandedStyle(expanded, layer);
66-
const parallax = useParallax<HTMLDivElement>({
67-
shouldDisableScalingTranslations: true,
68-
...parallaxProps,
69-
});
70-
71-
return (
72-
<div
73-
data-testid={key}
74-
key={key}
75-
ref={parallax.ref}
76-
style={{
77-
...imageStyle,
78-
...absoluteStyle,
79-
...expandedStyle,
80-
...style,
81-
}}
82-
{...divProps}
83-
>
84-
{rest.children}
85-
</div>
86-
);
87-
})}
34+
<div
35+
ref={containerRef}
36+
style={{ ...containerStyle, ...rootStyle }}
37+
{...rootRest}
38+
>
39+
{layers.map(
40+
(layer, i) =>
41+
targetElement && (
42+
<ParallaxBannerLayer
43+
{...layer}
44+
targetElement={targetElement}
45+
key={`layer-${i}`}
46+
testId={`layer-${i}`}
47+
/>
48+
)
49+
)}
8850
{props.children}
8951
</div>
9052
);

0 commit comments

Comments
 (0)