Skip to content

Commit e4685a8

Browse files
committed
BC: Enable ScrollView arrows to overflow the ScrollView area (#128)
1 parent 5c189fe commit e4685a8

File tree

4 files changed

+91
-25
lines changed

4 files changed

+91
-25
lines changed

src/lib/components/ui/Modal/__tests__/__snapshots__/Modal.test.jsx.snap

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ exports[`rendering renders correctly with all props except translations 1`] = `
181181
</div>
182182
</div>
183183
</div>
184+
<div
185+
aria-hidden={true}
186+
className="scrollingShadows"
187+
/>
184188
</div>
185189
</ScrollView>
186190
</WithTranslationContextComponent>
@@ -510,6 +514,10 @@ exports[`rendering renders correctly with all props except translations and with
510514
</div>
511515
</div>
512516
</div>
517+
<div
518+
aria-hidden={true}
519+
className="scrollingShadows"
520+
/>
513521
</div>
514522
</ScrollView>
515523
</WithTranslationContextComponent>
@@ -803,6 +811,10 @@ exports[`rendering renders correctly with mandatory props only 1`] = `
803811
</div>
804812
</div>
805813
</div>
814+
<div
815+
aria-hidden={true}
816+
className="scrollingShadows"
817+
/>
806818
</div>
807819
</ScrollView>
808820
</WithTranslationContextComponent>
@@ -901,6 +913,10 @@ exports[`rendering renders correctly with portal id 1`] = `
901913
</div>
902914
</div>
903915
</div>
916+
<div
917+
aria-hidden="true"
918+
class="scrollingShadows"
919+
/>
904920
</div>
905921
</div>
906922
<div
@@ -1013,6 +1029,10 @@ exports[`rendering renders correctly with portal id 1`] = `
10131029
</div>
10141030
</div>
10151031
</div>
1032+
<div
1033+
aria-hidden={true}
1034+
className="scrollingShadows"
1035+
/>
10161036
</div>
10171037
</ScrollView>
10181038
</WithTranslationContextComponent>
@@ -1179,6 +1199,10 @@ exports[`rendering renders correctly with translations 1`] = `
11791199
</div>
11801200
</div>
11811201
</div>
1202+
<div
1203+
aria-hidden={true}
1204+
className="scrollingShadows"
1205+
/>
11821206
</div>
11831207
</ScrollView>
11841208
</WithTranslationContextComponent>

src/lib/components/ui/ScrollView/ScrollView.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export const ScrollView = (props) => {
148148
{children}
149149
</div>
150150
</div>
151+
<div className={styles.scrollingShadows} aria-hidden />
151152
{arrows && (
152153
<>
153154
<button

src/lib/components/ui/ScrollView/ScrollView.scss

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1+
// 1. Scrolling shadows are implemented as pseudo elements. This way we can customise them only
2+
// with custom properties.
3+
// 2. Stack scrolling shadows over viewport content while keeping the content interactive.
4+
//
5+
// - `.scrollingShadows` is positioned absolutely over the `.root`, with auto `z-index` (this is
6+
// important!), and with `overflow: hidden` to clip the shadows (ie. its pseudo elements).
7+
// - The `.viewport` is in `.root`'s stacking context and remains interactive because its
8+
// `z-index` is higher than the auto `z-index` of `.scrollingShadows`.
9+
//
10+
// 3. Optional arrows are positioned relative to the `.root` and stacked on top of scrolling
11+
// shadows. They can be shifted outside the `ScrollView` area only because `overflow: hidden` is
12+
// **not** present at `.root`.
13+
//
14+
// 4. Make the `.content`'s bounding rectangle spread beyond the part visible through `.viewport`.
15+
// 5. Prevent undesired vertical scrolling that may occur with tables inside.
16+
// 6. Make `ScrollView` adjust to flexible layouts.
17+
118
@import '../../../styles/tools/caret';
219
@import '../../../styles/tools/hide-scrollbar';
320
@import '../../../styles/tools/reset';
421
@import '../../../styles/tools/transitions';
522
@import 'theme';
623

7-
// 1. Scrolling shadows are implemented as pseudo elements. This way we can customise them only
8-
// thanks to custom properties.
9-
// 2. Clip scrolling shadows.
10-
// 3. Create stacking context just to avoid scrolling shadows being covered by content.
11-
// 4. Make the content bounding rectangle spread beyond the visible part.
12-
// 5. Prevent undesired vertical scrolling that may occur with tables inside.
13-
1424
$_arrow-inner-offset: 0.5rem;
1525
$_arrow-outer-offset: 1rem;
1626

1727
.root {
18-
position: relative;
28+
position: relative; // 2.
1929
display: flex;
2030
flex-direction: column;
2131
width: 100%;
32+
}
33+
34+
// 1.
35+
.scrollingShadows {
36+
position: absolute; // 2.
37+
width: 100%; // 2.
38+
height: 100%; // 2.
2239
overflow: hidden; // 2.
2340

24-
// 1.
2541
&::before,
2642
&::after {
2743
@include transition((visibility, opacity, transform));
2844

2945
content: '';
3046
position: absolute;
31-
z-index: 1;
47+
z-index: 2; // 2.
3248
display: block;
3349
visibility: hidden;
3450
opacity: 0;
@@ -48,7 +64,7 @@ $_arrow-outer-offset: 1rem;
4864
}
4965

5066
.viewport {
51-
position: relative; // 3.
67+
z-index: 1; // 2.
5268
width: 100%;
5369
scroll-behavior: smooth;
5470
-webkit-overflow-scrolling: touch;
@@ -59,8 +75,8 @@ $_arrow-outer-offset: 1rem;
5975
@include reset-button();
6076
@include transition((visibility, opacity, transform));
6177

62-
position: absolute;
63-
z-index: 2;
78+
position: absolute; // 3.
79+
z-index: 3; // 3.
6480
display: flex;
6581
visibility: hidden;
6682
opacity: 0;
@@ -75,11 +91,12 @@ $_arrow-outer-offset: 1rem;
7591

7692
.isRootVertical {
7793
height: 100%;
94+
min-height: 0; // 6.
7895
}
7996

8097
.isRootVertical .viewport {
8198
height: 100%;
82-
overflow-y: auto;
99+
overflow-y: auto; // 2.
83100
}
84101

85102
.isRootVertical .arrowPrev {
@@ -106,6 +123,10 @@ $_arrow-outer-offset: 1rem;
106123
transform: translateY(calc(-1 * #{$scrollview-arrow-initial-offset}));
107124
}
108125

126+
.isRootHorizontal {
127+
min-width: 0; // 6.
128+
}
129+
109130
.isRootHorizontal .arrowPrev {
110131
top: 0;
111132
bottom: 0;
@@ -132,24 +153,24 @@ $_arrow-outer-offset: 1rem;
132153
@include caret-rotate(270);
133154
}
134155

135-
.isRootVertical::before,
136-
.isRootVertical::after {
156+
.isRootVertical .scrollingShadows::before,
157+
.isRootVertical .scrollingShadows::after {
137158
right: 0;
138159
left: 0;
139160
}
140161

141-
.isRootVertical::before {
162+
.isRootVertical .scrollingShadows::before {
142163
top: 0;
143164
transform: translateY($scrollview-shadow-initial-offset);
144165
}
145166

146-
.isRootVertical::after {
167+
.isRootVertical .scrollingShadows::after {
147168
bottom: 0;
148169
transform: translateY(calc(-1 * #{$scrollview-shadow-initial-offset}));
149170
}
150171

151172
.isRootHorizontal .viewport {
152-
overflow-x: auto;
173+
overflow-x: auto; // 2.
153174
overflow-y: hidden; // 5.
154175
}
155176

@@ -158,30 +179,30 @@ $_arrow-outer-offset: 1rem;
158179
min-width: 100%;
159180
}
160181

161-
.isRootHorizontal::before,
162-
.isRootHorizontal::after {
182+
.isRootHorizontal .scrollingShadows::before,
183+
.isRootHorizontal .scrollingShadows::after {
163184
top: 0;
164185
bottom: 0;
165186
}
166187

167-
.isRootHorizontal::before {
188+
.isRootHorizontal .scrollingShadows::before {
168189
left: 0;
169190
transform: translateX($scrollview-shadow-initial-offset);
170191
}
171192

172-
.isRootHorizontal::after {
193+
.isRootHorizontal .scrollingShadows::after {
173194
right: 0;
174195
transform: translateX(calc(-1 * #{$scrollview-shadow-initial-offset}));
175196
}
176197

177-
.isRootScrolledAtStart::before,
198+
.isRootScrolledAtStart .scrollingShadows::before,
178199
.isRootScrolledAtStart .arrowPrev {
179200
visibility: visible;
180201
opacity: 1;
181202
transform: translate(0, 0);
182203
}
183204

184-
.isRootScrolledAtEnd::after,
205+
.isRootScrolledAtEnd .scrollingShadows::after,
185206
.isRootScrolledAtEnd .arrowNext {
186207
visibility: visible;
187208
opacity: 1;

src/lib/components/ui/ScrollView/__tests__/__snapshots__/ScrollView.test.jsx.snap

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ exports[`rendering renders correctly with a single child 1`] = `
5555
</span>
5656
</div>
5757
</div>
58+
<div
59+
aria-hidden={true}
60+
className="scrollingShadows"
61+
/>
5862
</div>
5963
</ScrollView>
6064
</WithTranslationContextComponent>
@@ -179,6 +183,10 @@ exports[`rendering renders correctly with all props 1`] = `
179183
</span>
180184
</div>
181185
</div>
186+
<div
187+
aria-hidden={true}
188+
className="scrollingShadows"
189+
/>
182190
<button
183191
className="arrowPrev"
184192
id="my-scrollview__arrowPrevButton"
@@ -263,6 +271,10 @@ exports[`rendering renders correctly with arrows 1`] = `
263271
</span>
264272
</div>
265273
</div>
274+
<div
275+
aria-hidden={true}
276+
className="scrollingShadows"
277+
/>
266278
<button
267279
className="arrowPrev"
268280
onClick={[Function]}
@@ -349,6 +361,10 @@ exports[`rendering renders correctly with multiple children 1`] = `
349361
</span>
350362
</div>
351363
</div>
364+
<div
365+
aria-hidden={true}
366+
className="scrollingShadows"
367+
/>
352368
</div>
353369
</ScrollView>
354370
</WithTranslationContextComponent>
@@ -410,6 +426,10 @@ exports[`rendering renders correctly with scrollbar disabled 1`] = `
410426
</span>
411427
</div>
412428
</div>
429+
<div
430+
aria-hidden={true}
431+
className="scrollingShadows"
432+
/>
413433
</div>
414434
</ScrollView>
415435
</WithTranslationContextComponent>

0 commit comments

Comments
 (0)