Skip to content

Commit a38abfb

Browse files
committed
fix(utils): Positioning logic for inner-left/inner-right and vertical anchors
fix(utils): Vertical positioning behavior
1 parent 04749c6 commit a38abfb

File tree

4 files changed

+199
-48
lines changed

4 files changed

+199
-48
lines changed

packages/transition/src/__tests__/__snapshots__/useFixedPositioning.tsx.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ exports[`useFixedPositioning should allow for configuring the fixed position 1`]
99
</button>
1010
<div
1111
data-testid="element"
12-
style="left: 0px; top: 0px; position: fixed; transform-origin: 0 0;"
12+
style="left: 16px; top: 0px; position: fixed; transform-origin: 0 0;"
1313
>
1414
Some content.
1515
</div>

packages/utils/src/positioning/__tests__/createHorizonalPosition.ts

Lines changed: 176 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import {
55
createAnchoredLeft,
66
createAnchoredRight,
77
createEqualWidth,
8+
createHorizontalPosition,
89
FixConfig,
910
} from "../createHorizontalPosition";
11+
1012
import {
1113
getCenterXCoord,
1214
getInnerLeftCoord,
@@ -137,10 +139,10 @@ describe("createAnchoredLeft", () => {
137139
});
138140

139141
it("should ignore the disableSwapping argument", () => {
140-
const updatedConfig1 = { ...config1, disableSwapping: true };
141-
const updatedConfig2 = { ...config2, disableSwapping: true };
142-
const updatedConfig3 = { ...config3, disableSwapping: true };
143-
const updatedConfig4 = { ...config4, disableSwapping: true };
142+
const updatedConfig1: FixConfig = { ...config1, disableSwapping: true };
143+
const updatedConfig2: FixConfig = { ...config2, disableSwapping: true };
144+
const updatedConfig3: FixConfig = { ...config3, disableSwapping: true };
145+
const updatedConfig4: FixConfig = { ...config4, disableSwapping: true };
144146

145147
expect(createAnchoredLeft(updatedConfig1)).toEqual({
146148
left: getLeftCoord(updatedConfig1),
@@ -161,10 +163,10 @@ describe("createAnchoredLeft", () => {
161163
});
162164

163165
it("should return the vwMargin value as the left value if swapping is disabled", () => {
164-
const updatedConfig5 = { ...config5, disableSwapping: true };
165-
const updatedConfig6 = { ...config6, disableSwapping: true };
166-
const updatedConfig7 = { ...config7, disableSwapping: true };
167-
const updatedConfig8 = { ...config8, disableSwapping: true };
166+
const updatedConfig5: FixConfig = { ...config5, disableSwapping: true };
167+
const updatedConfig6: FixConfig = { ...config6, disableSwapping: true };
168+
const updatedConfig7: FixConfig = { ...config7, disableSwapping: true };
169+
const updatedConfig8: FixConfig = { ...config8, disableSwapping: true };
168170
expect(createAnchoredLeft(updatedConfig5)).toEqual({
169171
left: 0,
170172
actualX: "left",
@@ -184,10 +186,10 @@ describe("createAnchoredLeft", () => {
184186
});
185187

186188
it("should return the vwMargin value as the left value if swapping to the right will force it out of the viewport right bounds", () => {
187-
const updatedConfig5 = { ...config5, screenRight: 200 };
188-
const updatedConfig6 = { ...config6, screenRight: 200 };
189-
const updatedConfig7 = { ...config7, screenRight: 200 };
190-
const updatedConfig8 = { ...config8, screenRight: 200 };
189+
const updatedConfig5: FixConfig = { ...config5, screenRight: 200 };
190+
const updatedConfig6: FixConfig = { ...config6, screenRight: 200 };
191+
const updatedConfig7: FixConfig = { ...config7, screenRight: 200 };
192+
const updatedConfig8: FixConfig = { ...config8, screenRight: 200 };
191193
expect(createAnchoredLeft(updatedConfig5)).toEqual({
192194
left: 0,
193195
actualX: "left",
@@ -246,31 +248,50 @@ describe("createAnchoredInnerLeft", () => {
246248
});
247249
});
248250

249-
it("should return the vwMargin if swapping is disabled", () => {
250-
const updatedConfig1 = { ...rightBoundsConfig1, disableSwapping: true };
251-
const updatedConfig2 = { ...rightBoundsConfig2, disableSwapping: true };
251+
it("should just update the position to be within the viewport if swapping is disabled", () => {
252+
const leftOutOfBounds1: FixConfig = {
253+
...leftBoundsConfig1,
254+
disableSwapping: true,
255+
containerRect: {
256+
...leftBoundsConfig1.containerRect,
257+
left: -20,
258+
},
259+
};
260+
const leftOutOfBounds2: FixConfig = {
261+
...leftOutOfBounds1,
262+
vwMargin: 8,
263+
};
264+
265+
const tooWideConfig1: FixConfig = {
266+
...rightBoundsConfig1,
267+
disableSwapping: true,
268+
};
252269

253-
expect(createAnchoredInnerLeft(updatedConfig1)).toEqual({
270+
expect(createAnchoredInnerLeft(leftOutOfBounds1)).toEqual({
254271
left: 0,
255272
actualX: "inner-left",
256273
});
257-
expect(createAnchoredInnerLeft(updatedConfig2)).toEqual({
274+
expect(createAnchoredInnerLeft(leftOutOfBounds2)).toEqual({
258275
left: 8,
259276
actualX: "inner-left",
260277
});
278+
expect(createAnchoredInnerLeft(tooWideConfig1)).toEqual({
279+
left: 25,
280+
actualX: "inner-left",
281+
});
261282
});
262283

263284
it("should return the vwMargin if the swapped inner right values are less than the vwMargin", () => {
264285
// need to move the container rect a bit more into the page so that the fixed element will
265286
// no longer be within the viewport
266-
const updatedConfig1 = {
287+
const updatedConfig1: FixConfig = {
267288
...leftBoundsConfig1,
268289
containerRect: {
269290
...leftBoundsConfig1.containerRect,
270291
left: 40,
271292
},
272293
};
273-
const updatedConfig2 = {
294+
const updatedConfig2: FixConfig = {
274295
...leftBoundsConfig2,
275296
containerRect: {
276297
...leftBoundsConfig2.containerRect,
@@ -296,6 +317,29 @@ describe("createAnchoredInnerLeft", () => {
296317
left: getInnerRightCoord(rightBoundsConfig2),
297318
actualX: "inner-right",
298319
});
320+
321+
const scrolledOffscreenRight: FixConfig = {
322+
xMargin: 0,
323+
vwMargin: 0,
324+
elWidth: 100,
325+
screenRight: vw,
326+
disableSwapping: false,
327+
containerRect: {
328+
top: 0,
329+
bottom: 0,
330+
left: vw - 80,
331+
right: vw - 40,
332+
height: 40,
333+
width: 120,
334+
x: 0,
335+
y: 0,
336+
toJSON() {},
337+
},
338+
};
339+
expect(createAnchoredInnerLeft(scrolledOffscreenRight)).toEqual({
340+
left: vw - 100,
341+
actualX: "inner-right",
342+
});
299343
});
300344
});
301345

@@ -379,42 +423,48 @@ describe("createAnchoredInnerRight", () => {
379423
});
380424

381425
it("should return the screenRight minus the element's width as the left value if swapping is disabled", () => {
382-
const updatedConfig1 = { ...leftBoundsConfig1, disableSwapping: true };
383-
const updatedConfig2 = { ...leftBoundsConfig2, disableSwapping: true };
426+
const updatedConfig1: FixConfig = {
427+
...leftBoundsConfig1,
428+
disableSwapping: true,
429+
};
430+
const updatedConfig2: FixConfig = {
431+
...leftBoundsConfig2,
432+
disableSwapping: true,
433+
};
384434

385435
expect(createAnchoredInnerRight(updatedConfig1)).toEqual({
386-
left: 25,
436+
left: 0,
387437
actualX: "inner-right",
388438
});
389439
expect(createAnchoredInnerRight(updatedConfig2)).toEqual({
390-
left: 25,
440+
left: 8,
391441
actualX: "inner-right",
392442
});
393443
});
394444

395445
it("should return the screenRight minus the element's width as the left value if the swapped inner left values are less than the vwMargin", () => {
396446
// need to move the container rect a bit more into the page so that the fixed element will
397447
// no longer be within the viewport
398-
const updatedConfig1 = {
448+
const updatedConfig1: FixConfig = {
399449
...leftBoundsConfig1,
400450
containerRect: {
401451
...leftBoundsConfig1.containerRect,
402452
left: 40,
403453
},
404454
};
405-
const updatedConfig2 = {
455+
const updatedConfig2: FixConfig = {
406456
...leftBoundsConfig2,
407457
containerRect: {
408458
...leftBoundsConfig2.containerRect,
409459
left: 40,
410460
},
411461
};
412462
expect(createAnchoredInnerRight(updatedConfig1)).toEqual({
413-
left: 25,
463+
left: 0,
414464
actualX: "inner-right",
415465
});
416466
expect(createAnchoredInnerRight(updatedConfig2)).toEqual({
417-
left: 25,
467+
left: 8,
418468
actualX: "inner-right",
419469
});
420470
});
@@ -425,7 +475,7 @@ describe("createAnchoredInnerRight", () => {
425475
actualX: "inner-left",
426476
});
427477
expect(createAnchoredInnerRight(leftBoundsConfig2)).toEqual({
428-
left: getInnerLeftCoord(leftBoundsConfig2),
478+
left: 8,
429479
actualX: "inner-left",
430480
});
431481
});
@@ -452,10 +502,10 @@ describe("createAnchoredRight", () => {
452502
});
453503

454504
it("should ignore the disableSwapping argument", () => {
455-
const updatedConfig1 = { ...config1, disableSwapping: true };
456-
const updatedConfig2 = { ...config2, disableSwapping: true };
457-
const updatedConfig3 = { ...config3, disableSwapping: true };
458-
const updatedConfig4 = { ...config4, disableSwapping: true };
505+
const updatedConfig1: FixConfig = { ...config1, disableSwapping: true };
506+
const updatedConfig2: FixConfig = { ...config2, disableSwapping: true };
507+
const updatedConfig3: FixConfig = { ...config3, disableSwapping: true };
508+
const updatedConfig4: FixConfig = { ...config4, disableSwapping: true };
459509

460510
expect(createAnchoredRight(updatedConfig1)).toEqual({
461511
left: getRightCoord(updatedConfig1),
@@ -624,4 +674,98 @@ describe("createEqualWidth", () => {
624674
actualX: "center",
625675
});
626676
});
677+
678+
it("should use the initialX value instead of the containerRect.left and xMargin if it was provided", () => {
679+
expect(createEqualWidth({ ...options1, initialX: 150 })).toEqual({
680+
left: 150,
681+
width: 400,
682+
actualX: "center",
683+
});
684+
});
685+
});
686+
687+
describe("createHorizontalPosition", () => {
688+
it("should return the correct positions...", () => {
689+
const equalWidthOptions = {
690+
x: "inner-left",
691+
vw: 500,
692+
vwMargin: 0,
693+
xMargin: 0,
694+
width: "equal",
695+
elWidth: 120,
696+
initialX: undefined,
697+
containerRect: containerRect1,
698+
disableSwapping: false,
699+
} as const;
700+
701+
expect(createHorizontalPosition(equalWidthOptions)).toEqual({
702+
left: containerRect1.left,
703+
width: containerRect1.width,
704+
actualX: "inner-left",
705+
});
706+
707+
const minWidthOptions = {
708+
...equalWidthOptions,
709+
width: "min",
710+
} as const;
711+
expect(createHorizontalPosition(minWidthOptions)).toEqual({
712+
left: containerRect1.left,
713+
minWidth: containerRect1.width,
714+
actualX: "inner-left",
715+
});
716+
717+
const tooLargeOptions = {
718+
...equalWidthOptions,
719+
elWidth: 560,
720+
width: "auto",
721+
} as const;
722+
expect(createHorizontalPosition(tooLargeOptions)).toEqual({
723+
left: 0,
724+
right: 0,
725+
actualX: "inner-left",
726+
});
727+
728+
const simpleLeftOptions = {
729+
...equalWidthOptions,
730+
x: "left",
731+
width: "auto",
732+
} as const;
733+
const simpleInnerLeftOptions = {
734+
...simpleLeftOptions,
735+
x: "inner-left",
736+
} as const;
737+
const simpleCenterOptions = {
738+
...simpleInnerLeftOptions,
739+
x: "center",
740+
} as const;
741+
const simpleInnerRightOptions = {
742+
...simpleCenterOptions,
743+
x: "inner-right",
744+
} as const;
745+
const simpleRightOptions = {
746+
...simpleInnerRightOptions,
747+
x: "right",
748+
} as const;
749+
750+
expect(createHorizontalPosition(simpleLeftOptions)).toEqual({
751+
left: 200,
752+
actualX: "right",
753+
});
754+
expect(createHorizontalPosition(simpleInnerLeftOptions)).toEqual({
755+
left: 100,
756+
actualX: "inner-left",
757+
});
758+
expect(createHorizontalPosition(simpleCenterOptions)).toEqual({
759+
left: 90,
760+
actualX: "center",
761+
});
762+
expect(createHorizontalPosition(simpleInnerRightOptions)).toEqual({
763+
left: 80,
764+
actualX: "inner-right",
765+
});
766+
expect(createHorizontalPosition(simpleRightOptions)).toEqual({
767+
left: 200,
768+
actualX: "right",
769+
});
770+
});
627771
});

packages/utils/src/positioning/createHorizontalPosition.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,26 @@ export function createAnchoredInnerLeft(config: FixConfig): XPosition {
9090

9191
let left = getInnerLeftCoord(config);
9292
let actualX: HorizontalPosition = "inner-left";
93-
if (left + elWidth <= screenRight) {
93+
if (left + elWidth <= screenRight && left >= vwMargin) {
94+
return { actualX, left };
95+
}
96+
97+
if (disableSwapping) {
98+
if (left + elWidth > screenRight) {
99+
left = screenRight - elWidth;
100+
} else {
101+
left = vwMargin;
102+
}
103+
94104
return { actualX, left };
95105
}
96106

97107
const swappedLeft = getInnerRightCoord(config);
98-
if (disableSwapping || swappedLeft < vwMargin) {
108+
if (swappedLeft < vwMargin) {
99109
left = vwMargin;
110+
} else if (swappedLeft + elWidth > screenRight) {
111+
left = screenRight - elWidth;
112+
actualX = "inner-right";
100113
} else {
101114
left = swappedLeft;
102115
actualX = "inner-right";
@@ -141,17 +154,15 @@ export function createAnchoredInnerRight(config: FixConfig): XPosition {
141154

142155
let left = getInnerRightCoord(config);
143156
let actualX: HorizontalPosition = "inner-right";
144-
145157
if (left >= vwMargin) {
146-
return { actualX, left };
158+
return { actualX, left: Math.min(left, screenRight - elWidth) };
147159
}
148160

149161
const swappedLeft = getInnerLeftCoord(config);
150-
151162
if (disableSwapping || swappedLeft + elWidth > screenRight) {
152-
left = screenRight - elWidth;
163+
left = vwMargin;
153164
} else {
154-
left = swappedLeft;
165+
left = Math.max(swappedLeft, vwMargin);
155166
actualX = "inner-left";
156167
}
157168

@@ -300,7 +311,5 @@ export function createHorizontalPosition({
300311
return createAnchoredInnerRight(config);
301312
case "right":
302313
return createAnchoredRight(config);
303-
default:
304-
throw new Error("This should never happen");
305314
}
306315
}

0 commit comments

Comments
 (0)