@asd20/ui-next 2.21.6 → 2.21.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +14 -0
- package/package.json +1 -1
- package/src/components/molecules/Asd20SearchField/index.vue +2 -2
- package/src/components/organisms/Asd20SiteMenu/index.vue +43 -1
- package/src/components/organisms/Asd20SiteNavigation/index.vue +1 -7
- package/src/components/organisms/Asd20SiteSearch/index.vue +9 -12
- package/src/components/organisms/Asd20TabBar/index.vue +1 -0
- package/src/helpers/documentScrollLock.js +60 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.21.7](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.6...ui-next-v2.21.7) (2026-08-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* mobile scroll fixes; ios input zoom issue ([56ba4a7](https://github.com/academydistrict20/asd20-ui-next/commit/56ba4a75391744db8a4f43ff74e30b1fa0bd5f75))
|
|
9
|
+
|
|
3
10
|
## [2.21.6](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.5...ui-next-v2.21.6) (2026-08-25)
|
|
4
11
|
|
|
5
12
|
|
package/README.md
CHANGED
|
@@ -87,6 +87,20 @@ npm test
|
|
|
87
87
|
npm run build
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
Optional harnesses are available to facilitate repeatable physical-device
|
|
91
|
+
checks for scrolling, touch, focus, keyboard, and viewport changes:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
npm run test:mobile:storybook
|
|
95
|
+
npm run test:mobile:package
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
These commands are manual testing aids. They are not testing, CI, release, or
|
|
99
|
+
package-publishing requirements.
|
|
100
|
+
|
|
101
|
+
See [Physical Mobile Device Testing](docs/mobile-device-testing.md) for the
|
|
102
|
+
device checklist, packed-consumer workflow, options, and troubleshooting.
|
|
103
|
+
|
|
90
104
|
Regenerate icon metadata after adding, removing, or renaming SVG icon assets:
|
|
91
105
|
|
|
92
106
|
```sh
|
package/package.json
CHANGED
|
@@ -108,8 +108,8 @@ export default {
|
|
|
108
108
|
border: none;
|
|
109
109
|
border-radius: var(--website-shape__radius-s);
|
|
110
110
|
// outline: 1px solid red;
|
|
111
|
-
//
|
|
112
|
-
font-size:
|
|
111
|
+
// Keep form text at least 16px so iOS Safari does not zoom the page on focus.
|
|
112
|
+
font-size: $base-font-size;
|
|
113
113
|
line-height: 1;
|
|
114
114
|
height: space(1.5);
|
|
115
115
|
background: white;
|
|
@@ -153,6 +153,10 @@ import {
|
|
|
153
153
|
shouldOpenInNewWindow,
|
|
154
154
|
shouldShowExternalIcon,
|
|
155
155
|
} from '../../../helpers/linkPolicy'
|
|
156
|
+
import {
|
|
157
|
+
acquireDocumentScrollLock,
|
|
158
|
+
releaseDocumentScrollLock,
|
|
159
|
+
} from '../../../helpers/documentScrollLock'
|
|
156
160
|
|
|
157
161
|
export default {
|
|
158
162
|
name: 'Asd20SiteMenu',
|
|
@@ -168,6 +172,7 @@ export default {
|
|
|
168
172
|
focusedItemIndex: -1,
|
|
169
173
|
hasFocus: false,
|
|
170
174
|
keyboardTriggeredLastAction: false,
|
|
175
|
+
bodyScrollLock: null,
|
|
171
176
|
}),
|
|
172
177
|
computed: {
|
|
173
178
|
activeSection() {
|
|
@@ -179,12 +184,43 @@ export default {
|
|
|
179
184
|
return resolveCurrentHostname(this)
|
|
180
185
|
},
|
|
181
186
|
},
|
|
187
|
+
watch: {
|
|
188
|
+
active(newVal) {
|
|
189
|
+
if (newVal && !this.emptyNav) {
|
|
190
|
+
this.lockBodyScroll()
|
|
191
|
+
} else {
|
|
192
|
+
this.unlockBodyScroll()
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
emptyNav(newVal) {
|
|
196
|
+
if (this.active && !newVal) {
|
|
197
|
+
this.lockBodyScroll()
|
|
198
|
+
} else {
|
|
199
|
+
this.unlockBodyScroll()
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
},
|
|
182
203
|
mounted() {
|
|
204
|
+
if (this.active && !this.emptyNav) {
|
|
205
|
+
this.lockBodyScroll()
|
|
206
|
+
}
|
|
183
207
|
if (!this.emptyNav) {
|
|
184
208
|
this.setFocus()
|
|
185
209
|
}
|
|
186
210
|
},
|
|
211
|
+
beforeUnmount() {
|
|
212
|
+
this.unlockBodyScroll()
|
|
213
|
+
},
|
|
187
214
|
methods: {
|
|
215
|
+
lockBodyScroll() {
|
|
216
|
+
if (this.bodyScrollLock) return
|
|
217
|
+
this.bodyScrollLock = acquireDocumentScrollLock()
|
|
218
|
+
},
|
|
219
|
+
unlockBodyScroll() {
|
|
220
|
+
if (!this.bodyScrollLock) return
|
|
221
|
+
releaseDocumentScrollLock(this.bodyScrollLock)
|
|
222
|
+
this.bodyScrollLock = null
|
|
223
|
+
},
|
|
188
224
|
onFocus(event) {
|
|
189
225
|
this.hasFocus = true
|
|
190
226
|
this.focusedSectionIndex = 0
|
|
@@ -380,6 +416,7 @@ export default {
|
|
|
380
416
|
display: flex;
|
|
381
417
|
flex-direction: column;
|
|
382
418
|
overflow: hidden;
|
|
419
|
+
overscroll-behavior: none;
|
|
383
420
|
&:focus {
|
|
384
421
|
// outline: none;
|
|
385
422
|
}
|
|
@@ -393,6 +430,8 @@ export default {
|
|
|
393
430
|
bottom: space(7);
|
|
394
431
|
background: var(--website-menu__open-background-color, white);
|
|
395
432
|
overflow: auto;
|
|
433
|
+
overscroll-behavior: contain;
|
|
434
|
+
-webkit-overflow-scrolling: touch;
|
|
396
435
|
// overflow-y: scroll !important;
|
|
397
436
|
}
|
|
398
437
|
.asd20-table-of-contents {
|
|
@@ -493,8 +532,11 @@ export default {
|
|
|
493
532
|
}
|
|
494
533
|
}
|
|
495
534
|
.asd20-site-menu__items {
|
|
535
|
+
flex: 1 1 auto;
|
|
536
|
+
min-height: 0;
|
|
496
537
|
overflow-y: scroll; /* has to be scroll, not auto */
|
|
497
|
-
|
|
538
|
+
overscroll-behavior: contain;
|
|
539
|
+
-webkit-overflow-scrolling: touch;
|
|
498
540
|
}
|
|
499
541
|
.asd20-site-menu__item {
|
|
500
542
|
display: block;
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
/> -->
|
|
41
41
|
<asd20-action-menu
|
|
42
42
|
v-show="
|
|
43
|
-
(menuOpen && (mq === 'sm' || mq === 'md') && isLoaded
|
|
43
|
+
(menuOpen && (mq === 'sm' || mq === 'md') && isLoaded) ||
|
|
44
44
|
((mq === 'lg' || mq === 'xl') && isLoaded)
|
|
45
45
|
"
|
|
46
46
|
:items="actionItems"
|
|
@@ -54,7 +54,6 @@
|
|
|
54
54
|
<asd20-site-menu
|
|
55
55
|
v-show="menuOpen"
|
|
56
56
|
ref="siteMenu"
|
|
57
|
-
:class="zoomed ? 'zoomed' : ''"
|
|
58
57
|
:active="menuOpen"
|
|
59
58
|
:sections="navigation"
|
|
60
59
|
:empty-nav="emptyNav"
|
|
@@ -290,11 +289,6 @@ export default {
|
|
|
290
289
|
&.active::before {
|
|
291
290
|
opacity: 1;
|
|
292
291
|
}
|
|
293
|
-
.zoomed {
|
|
294
|
-
& :deep(.asd20-site-menu__viewport) {
|
|
295
|
-
bottom: 0 !important;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
292
|
}
|
|
299
293
|
|
|
300
294
|
@media (min-width: 1024px) {
|
|
@@ -563,6 +563,10 @@ import queryAiSite from '../../../helpers/queryAiSite'
|
|
|
563
563
|
import queryFiles from '../../../helpers/queryFiles'
|
|
564
564
|
import queryPeople from '../../../helpers/queryPeople'
|
|
565
565
|
import queryPages, { NO_PAGE_RESULTS_FALLBACKS } from '../../../helpers/queryPages'
|
|
566
|
+
import {
|
|
567
|
+
acquireDocumentScrollLock,
|
|
568
|
+
releaseDocumentScrollLock,
|
|
569
|
+
} from '../../../helpers/documentScrollLock'
|
|
566
570
|
|
|
567
571
|
// Mixins
|
|
568
572
|
import globalPropMixinFactory from '../../../mixins/globalPropMixinFactory.js'
|
|
@@ -2162,18 +2166,13 @@ export default {
|
|
|
2162
2166
|
},
|
|
2163
2167
|
|
|
2164
2168
|
lockBodyScroll() {
|
|
2165
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
2166
|
-
return
|
|
2167
|
-
}
|
|
2168
2169
|
if (this.bodyScrollLock) return
|
|
2169
|
-
this.bodyScrollLock =
|
|
2170
|
+
this.bodyScrollLock = acquireDocumentScrollLock()
|
|
2170
2171
|
},
|
|
2171
2172
|
|
|
2172
2173
|
unlockBodyScroll() {
|
|
2173
|
-
if (typeof window === 'undefined' || typeof document === 'undefined') {
|
|
2174
|
-
return
|
|
2175
|
-
}
|
|
2176
2174
|
if (!this.bodyScrollLock) return
|
|
2175
|
+
releaseDocumentScrollLock(this.bodyScrollLock)
|
|
2177
2176
|
this.bodyScrollLock = null
|
|
2178
2177
|
},
|
|
2179
2178
|
|
|
@@ -3601,6 +3600,7 @@ export default {
|
|
|
3601
3600
|
display: flex;
|
|
3602
3601
|
flex-direction: column;
|
|
3603
3602
|
overflow: hidden;
|
|
3603
|
+
overscroll-behavior: none;
|
|
3604
3604
|
|
|
3605
3605
|
/* allow descendants to use flex+overflow scrolling properly (Safari/iOS) */
|
|
3606
3606
|
min-height: 0;
|
|
@@ -3852,6 +3852,8 @@ export default {
|
|
|
3852
3852
|
border-radius: 0.5rem;
|
|
3853
3853
|
padding: space(0.5);
|
|
3854
3854
|
font: inherit;
|
|
3855
|
+
// Match the iOS-safe minimum used by Asd20SearchField.
|
|
3856
|
+
font-size: $base-font-size;
|
|
3855
3857
|
color: var(--color__primary);
|
|
3856
3858
|
background: white;
|
|
3857
3859
|
box-sizing: border-box;
|
|
@@ -4188,11 +4190,6 @@ export default {
|
|
|
4188
4190
|
transform: translate3d(0, -0.75rem, 0);
|
|
4189
4191
|
}
|
|
4190
4192
|
|
|
4191
|
-
:global(html.asd20-site-search--scroll-locked),
|
|
4192
|
-
:global(body.asd20-site-search--scroll-locked) {
|
|
4193
|
-
overscroll-behavior: none;
|
|
4194
|
-
}
|
|
4195
|
-
|
|
4196
4193
|
@media (min-width: 768px) {
|
|
4197
4194
|
.asd20-site-search {
|
|
4198
4195
|
.asd20-notificaiton {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const SCROLL_LOCK_CLASS = 'asd20-document-scroll-locked'
|
|
2
|
+
|
|
3
|
+
const activeLocks = new Set()
|
|
4
|
+
let originalDocumentStyles = null
|
|
5
|
+
|
|
6
|
+
export function acquireDocumentScrollLock() {
|
|
7
|
+
if (typeof document === 'undefined') return null
|
|
8
|
+
|
|
9
|
+
const docEl = document.documentElement
|
|
10
|
+
const body = document.body
|
|
11
|
+
if (!docEl || !body) return null
|
|
12
|
+
|
|
13
|
+
// A primitive token is preserved when stored in Vue reactive component data.
|
|
14
|
+
const lock = Symbol(SCROLL_LOCK_CLASS)
|
|
15
|
+
|
|
16
|
+
if (activeLocks.size === 0) {
|
|
17
|
+
originalDocumentStyles = {
|
|
18
|
+
bodyOverflow: body.style.overflow,
|
|
19
|
+
bodyOverscrollBehavior: body.style.overscrollBehavior,
|
|
20
|
+
docOverflow: docEl.style.overflow,
|
|
21
|
+
docOverscrollBehavior: docEl.style.overscrollBehavior,
|
|
22
|
+
bodyHadLockClass: body.classList.contains(SCROLL_LOCK_CLASS),
|
|
23
|
+
docHadLockClass: docEl.classList.contains(SCROLL_LOCK_CLASS),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
body.classList.add(SCROLL_LOCK_CLASS)
|
|
27
|
+
docEl.classList.add(SCROLL_LOCK_CLASS)
|
|
28
|
+
body.style.overflow = 'hidden'
|
|
29
|
+
body.style.overscrollBehavior = 'none'
|
|
30
|
+
docEl.style.overflow = 'hidden'
|
|
31
|
+
docEl.style.overscrollBehavior = 'none'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
activeLocks.add(lock)
|
|
35
|
+
return lock
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function releaseDocumentScrollLock(lock) {
|
|
39
|
+
if (!lock || !activeLocks.delete(lock) || activeLocks.size > 0) return
|
|
40
|
+
if (typeof document === 'undefined' || !originalDocumentStyles) return
|
|
41
|
+
|
|
42
|
+
const docEl = document.documentElement
|
|
43
|
+
const body = document.body
|
|
44
|
+
if (!docEl || !body) return
|
|
45
|
+
|
|
46
|
+
body.style.overflow = originalDocumentStyles.bodyOverflow
|
|
47
|
+
body.style.overscrollBehavior =
|
|
48
|
+
originalDocumentStyles.bodyOverscrollBehavior
|
|
49
|
+
docEl.style.overflow = originalDocumentStyles.docOverflow
|
|
50
|
+
docEl.style.overscrollBehavior = originalDocumentStyles.docOverscrollBehavior
|
|
51
|
+
|
|
52
|
+
if (!originalDocumentStyles.bodyHadLockClass) {
|
|
53
|
+
body.classList.remove(SCROLL_LOCK_CLASS)
|
|
54
|
+
}
|
|
55
|
+
if (!originalDocumentStyles.docHadLockClass) {
|
|
56
|
+
docEl.classList.remove(SCROLL_LOCK_CLASS)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
originalDocumentStyles = null
|
|
60
|
+
}
|