@asd20/ui-next 2.21.5 → 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 +14 -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 +36 -41
- package/src/components/organisms/Asd20TabBar/index.vue +1 -0
- package/src/helpers/documentScrollLock.js +60 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
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
|
+
|
|
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)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* improve relevance of related pages in search results ([ecbcad7](https://github.com/academydistrict20/asd20-ui-next/commit/ecbcad7f5df39273dc6a5b1299b1c94d766d6af3))
|
|
16
|
+
|
|
3
17
|
## [2.21.5](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.4...ui-next-v2.21.5) (2026-08-21)
|
|
4
18
|
|
|
5
19
|
|
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
|
|
|
@@ -2441,10 +2440,19 @@ export default {
|
|
|
2441
2440
|
return turn.relatedPages
|
|
2442
2441
|
},
|
|
2443
2442
|
|
|
2444
|
-
buildAiRelatedPagesPreview(items =
|
|
2445
|
-
|
|
2443
|
+
buildAiRelatedPagesPreview(items = null) {
|
|
2444
|
+
const previewItems = Array.isArray(items)
|
|
2445
|
+
? items
|
|
2446
|
+
: Array.isArray(this.currentPages)
|
|
2447
|
+
? this.currentPages.map(page => ({
|
|
2448
|
+
page,
|
|
2449
|
+
label: page && page.title ? page.title : '',
|
|
2450
|
+
link: page && page.url ? page.url : '',
|
|
2451
|
+
}))
|
|
2452
|
+
: []
|
|
2453
|
+
if (!previewItems.length) return []
|
|
2446
2454
|
|
|
2447
|
-
return
|
|
2455
|
+
return previewItems
|
|
2448
2456
|
.map((item, index) => {
|
|
2449
2457
|
const page = item && item.page ? item.page : {}
|
|
2450
2458
|
if (page.isNoResultsFallback) return null
|
|
@@ -2491,12 +2499,13 @@ export default {
|
|
|
2491
2499
|
},
|
|
2492
2500
|
|
|
2493
2501
|
completeAiTurnRelatedPages(turnId, runId) {
|
|
2494
|
-
if (!turnId || !this.isCurrentSearchRun(runId)) return
|
|
2502
|
+
if (!turnId || !this.isCurrentSearchRun(runId)) return []
|
|
2503
|
+
const relatedPages = this.buildAiRelatedPagesPreview()
|
|
2495
2504
|
this.updateAiTranscriptTurn(turnId, {
|
|
2496
|
-
relatedPages
|
|
2505
|
+
relatedPages,
|
|
2497
2506
|
relatedPagesStatus: 'complete',
|
|
2498
2507
|
})
|
|
2499
|
-
return
|
|
2508
|
+
return relatedPages
|
|
2500
2509
|
},
|
|
2501
2510
|
|
|
2502
2511
|
setAiTurnSearchLogId(turnId, searchLogId) {
|
|
@@ -2691,6 +2700,7 @@ export default {
|
|
|
2691
2700
|
keywords: this.keywords,
|
|
2692
2701
|
question: this.searchQuestion || this.inputText,
|
|
2693
2702
|
}
|
|
2703
|
+
if (options.purpose) searchInput.purpose = options.purpose
|
|
2694
2704
|
|
|
2695
2705
|
this.searchingPages = true
|
|
2696
2706
|
try {
|
|
@@ -3116,12 +3126,16 @@ export default {
|
|
|
3116
3126
|
this.keywordsFromAi = true
|
|
3117
3127
|
this.skipKeywordWatcher = true
|
|
3118
3128
|
this.keywords = keywordsToUse
|
|
3119
|
-
await this.searchPages({
|
|
3129
|
+
await this.searchPages({
|
|
3130
|
+
keepAnswersTab: true,
|
|
3131
|
+
purpose: 'ai-related-pages',
|
|
3132
|
+
runId,
|
|
3133
|
+
})
|
|
3120
3134
|
if (!this.isCurrentSearchRun(runId)) return
|
|
3121
|
-
this.completeAiTurnRelatedPages(
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3135
|
+
relatedPagesForAnalytics = this.completeAiTurnRelatedPages(
|
|
3136
|
+
assistantTurnId,
|
|
3137
|
+
runId
|
|
3138
|
+
).map(p => ({ title: p.title || '', url: p.url || '' }))
|
|
3125
3139
|
this.searchFilesWithGroupOwners({ runId })
|
|
3126
3140
|
}
|
|
3127
3141
|
|
|
@@ -3169,29 +3183,12 @@ export default {
|
|
|
3169
3183
|
}
|
|
3170
3184
|
|
|
3171
3185
|
// Log AI search analytics
|
|
3172
|
-
const logPagesFromPages =
|
|
3173
|
-
relatedPagesForAnalytics.length > 0
|
|
3174
|
-
? relatedPagesForAnalytics
|
|
3175
|
-
: this.getAnalyticsPages()
|
|
3176
|
-
.slice(0, 10)
|
|
3177
|
-
.map(p => ({ title: p.title || '', url: p.url || '' }))
|
|
3178
|
-
const logPagesFromSources = (
|
|
3179
|
-
Array.isArray(this.aiSources) ? this.aiSources : []
|
|
3180
|
-
)
|
|
3181
|
-
.slice(0, 10)
|
|
3182
|
-
.map(source => ({
|
|
3183
|
-
title: source && source.title ? source.title : '',
|
|
3184
|
-
url: source && source.url ? source.url : '',
|
|
3185
|
-
}))
|
|
3186
|
-
.filter(source => source.url)
|
|
3187
|
-
const logPages =
|
|
3188
|
-
logPagesFromPages.length > 0 ? logPagesFromPages : logPagesFromSources
|
|
3189
3186
|
const logPromise = logSearchAnalytics({
|
|
3190
3187
|
question: normalizedQuestion,
|
|
3191
3188
|
searchType: 'ai_chat',
|
|
3192
3189
|
keywords: this.keywords || '',
|
|
3193
3190
|
hadResults: !!this.aiAnswer,
|
|
3194
|
-
relatedPages:
|
|
3191
|
+
relatedPages: relatedPagesForAnalytics,
|
|
3195
3192
|
organizationId:
|
|
3196
3193
|
this.organization && this.organization.id
|
|
3197
3194
|
? this.organization.id
|
|
@@ -3603,6 +3600,7 @@ export default {
|
|
|
3603
3600
|
display: flex;
|
|
3604
3601
|
flex-direction: column;
|
|
3605
3602
|
overflow: hidden;
|
|
3603
|
+
overscroll-behavior: none;
|
|
3606
3604
|
|
|
3607
3605
|
/* allow descendants to use flex+overflow scrolling properly (Safari/iOS) */
|
|
3608
3606
|
min-height: 0;
|
|
@@ -3854,6 +3852,8 @@ export default {
|
|
|
3854
3852
|
border-radius: 0.5rem;
|
|
3855
3853
|
padding: space(0.5);
|
|
3856
3854
|
font: inherit;
|
|
3855
|
+
// Match the iOS-safe minimum used by Asd20SearchField.
|
|
3856
|
+
font-size: $base-font-size;
|
|
3857
3857
|
color: var(--color__primary);
|
|
3858
3858
|
background: white;
|
|
3859
3859
|
box-sizing: border-box;
|
|
@@ -4190,11 +4190,6 @@ export default {
|
|
|
4190
4190
|
transform: translate3d(0, -0.75rem, 0);
|
|
4191
4191
|
}
|
|
4192
4192
|
|
|
4193
|
-
:global(html.asd20-site-search--scroll-locked),
|
|
4194
|
-
:global(body.asd20-site-search--scroll-locked) {
|
|
4195
|
-
overscroll-behavior: none;
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
4193
|
@media (min-width: 768px) {
|
|
4199
4194
|
.asd20-site-search {
|
|
4200
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
|
+
}
|