• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JedWatson / react-select / 9de42ca4-8c55-4031-8310-e7e0a2b0128b

26 Oct 2024 11:27AM CUT coverage: 75.844%. Remained the same
9de42ca4-8c55-4031-8310-e7e0a2b0128b

Pull #5880

circleci

lukebennett88
add box-sizing to border-box for RequiredInput

adding `required` would otherwise cause an extra (unstylable) component to be added which has some implicit padding from the user agent style sheet (inputs have padding) which could cause horizontal scrolling when the whole scroll field is 100% wide.
Pull Request #5880: add box-sizing to border-box for RequiredInput

658 of 1052 branches covered (62.55%)

1033 of 1362 relevant lines covered (75.84%)

1934.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

20.31
/packages/react-select/src/internal/useScrollLock.ts
1
import { useCallback, useEffect, useRef } from 'react';
2

3
const STYLE_KEYS = [
5✔
4
  'boxSizing',
5
  'height',
6
  'overflow',
7
  'paddingRight',
8
  'position',
9
] as const;
10

11
const LOCK_STYLES = {
5✔
12
  boxSizing: 'border-box', // account for possible declaration `width: 100%;` on body
13
  overflow: 'hidden',
14
  position: 'relative',
15
  height: '100%',
16
};
17

18
function preventTouchMove(e: TouchEvent) {
19
  if (e.cancelable) e.preventDefault();
×
20
}
21

22
function allowTouchMove(e: TouchEvent) {
23
  e.stopPropagation();
×
24
}
25

26
function preventInertiaScroll(this: HTMLElement) {
27
  const top = this.scrollTop;
×
28
  const totalScroll = this.scrollHeight;
×
29
  const currentScroll = top + this.offsetHeight;
×
30

31
  if (top === 0) {
×
32
    this.scrollTop = 1;
×
33
  } else if (currentScroll === totalScroll) {
×
34
    this.scrollTop = top - 1;
×
35
  }
36
}
37

38
// `ontouchstart` check works on most browsers
39
// `maxTouchPoints` works on IE10/11 and Surface
40
function isTouchDevice() {
41
  return 'ontouchstart' in window || navigator.maxTouchPoints;
×
42
}
43

44
const canUseDOM = !!(
5✔
45
  typeof window !== 'undefined' &&
15✔
46
  window.document &&
47
  window.document.createElement
48
);
49

50
let activeScrollLocks = 0;
5✔
51

52
interface Options {
53
  readonly isEnabled: boolean;
54
  readonly accountForScrollbars?: boolean;
55
}
56

57
const listenerOptions = {
5✔
58
  capture: false,
59
  passive: false,
60
};
61

62
export default function useScrollLock({
63
  isEnabled,
64
  accountForScrollbars = true,
768✔
65
}: Options) {
66
  const originalStyles = useRef<{ [key: string]: string }>({});
768✔
67
  const scrollTarget = useRef<HTMLElement | null>(null);
768✔
68

69
  const addScrollLock = useCallback(
768✔
70
    (touchScrollTarget: HTMLElement | null) => {
71
      if (!canUseDOM) return;
×
72

73
      const target = document.body;
×
74
      const targetStyle = target && target.style;
×
75

76
      if (accountForScrollbars) {
×
77
        // store any styles already applied to the body
78
        STYLE_KEYS.forEach((key) => {
×
79
          const val = targetStyle && targetStyle[key];
×
80
          originalStyles.current[key] = val;
×
81
        });
82
      }
83

84
      // apply the lock styles and padding if this is the first scroll lock
85
      if (accountForScrollbars && activeScrollLocks < 1) {
×
86
        const currentPadding =
87
          parseInt(originalStyles.current.paddingRight, 10) || 0;
×
88
        const clientWidth = document.body ? document.body.clientWidth : 0;
×
89
        const adjustedPadding =
90
          window.innerWidth - clientWidth + currentPadding || 0;
×
91

92
        Object.keys(LOCK_STYLES).forEach((key) => {
×
93
          const val = LOCK_STYLES[key as keyof typeof LOCK_STYLES];
×
94
          if (targetStyle) {
×
95
            targetStyle[key as keyof typeof LOCK_STYLES] = val;
×
96
          }
97
        });
98

99
        if (targetStyle) {
×
100
          targetStyle.paddingRight = `${adjustedPadding}px`;
×
101
        }
102
      }
103

104
      // account for touch devices
105
      if (target && isTouchDevice()) {
×
106
        // Mobile Safari ignores { overflow: hidden } declaration on the body.
107
        target.addEventListener('touchmove', preventTouchMove, listenerOptions);
×
108

109
        // Allow scroll on provided target
110
        if (touchScrollTarget) {
×
111
          touchScrollTarget.addEventListener(
×
112
            'touchstart',
113
            preventInertiaScroll,
114
            listenerOptions
115
          );
116
          touchScrollTarget.addEventListener(
×
117
            'touchmove',
118
            allowTouchMove,
119
            listenerOptions
120
          );
121
        }
122
      }
123

124
      // increment active scroll locks
125
      activeScrollLocks += 1;
×
126
    },
127
    [accountForScrollbars]
128
  );
129

130
  const removeScrollLock = useCallback(
768✔
131
    (touchScrollTarget: HTMLElement | null) => {
132
      if (!canUseDOM) return;
×
133

134
      const target = document.body;
×
135
      const targetStyle = target && target.style;
×
136

137
      // safely decrement active scroll locks
138
      activeScrollLocks = Math.max(activeScrollLocks - 1, 0);
×
139

140
      // reapply original body styles, if any
141
      if (accountForScrollbars && activeScrollLocks < 1) {
×
142
        STYLE_KEYS.forEach((key) => {
×
143
          const val = originalStyles.current[key];
×
144
          if (targetStyle) {
×
145
            targetStyle[key] = val;
×
146
          }
147
        });
148
      }
149

150
      // remove touch listeners
151
      if (target && isTouchDevice()) {
×
152
        target.removeEventListener(
×
153
          'touchmove',
154
          preventTouchMove,
155
          listenerOptions
156
        );
157

158
        if (touchScrollTarget) {
×
159
          touchScrollTarget.removeEventListener(
×
160
            'touchstart',
161
            preventInertiaScroll,
162
            listenerOptions
163
          );
164
          touchScrollTarget.removeEventListener(
×
165
            'touchmove',
166
            allowTouchMove,
167
            listenerOptions
168
          );
169
        }
170
      }
171
    },
172
    [accountForScrollbars]
173
  );
174

175
  useEffect(() => {
768✔
176
    if (!isEnabled) return;
169!
177

178
    const element = scrollTarget.current;
×
179
    addScrollLock(element);
×
180

181
    return () => {
×
182
      removeScrollLock(element);
×
183
    };
184
  }, [isEnabled, addScrollLock, removeScrollLock]);
185

186
  return (element: HTMLElement | null) => {
768✔
187
    scrollTarget.current = element;
1,536✔
188
  };
189
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc