Skip to content

Commit a8192da

Browse files
ScrollView: prevent unnecessary scrollbar at non-100% browser zoom (T1310288) (#33291)
1 parent 7041e92 commit a8192da

3 files changed

Lines changed: 63 additions & 5 deletions

File tree

packages/devextreme/js/__internal/ui/scroll_view/scrollable.simulated.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import Scrollbar from '@ts/ui/scroll_view/scrollbar';
3838
import type {
3939
AllowedDirections, DxMouseEvent, DxMouseWheelEvent, ScrollEventArgs, ScrollOffset,
4040
} from '@ts/ui/scroll_view/types';
41+
import { getAdjustedBaseContainerSize } from '@ts/ui/scroll_view/utils/get_adjusted_base_container_size';
4142

4243
interface ScrollVelocity {
4344
x: number;
@@ -562,16 +563,22 @@ export class Scroller {
562563

563564
_updateScrollbar(): void {
564565
deferUpdater(() => {
565-
const containerSize = this._containerSize();
566+
const dimension = this._dimension;
567+
const rawContainerSize = getBoundingRect(this._$container[0])[dimension];
568+
const containerSize = Math.round(rawContainerSize);
566569
const contentSize = this._contentSize();
567570

568571
// NOTE: Real container and content sizes can be a fractional number when scaling.
569572
// Let's save sizes when scale = 100% to decide whether it is necessary to show
570573
// the scrollbar based on by more precise numbers. We can do it because the container
571574
// size to content size ratio should remain approximately the same at any zoom.
572-
const dimension = this._dimension;
573-
const baseContainerSize = this._getBaseDimension(this._$container[0], dimension);
575+
const rawBaseContainerSize = this._getBaseDimension(this._$container[0], dimension);
574576
const baseContentSize = this._getBaseDimension(this._$content[0], dimension);
577+
const baseContainerSize = getAdjustedBaseContainerSize(
578+
rawContainerSize,
579+
rawBaseContainerSize,
580+
baseContentSize,
581+
);
575582

576583
// eslint-disable-next-line @typescript-eslint/no-floating-promises
577584
deferRender(() => {
@@ -690,10 +697,8 @@ export class SimulatedStrategy<
690697

691698
_$content!: dxElementWrapper;
692699

693-
// eslint-disable-next-line no-restricted-globals
694700
_updateHandlerTimeout?: ReturnType<typeof setTimeout>;
695701

696-
// eslint-disable-next-line no-restricted-globals
697702
_validateWheelTimer?: ReturnType<typeof setTimeout>;
698703

699704
_eventForUserAction?: unknown;
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { describe, expect, it } from '@jest/globals';
2+
3+
import { getAdjustedBaseContainerSize } from './get_adjusted_base_container_size';
4+
5+
describe('getAdjustedBaseContainerSize', () => {
6+
it('should not adjust when zoom is 100% even if sizes differ by 1px', () => {
7+
const result = getAdjustedBaseContainerSize(100, 100, 101);
8+
9+
expect(result).toBe(100);
10+
});
11+
12+
it('should adjust when zoomed and sizes differ by 1px', () => {
13+
const result = getAdjustedBaseContainerSize(99.82, 100, 101);
14+
15+
expect(result).toBe(101);
16+
});
17+
18+
it('should not adjust when zoomed but sizes differ by more than 1px', () => {
19+
const result = getAdjustedBaseContainerSize(99.82, 100, 102);
20+
21+
expect(result).toBe(100);
22+
});
23+
24+
it('should not adjust when zoomed and container is larger than content', () => {
25+
const result = getAdjustedBaseContainerSize(99.82, 101, 100);
26+
27+
expect(result).toBe(101);
28+
});
29+
30+
it('should not adjust when zoomed and sizes are equal', () => {
31+
const result = getAdjustedBaseContainerSize(99.82, 100, 100);
32+
33+
expect(result).toBe(100);
34+
});
35+
});
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const ZOOM_DETECTION_THRESHOLD = 0.01;
2+
const MAX_ROUNDING_ARTIFACT_SIZE = 1;
3+
4+
export function getAdjustedBaseContainerSize(
5+
rawContainerSize: number,
6+
baseContainerSize: number,
7+
baseContentSize: number,
8+
): number {
9+
const isZoomed = Math.abs(rawContainerSize - baseContainerSize) > ZOOM_DETECTION_THRESHOLD;
10+
11+
if (isZoomed
12+
&& baseContentSize > baseContainerSize
13+
&& baseContentSize - baseContainerSize <= MAX_ROUNDING_ARTIFACT_SIZE) {
14+
return baseContentSize;
15+
}
16+
17+
return baseContainerSize;
18+
}

0 commit comments

Comments
 (0)