@asd20/ui-next 2.24.3 → 2.24.4
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/components.js +0 -1
- package/helpers.js +0 -1
- package/package.json +1 -1
- package/src/helpers/injectedContentInteractions.js +0 -26
- package/templates.js +0 -1
- package/src/components/organisms/Asd20AccessibilityIssueModal/index.vue +0 -375
- package/src/components/templates/Asd20WayfindingAccessibilityTemplate/index.vue +0 -310
- package/src/helpers/sendAccessibilityIssue.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.24.4](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.24.3...ui-next-v2.24.4) (2026-09-17)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* remove legacy accessibility form templates and components ([3e9fa07](https://github.com/academydistrict20/asd20-ui-next/commit/3e9fa0749a9000f9c8970bbb8d1a81ed665a94e8))
|
|
9
|
+
|
|
3
10
|
## [2.24.3](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.24.2...ui-next-v2.24.3) (2026-09-17)
|
|
4
11
|
|
|
5
12
|
|
package/components.js
CHANGED
|
@@ -66,7 +66,6 @@ export { default as Asd20Toolbar } from './src/components/molecules/Asd20Toolbar
|
|
|
66
66
|
export { default as Asd20WidgetGroup } from './src/components/molecules/Asd20WidgetGroup.vue'
|
|
67
67
|
|
|
68
68
|
// Organisms
|
|
69
|
-
export { default as Asd20AccessibilityIssueModal } from './src/components/organisms/Asd20AccessibilityIssueModal/index.vue'
|
|
70
69
|
export { default as Asd20ActionMenu } from './src/components/organisms/Asd20ActionMenu/index.vue'
|
|
71
70
|
export { default as Asd20AiSearch } from './src/components/organisms/Asd20AiSearch/index.vue'
|
|
72
71
|
export { default as Asd20AppHeader } from './src/components/organisms/Asd20AppHeader/index.vue'
|
package/helpers.js
CHANGED
|
@@ -50,6 +50,5 @@ export { default as queryFiles } from './src/helpers/queryFiles.js'
|
|
|
50
50
|
export { default as queryMessages } from './src/helpers/queryMessages.js'
|
|
51
51
|
export { default as queryPages } from './src/helpers/queryPages.js'
|
|
52
52
|
export { default as queryPeople } from './src/helpers/queryPeople.js'
|
|
53
|
-
export { default as sendAccessibilityIssue } from './src/helpers/sendAccessibilityIssue.js'
|
|
54
53
|
export { default as sendEmail } from './src/helpers/sendEmail.js'
|
|
55
54
|
export { default as sortOrgsByLevel } from './src/helpers/sortOrgsByLevel.js'
|
package/package.json
CHANGED
|
@@ -74,29 +74,3 @@ export function bindInjectedAccordionInteractions(root) {
|
|
|
74
74
|
root.removeEventListener('click', onClick)
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
export function bindAccessibilityModalTrigger(root, onOpen) {
|
|
79
|
-
if (!root?.addEventListener) {
|
|
80
|
-
return () => {}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const onClick = event => {
|
|
84
|
-
const trigger = findClosestInRoot(
|
|
85
|
-
event,
|
|
86
|
-
'[data-trigger-accessibility-modal]',
|
|
87
|
-
root
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
if (!trigger) {
|
|
91
|
-
return
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
onOpen()
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
root.addEventListener('click', onClick)
|
|
98
|
-
|
|
99
|
-
return () => {
|
|
100
|
-
root.removeEventListener('click', onClick)
|
|
101
|
-
}
|
|
102
|
-
}
|
package/templates.js
CHANGED
|
@@ -23,7 +23,6 @@ export { default as Asd20SalaryCalculatorTemplate } from './src/components/templ
|
|
|
23
23
|
export { default as Asd20SchoolHomeTemplate } from './src/components/templates/Asd20SchoolHomeTemplate/index.vue'
|
|
24
24
|
export { default as Asd20SchoolHomeVideoTemplate } from './src/components/templates/Asd20SchoolHomeVideoTemplate/index.vue'
|
|
25
25
|
export { default as Asd20SearchAppTemplate } from './src/components/templates/Asd20SearchAppTemplate/index.vue'
|
|
26
|
-
export { default as Asd20WayfindingAccessibilityTemplate } from './src/components/templates/Asd20WayfindingAccessibilityTemplate/index.vue'
|
|
27
26
|
export { default as Asd20WayfindingAlternateTemplate } from './src/components/templates/Asd20WayfindingAlternateTemplate/index.vue'
|
|
28
27
|
export { default as Asd20WayfindingImageTemplate } from './src/components/templates/Asd20WayfindingImageTemplate/index.vue'
|
|
29
28
|
export { default as Asd20WayfindingTemplate } from './src/components/templates/Asd20WayfindingTemplate/index.vue'
|
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<asd20-modal
|
|
3
|
-
:open="open"
|
|
4
|
-
title="Report an Accessibility Issue"
|
|
5
|
-
icon="accessibility"
|
|
6
|
-
dismissable
|
|
7
|
-
class="asd20-accessibility-issue-modal"
|
|
8
|
-
@dismiss="$emit('dismiss')"
|
|
9
|
-
>
|
|
10
|
-
<Asd20Viewport scrollable>
|
|
11
|
-
<div
|
|
12
|
-
v-if="submissionSuccess"
|
|
13
|
-
class="submission-success"
|
|
14
|
-
role="status"
|
|
15
|
-
aria-live="polite"
|
|
16
|
-
>
|
|
17
|
-
<p style="text-align: center; font-weight: bold; font-size: 1.1rem;">
|
|
18
|
-
Thank you! Your issue has been submitted.
|
|
19
|
-
</p>
|
|
20
|
-
</div>
|
|
21
|
-
|
|
22
|
-
<template v-else>
|
|
23
|
-
<asd20-text-input
|
|
24
|
-
id="firstName"
|
|
25
|
-
v-model="form.firstName"
|
|
26
|
-
label="First Name"
|
|
27
|
-
required
|
|
28
|
-
@validated="validationErrors.firstName = $event"
|
|
29
|
-
/>
|
|
30
|
-
<asd20-text-input
|
|
31
|
-
id="lastName"
|
|
32
|
-
v-model="form.lastName"
|
|
33
|
-
label="Last Name"
|
|
34
|
-
required
|
|
35
|
-
@validated="validationErrors.lastName = $event"
|
|
36
|
-
/>
|
|
37
|
-
<asd20-text-input
|
|
38
|
-
id="email"
|
|
39
|
-
v-model="form.email"
|
|
40
|
-
label="Email Address"
|
|
41
|
-
type="email"
|
|
42
|
-
required
|
|
43
|
-
:validator="validateEmailAddress"
|
|
44
|
-
@validated="validationErrors.email = $event"
|
|
45
|
-
/>
|
|
46
|
-
<asd20-text-input
|
|
47
|
-
id="phone"
|
|
48
|
-
v-model="form.phone"
|
|
49
|
-
label="Phone Number"
|
|
50
|
-
/>
|
|
51
|
-
<asd20-text-input
|
|
52
|
-
id="address"
|
|
53
|
-
v-model="form.address"
|
|
54
|
-
label="Mailing Address"
|
|
55
|
-
/>
|
|
56
|
-
<asd20-text-input
|
|
57
|
-
id="organization"
|
|
58
|
-
v-model="form.organization"
|
|
59
|
-
label="Organization"
|
|
60
|
-
/>
|
|
61
|
-
<asd20-text-area-input
|
|
62
|
-
id="issue"
|
|
63
|
-
v-model="form.issue"
|
|
64
|
-
label="Describe the Issue"
|
|
65
|
-
maxlength="2000"
|
|
66
|
-
help-text="<ul style='margin: 0 0 0.5rem 1rem; font-size: 0.75rem !important;'>
|
|
67
|
-
<li>Specify the exact URL, program, service, or digital asset you had a problem with.</li>
|
|
68
|
-
<li>Provide as much detail as possible, including the nature of the accessibility barrier, the date and time it occurred, any assistive technology you were using, or the nature of the program or service being denied.</li>
|
|
69
|
-
<li>Describe what you believe should be done to resolve the issue.</li><br>
|
|
70
|
-
</ul>"
|
|
71
|
-
required
|
|
72
|
-
:validator="validateIssue"
|
|
73
|
-
@validated="validationErrors.issue = $event"
|
|
74
|
-
/>
|
|
75
|
-
<div
|
|
76
|
-
class="asd20-accessibility-issue-modal__honeypot"
|
|
77
|
-
aria-hidden="true"
|
|
78
|
-
>
|
|
79
|
-
<label for="accessibility-website">Website</label>
|
|
80
|
-
<input
|
|
81
|
-
id="accessibility-website"
|
|
82
|
-
v-model="form.website"
|
|
83
|
-
type="text"
|
|
84
|
-
name="website"
|
|
85
|
-
tabindex="-1"
|
|
86
|
-
autocomplete="off"
|
|
87
|
-
>
|
|
88
|
-
</div>
|
|
89
|
-
<div
|
|
90
|
-
class="asd20-accessibility-issue-modal__submit"
|
|
91
|
-
aria-live="polite"
|
|
92
|
-
>
|
|
93
|
-
<Recaptcha
|
|
94
|
-
v-if="!sending"
|
|
95
|
-
sitekey="6LfidKoUAAAAAFqr3QEbia3jIkecsZyxBYlMvWrX"
|
|
96
|
-
:load-recaptcha-script="true"
|
|
97
|
-
size="invisible"
|
|
98
|
-
@verify="captchaVerified"
|
|
99
|
-
@error="captchaFailed"
|
|
100
|
-
>
|
|
101
|
-
<asd20-button
|
|
102
|
-
label="Submit Issue"
|
|
103
|
-
:disabled="!isValid || sending"
|
|
104
|
-
horizontal
|
|
105
|
-
centered
|
|
106
|
-
bordered
|
|
107
|
-
@click="validateBeforeSend"
|
|
108
|
-
/>
|
|
109
|
-
</Recaptcha>
|
|
110
|
-
<asd20-spinner
|
|
111
|
-
v-else
|
|
112
|
-
size="sm"
|
|
113
|
-
/>
|
|
114
|
-
</div>
|
|
115
|
-
<div
|
|
116
|
-
v-if="feedbackMessage"
|
|
117
|
-
ref="feedback"
|
|
118
|
-
class="asd20-accessibility-issue-modal__feedback"
|
|
119
|
-
:role="feedbackRole"
|
|
120
|
-
tabindex="-1"
|
|
121
|
-
>
|
|
122
|
-
{{ feedbackMessage }}
|
|
123
|
-
</div>
|
|
124
|
-
</template>
|
|
125
|
-
</Asd20Viewport>
|
|
126
|
-
</asd20-modal>
|
|
127
|
-
</template>
|
|
128
|
-
|
|
129
|
-
<script>
|
|
130
|
-
import Asd20Modal from '../../molecules/Asd20Modal'
|
|
131
|
-
import Asd20TextInput from '../../molecules/Asd20TextInput'
|
|
132
|
-
import Asd20TextAreaInput from '../../molecules/Asd20TextAreaInput'
|
|
133
|
-
import Asd20Viewport from '../../atoms/Asd20Viewport'
|
|
134
|
-
import Asd20Spinner from '../../atoms/Asd20Spinner'
|
|
135
|
-
import Asd20Button from '../../atoms/Asd20Button'
|
|
136
|
-
import Recaptcha from '../../utils/Recaptcha'
|
|
137
|
-
import sendAccessibilityIssue, {
|
|
138
|
-
resolveAccessibilityIssueEndpoint,
|
|
139
|
-
} from '../../../helpers/sendAccessibilityIssue.js'
|
|
140
|
-
|
|
141
|
-
export default {
|
|
142
|
-
name: 'Asd20AccessibilityIssueModal',
|
|
143
|
-
components: {
|
|
144
|
-
Asd20Modal,
|
|
145
|
-
Asd20TextInput,
|
|
146
|
-
Asd20TextAreaInput,
|
|
147
|
-
Asd20Viewport,
|
|
148
|
-
Asd20Spinner,
|
|
149
|
-
Asd20Button,
|
|
150
|
-
Recaptcha,
|
|
151
|
-
},
|
|
152
|
-
props: {
|
|
153
|
-
open: { type: Boolean, default: false },
|
|
154
|
-
},
|
|
155
|
-
data() {
|
|
156
|
-
return {
|
|
157
|
-
sending: false,
|
|
158
|
-
submissionSuccess: false,
|
|
159
|
-
feedbackMessage: '',
|
|
160
|
-
feedbackRole: 'alert',
|
|
161
|
-
form: {
|
|
162
|
-
firstName: '',
|
|
163
|
-
lastName: '',
|
|
164
|
-
email: '',
|
|
165
|
-
phone: '',
|
|
166
|
-
address: '',
|
|
167
|
-
organization: '',
|
|
168
|
-
issue: '',
|
|
169
|
-
website: '',
|
|
170
|
-
},
|
|
171
|
-
validationErrors: {
|
|
172
|
-
firstName: [],
|
|
173
|
-
lastName: [],
|
|
174
|
-
email: [],
|
|
175
|
-
issue: [],
|
|
176
|
-
},
|
|
177
|
-
}
|
|
178
|
-
},
|
|
179
|
-
computed: {
|
|
180
|
-
isValid() {
|
|
181
|
-
return Object.values(this.validationErrors).every(e => e.length === 0)
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
watch: {
|
|
185
|
-
open(value) {
|
|
186
|
-
if (value) {
|
|
187
|
-
this.submissionSuccess = false
|
|
188
|
-
this.feedbackMessage = ''
|
|
189
|
-
}
|
|
190
|
-
},
|
|
191
|
-
},
|
|
192
|
-
methods: {
|
|
193
|
-
validateEmailAddress({ value, validationErrors }) {
|
|
194
|
-
const pattern = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
|
|
195
|
-
if (!pattern.test(value)) {
|
|
196
|
-
validationErrors.push('A valid email address is required')
|
|
197
|
-
}
|
|
198
|
-
return validationErrors
|
|
199
|
-
},
|
|
200
|
-
validateIssue({ value, validationErrors }) {
|
|
201
|
-
const maxLength = 2000
|
|
202
|
-
if (!value || value.trim().length === 0) {
|
|
203
|
-
validationErrors.push('Issue description is required.')
|
|
204
|
-
} else if (value.length > maxLength) {
|
|
205
|
-
validationErrors.push(
|
|
206
|
-
`Issue description must be fewer than ${maxLength} characters.`
|
|
207
|
-
)
|
|
208
|
-
}
|
|
209
|
-
return validationErrors
|
|
210
|
-
},
|
|
211
|
-
resolveRuntimeConfig() {
|
|
212
|
-
return (
|
|
213
|
-
this.$config ||
|
|
214
|
-
this.$?.appContext?.config?.globalProperties?.$config ||
|
|
215
|
-
{}
|
|
216
|
-
)
|
|
217
|
-
},
|
|
218
|
-
getFormValidationMessages() {
|
|
219
|
-
const messages = []
|
|
220
|
-
const emailPattern =
|
|
221
|
-
/^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/
|
|
222
|
-
|
|
223
|
-
if (!String(this.form.firstName || '').trim())
|
|
224
|
-
messages.push('First name is required.')
|
|
225
|
-
if (!String(this.form.lastName || '').trim())
|
|
226
|
-
messages.push('Last name is required.')
|
|
227
|
-
if (!emailPattern.test(String(this.form.email || '').trim()))
|
|
228
|
-
messages.push('Enter a valid email address.')
|
|
229
|
-
|
|
230
|
-
const issue = String(this.form.issue || '').trim()
|
|
231
|
-
if (!issue) messages.push('Describe the accessibility issue.')
|
|
232
|
-
else if (issue.length > 2000)
|
|
233
|
-
messages.push('The issue description must be 2,000 characters or fewer.')
|
|
234
|
-
|
|
235
|
-
return messages
|
|
236
|
-
},
|
|
237
|
-
validateBeforeSend(event) {
|
|
238
|
-
const messages = this.getFormValidationMessages()
|
|
239
|
-
if (!messages.length) return
|
|
240
|
-
|
|
241
|
-
event?.preventDefault?.()
|
|
242
|
-
this.showFeedback(messages.join(' '))
|
|
243
|
-
},
|
|
244
|
-
captchaVerified(token) {
|
|
245
|
-
if (token) this.sendAccessibilityIssue(token)
|
|
246
|
-
},
|
|
247
|
-
captchaFailed() {
|
|
248
|
-
this.showFeedback(
|
|
249
|
-
'The verification service could not start. Please try again or use the email and phone options on this page.'
|
|
250
|
-
)
|
|
251
|
-
},
|
|
252
|
-
showFeedback(message, role = 'alert') {
|
|
253
|
-
this.feedbackMessage = message
|
|
254
|
-
this.feedbackRole = role
|
|
255
|
-
this.$nextTick(() => this.$refs.feedback?.focus?.())
|
|
256
|
-
},
|
|
257
|
-
getServerFailureMessage(error) {
|
|
258
|
-
const status = Number(error?.response?.status || 0)
|
|
259
|
-
const serverMessage = error?.response?.data?.error
|
|
260
|
-
|
|
261
|
-
if (status === 400 && serverMessage) return serverMessage
|
|
262
|
-
if (status === 403)
|
|
263
|
-
return 'The verification was not accepted. Please try again.'
|
|
264
|
-
if (status === 429)
|
|
265
|
-
return 'Too many reports were submitted recently. Please wait and try again, or use the email and phone options on this page.'
|
|
266
|
-
|
|
267
|
-
return 'Something went wrong while submitting the form. Please try again later or use the email and phone options on this page.'
|
|
268
|
-
},
|
|
269
|
-
async sendAccessibilityIssue(captchaToken = '') {
|
|
270
|
-
const validationMessages = this.getFormValidationMessages()
|
|
271
|
-
if (validationMessages.length) {
|
|
272
|
-
this.showFeedback(validationMessages.join(' '))
|
|
273
|
-
return
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const endpoint = resolveAccessibilityIssueEndpoint({
|
|
277
|
-
runtimeConfig: this.resolveRuntimeConfig(),
|
|
278
|
-
})
|
|
279
|
-
const submissionClient =
|
|
280
|
-
this.$sendAccessibilityIssue ||
|
|
281
|
-
(endpoint
|
|
282
|
-
? async issue => await sendAccessibilityIssue(issue, endpoint)
|
|
283
|
-
: null)
|
|
284
|
-
|
|
285
|
-
if (!submissionClient) {
|
|
286
|
-
console.error('Accessibility issue endpoint is not configured.')
|
|
287
|
-
this.showFeedback(
|
|
288
|
-
'Issue reporting is not configured for this environment. Please use the email and phone options on this page.'
|
|
289
|
-
)
|
|
290
|
-
return
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
this.sending = true
|
|
294
|
-
this.feedbackMessage = ''
|
|
295
|
-
|
|
296
|
-
try {
|
|
297
|
-
await submissionClient({
|
|
298
|
-
...this.form,
|
|
299
|
-
captchaToken,
|
|
300
|
-
})
|
|
301
|
-
this.submissionSuccess = true
|
|
302
|
-
|
|
303
|
-
// Wait 2 seconds, then close the modal
|
|
304
|
-
setTimeout(() => {
|
|
305
|
-
this.submissionSuccess = false
|
|
306
|
-
this.form = {
|
|
307
|
-
firstName: '',
|
|
308
|
-
lastName: '',
|
|
309
|
-
email: '',
|
|
310
|
-
phone: '',
|
|
311
|
-
address: '',
|
|
312
|
-
organization: '',
|
|
313
|
-
issue: '',
|
|
314
|
-
website: '',
|
|
315
|
-
}
|
|
316
|
-
this.$emit('dismiss')
|
|
317
|
-
}, 2000)
|
|
318
|
-
} catch (e) {
|
|
319
|
-
console.error('Accessibility issue submission failed:', e.message || e)
|
|
320
|
-
this.showFeedback(this.getServerFailureMessage(e))
|
|
321
|
-
} finally {
|
|
322
|
-
this.sending = false
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
}
|
|
327
|
-
</script>
|
|
328
|
-
|
|
329
|
-
<style lang="scss" scoped>
|
|
330
|
-
@use '../../../design/component-stack' as *;
|
|
331
|
-
|
|
332
|
-
.asd20-accessibility-issue-modal {
|
|
333
|
-
& :deep(.asd20-modal__content .asd20-viewport) {
|
|
334
|
-
padding: space(1);
|
|
335
|
-
}
|
|
336
|
-
& :deep(.asd20-text-area-input textarea) {
|
|
337
|
-
font-family: Arial, sans-serif;
|
|
338
|
-
font-size: 1rem;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
&__submit {
|
|
342
|
-
display: flex;
|
|
343
|
-
justify-content: center;
|
|
344
|
-
min-height: 3rem;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
&__feedback {
|
|
348
|
-
font-weight: 600;
|
|
349
|
-
margin: space(0.5) 0;
|
|
350
|
-
text-align: center;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
&__honeypot {
|
|
354
|
-
position: absolute;
|
|
355
|
-
width: 1px;
|
|
356
|
-
height: 1px;
|
|
357
|
-
margin: -1px;
|
|
358
|
-
overflow: hidden;
|
|
359
|
-
clip: rect(0, 0, 0, 0);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
@media (min-width: 1024px) {
|
|
364
|
-
.asd20-accessibility-issue-modal {
|
|
365
|
-
& :deep(.asd20-modal__content .asd20-viewport) {
|
|
366
|
-
padding: space(1);
|
|
367
|
-
}
|
|
368
|
-
& :deep(.asd20-modal) {
|
|
369
|
-
margin-top: auto;
|
|
370
|
-
margin-bottom: auto;
|
|
371
|
-
max-width: 40vw;
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
</style>
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="asd20-template-wayfinding-accessibility">
|
|
3
|
-
<!-- Skip Nav -->
|
|
4
|
-
<asd20-skip-to></asd20-skip-to>
|
|
5
|
-
|
|
6
|
-
<!-- Site Navigation -->
|
|
7
|
-
<asd20-site-navigation
|
|
8
|
-
v-bind="siteNavigationSearchProps"
|
|
9
|
-
:navigation="navigation"
|
|
10
|
-
:action-items="actionItems"
|
|
11
|
-
:menu-open="menuOpen"
|
|
12
|
-
:search-open="searchOpen"
|
|
13
|
-
:organization="organization"
|
|
14
|
-
:organization-options="organizationOptions"
|
|
15
|
-
@update:menuOpen="$emit('update:menuOpen', $event)"
|
|
16
|
-
@update:searchOpen="$emit('update:searchOpen', $event)"
|
|
17
|
-
>
|
|
18
|
-
</asd20-site-navigation>
|
|
19
|
-
|
|
20
|
-
<!-- Banner Notifications -->
|
|
21
|
-
<asd20-notification-group
|
|
22
|
-
:notifications="activeNotificationsByType.banner"
|
|
23
|
-
group-type="banner"
|
|
24
|
-
@dismiss="$emit('dismiss-notification', $event)"
|
|
25
|
-
@toggle-all="$emit('toggle-all')"
|
|
26
|
-
></asd20-notification-group>
|
|
27
|
-
|
|
28
|
-
<!-- Page Header -->
|
|
29
|
-
<asd20-page-header
|
|
30
|
-
v-show="pageHeaderContent"
|
|
31
|
-
v-bind="pageHeaderContent"
|
|
32
|
-
id="page-header"
|
|
33
|
-
:organization="organization"
|
|
34
|
-
:organization-options="organizationOptions"
|
|
35
|
-
:language-code="languageCode"
|
|
36
|
-
:breadcrumb-links="breadcrumbLinks"
|
|
37
|
-
@update:languageCode="$emit('update:languageCode', $event)"
|
|
38
|
-
>
|
|
39
|
-
<!-- Floating Notifications -->
|
|
40
|
-
<template #top>
|
|
41
|
-
<asd20-notification-group
|
|
42
|
-
class="asd20-notification-group--floating"
|
|
43
|
-
:notifications="activeNotificationsByType.floating"
|
|
44
|
-
:total-dismissed-notifications="totalDismissedNotifications"
|
|
45
|
-
group-type="floating"
|
|
46
|
-
@dismiss="$emit('dismiss-notification', $event)"
|
|
47
|
-
@toggle-all="$emit('toggle-all')"
|
|
48
|
-
></asd20-notification-group>
|
|
49
|
-
</template>
|
|
50
|
-
<!-- <template #top><asd20-breadcrumb :links="breadcrumbLinks"></asd20-breadcrumb></template> -->
|
|
51
|
-
</asd20-page-header>
|
|
52
|
-
|
|
53
|
-
<!-- Inline-Notifications -->
|
|
54
|
-
<asd20-notification-group
|
|
55
|
-
class="asd20-notification-group--inline"
|
|
56
|
-
:notifications="activeNotificationsByType.inline"
|
|
57
|
-
group-type="inline"
|
|
58
|
-
@dismiss="$emit('dismiss-notification', $event)"
|
|
59
|
-
@toggle-all="$emit('toggle-all')"
|
|
60
|
-
></asd20-notification-group>
|
|
61
|
-
|
|
62
|
-
<!-- Page Content -->
|
|
63
|
-
<asd20-page-content
|
|
64
|
-
class="intro-message"
|
|
65
|
-
:primary-messages="primaryMessage"
|
|
66
|
-
omit-detail-links
|
|
67
|
-
omit-calls-to-action
|
|
68
|
-
/>
|
|
69
|
-
|
|
70
|
-
<!-- Feed Section -->
|
|
71
|
-
<asd20-feeds-section
|
|
72
|
-
:announcements="announcements"
|
|
73
|
-
:announcements-feed-props="announcementsFeedProps"
|
|
74
|
-
:stories="stories"
|
|
75
|
-
:stories-feed-props="storiesFeedProps"
|
|
76
|
-
:events="events"
|
|
77
|
-
:events-feed-props="eventsFeedProps"
|
|
78
|
-
:organization="organization"
|
|
79
|
-
:organization-options="organizationOptions"
|
|
80
|
-
@announcements-in-view="$emit('announcements-in-view')"
|
|
81
|
-
@stories-in-view="$emit('stories-in-view')"
|
|
82
|
-
@events-in-view="$emit('events-in-view')"
|
|
83
|
-
></asd20-feeds-section>
|
|
84
|
-
|
|
85
|
-
<asd20-page-content
|
|
86
|
-
:class="wayFindingPrimaryMessages.length > 3 ? 'limit-message-width' : ''"
|
|
87
|
-
:primary-messages="wayFindingPrimaryMessages"
|
|
88
|
-
:secondary-messages="secondaryMessages"
|
|
89
|
-
omit-body-content
|
|
90
|
-
omit-calls-to-action
|
|
91
|
-
grid
|
|
92
|
-
/>
|
|
93
|
-
|
|
94
|
-
<!-- Photos & Videos -->
|
|
95
|
-
<asd20-media-section
|
|
96
|
-
v-if="firstMessage.images.length > 1 || firstMessage.videos.length"
|
|
97
|
-
:images="firstMessage.images || []"
|
|
98
|
-
:videos="firstMessage.videos || []"
|
|
99
|
-
>
|
|
100
|
-
</asd20-media-section>
|
|
101
|
-
|
|
102
|
-
<!-- Sidebar Widgets -->
|
|
103
|
-
<asd20-widgets-section
|
|
104
|
-
v-if="widgetsSectionProps.sidebar"
|
|
105
|
-
v-bind="widgetsSectionProps.sidebar"
|
|
106
|
-
@events-in-view="$emit('events-in-view')"
|
|
107
|
-
@files-in-view="$emit('files-in-view')"
|
|
108
|
-
@people-in-view="$emit('people-in-view')"
|
|
109
|
-
/>
|
|
110
|
-
|
|
111
|
-
<!-- Bottom Widgets -->
|
|
112
|
-
<asd20-widgets-section
|
|
113
|
-
v-if="widgetsSectionProps.footer || $slots.widgets"
|
|
114
|
-
v-bind="widgetsSectionProps.footer"
|
|
115
|
-
full
|
|
116
|
-
@events-in-view="$emit('events-in-view')"
|
|
117
|
-
@files-in-view="$emit('files-in-view')"
|
|
118
|
-
@people-in-view="$emit('people-in-view')"
|
|
119
|
-
>
|
|
120
|
-
<slot
|
|
121
|
-
name="widgets"
|
|
122
|
-
/>
|
|
123
|
-
</asd20-widgets-section>
|
|
124
|
-
|
|
125
|
-
<slot name="before-footer" />
|
|
126
|
-
|
|
127
|
-
<!-- Quick Links -->
|
|
128
|
-
<asd20-quicklinks-menu
|
|
129
|
-
:quick-links="quickLinks"
|
|
130
|
-
>
|
|
131
|
-
<slot name="quicklinks-menu" />
|
|
132
|
-
</asd20-quicklinks-menu>
|
|
133
|
-
|
|
134
|
-
<!-- Footer -->
|
|
135
|
-
<asd20-page-footer
|
|
136
|
-
:organization="organization"
|
|
137
|
-
:social-links="socialLinks"
|
|
138
|
-
:disclosure-links="disclosureLinks"
|
|
139
|
-
:website-logo-props="websiteLogoProps"
|
|
140
|
-
:website-logo-props2="websiteLogoProps2"
|
|
141
|
-
>
|
|
142
|
-
<slot name="page-footer" />
|
|
143
|
-
</asd20-page-footer>
|
|
144
|
-
<Asd20AccessibilityIssueModal
|
|
145
|
-
v-show="modalVisible"
|
|
146
|
-
:open="modalOpen"
|
|
147
|
-
@dismiss="modalOpen = false"
|
|
148
|
-
/>
|
|
149
|
-
</div>
|
|
150
|
-
</template>
|
|
151
|
-
|
|
152
|
-
<script>
|
|
153
|
-
// Components
|
|
154
|
-
import Asd20SkipTo from '../../../components/atoms/Asd20SkipTo'
|
|
155
|
-
import Asd20PageHeader from '../../../components/organisms/Asd20PageHeader'
|
|
156
|
-
|
|
157
|
-
import Asd20SiteNavigation from '../../../components/organisms/Asd20SiteNavigation'
|
|
158
|
-
import Asd20PageContent from '../../../components/organisms/Asd20PageContent'
|
|
159
|
-
|
|
160
|
-
import Asd20WidgetsSection from '../../../components/organisms/Asd20WidgetsSection'
|
|
161
|
-
import Asd20FeedsSection from '../../../components/organisms/Asd20FeedsSection'
|
|
162
|
-
import Asd20MediaSection from '../../../components/organisms/Asd20MediaSection'
|
|
163
|
-
import Asd20AccessibilityIssueModal from '../../../components/organisms/Asd20AccessibilityIssueModal'
|
|
164
|
-
import Asd20PageFooter from '../../../components/organisms/Asd20PageFooter'
|
|
165
|
-
import Asd20QuicklinksMenu from '../../../components/organisms/Asd20QuicklinksMenu'
|
|
166
|
-
import Asd20NotificationGroup from '../../../components/organisms/Asd20NotificationGroup'
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Mixins
|
|
170
|
-
import pageTemplateMixin from '../../../mixins/pageTemplateMixin'
|
|
171
|
-
import { bindAccessibilityModalTrigger } from '../../../helpers/injectedContentInteractions'
|
|
172
|
-
|
|
173
|
-
export default {
|
|
174
|
-
name: 'Asd20WayfindingAccessibilityTemplate',
|
|
175
|
-
components: {
|
|
176
|
-
Asd20PageContent,
|
|
177
|
-
Asd20SkipTo,
|
|
178
|
-
Asd20PageHeader,
|
|
179
|
-
Asd20PageFooter,
|
|
180
|
-
Asd20SiteNavigation,
|
|
181
|
-
Asd20WidgetsSection,
|
|
182
|
-
Asd20FeedsSection,
|
|
183
|
-
Asd20NotificationGroup,
|
|
184
|
-
Asd20QuicklinksMenu,
|
|
185
|
-
Asd20MediaSection,
|
|
186
|
-
Asd20AccessibilityIssueModal,
|
|
187
|
-
},
|
|
188
|
-
mixins: [pageTemplateMixin],
|
|
189
|
-
data() {
|
|
190
|
-
return {
|
|
191
|
-
modalVisible: false,
|
|
192
|
-
modalOpen: false,
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
computed: {
|
|
196
|
-
wayFindingPrimaryMessages() {
|
|
197
|
-
// wayfinding page
|
|
198
|
-
return (
|
|
199
|
-
this.messages
|
|
200
|
-
// Remove the first message, limit
|
|
201
|
-
.slice(
|
|
202
|
-
1,
|
|
203
|
-
Math.min(this.messages.length, this.primaryMessageLimit + 1)
|
|
204
|
-
)
|
|
205
|
-
)
|
|
206
|
-
},
|
|
207
|
-
primaryMessage() {
|
|
208
|
-
const introMessage = this.primaryMessages.slice(0, 1)
|
|
209
|
-
if (introMessage[0].bodyHtml === '' || null) return
|
|
210
|
-
return introMessage
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
mounted() {
|
|
214
|
-
this.removeAccessibilityModalTrigger = bindAccessibilityModalTrigger(
|
|
215
|
-
this.$el,
|
|
216
|
-
() => {
|
|
217
|
-
this.modalVisible = true
|
|
218
|
-
this.$nextTick(() => {
|
|
219
|
-
this.modalOpen = true
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
)
|
|
223
|
-
},
|
|
224
|
-
beforeUnmount() {
|
|
225
|
-
this.removeAccessibilityModalTrigger?.()
|
|
226
|
-
},
|
|
227
|
-
}
|
|
228
|
-
</script>
|
|
229
|
-
|
|
230
|
-
<style src="../../../design/tokens.css"></style>
|
|
231
|
-
|
|
232
|
-
<style lang="scss">
|
|
233
|
-
@use '../../../design/template-stack' as *;
|
|
234
|
-
.asd20-template-wayfinding-accessibility {
|
|
235
|
-
@include typography;
|
|
236
|
-
@include template;
|
|
237
|
-
display: flex;
|
|
238
|
-
flex-direction: column;
|
|
239
|
-
flex-grow: 1;
|
|
240
|
-
flex-shrink: 0;
|
|
241
|
-
overflow: hidden;
|
|
242
|
-
margin-top: space(2.25);
|
|
243
|
-
.asd20-notification-group--floating {
|
|
244
|
-
position: absolute;
|
|
245
|
-
top: space(2);
|
|
246
|
-
right: space(0.5);
|
|
247
|
-
}
|
|
248
|
-
.asd20-notification-group--inline {
|
|
249
|
-
margin-top: space(2) !important;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
.intro-message {
|
|
253
|
-
.primary-messaging-section {
|
|
254
|
-
justify-content: center;
|
|
255
|
-
margin-top: space(1) !important;
|
|
256
|
-
padding: space(1) space(2) !important;
|
|
257
|
-
.asd20-messaging {
|
|
258
|
-
border-top: space(0.25) solid var(--color__tertiary);
|
|
259
|
-
border-bottom: space(0.25) solid var(--color__tertiary);
|
|
260
|
-
padding: space(1) 0;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
p,
|
|
265
|
-
p + ul,
|
|
266
|
-
p + ol {
|
|
267
|
-
font-size: 1rem !important;
|
|
268
|
-
line-height: 1.5;
|
|
269
|
-
font-family: var(--website-typography__font-family-headlines);
|
|
270
|
-
}
|
|
271
|
-
h2 {
|
|
272
|
-
@include asd20-font(
|
|
273
|
-
1.5,
|
|
274
|
-
var(--website-typography__font-family-headlines),
|
|
275
|
-
1.2,
|
|
276
|
-
700
|
|
277
|
-
);
|
|
278
|
-
font-family: var(--website-typography__font-family-headlines);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
@media (min-width: 1024px) {
|
|
284
|
-
.asd20-template-wayfinding-accessibility {
|
|
285
|
-
.asd20-notification-group--floating {
|
|
286
|
-
position: absolute;
|
|
287
|
-
top: 1rem;
|
|
288
|
-
}
|
|
289
|
-
@include template-desktop;
|
|
290
|
-
margin-left: space(3);
|
|
291
|
-
margin-top: 0;
|
|
292
|
-
.asd20-page-content {
|
|
293
|
-
margin-top: space(0);
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
@media (min-width: 1790px) {
|
|
299
|
-
.limit-message-width {
|
|
300
|
-
.primary-messaging-section,
|
|
301
|
-
.secondary-messaging-section {
|
|
302
|
-
.asd20-messaging {
|
|
303
|
-
flex: 1 1 var(--minimum-column-width);
|
|
304
|
-
margin: var(--gutter) !important;
|
|
305
|
-
max-width: 30% !important;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
</style>
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
|
|
3
|
-
import { normalizeBaseUrl, resolveRuntimeConfigValue } from './runtimeConfig'
|
|
4
|
-
|
|
5
|
-
const ACCESSIBILITY_ISSUE_ROUTE = '/api/accessibility/issues'
|
|
6
|
-
|
|
7
|
-
function issueIsValid(issue) {
|
|
8
|
-
return Boolean(
|
|
9
|
-
issue &&
|
|
10
|
-
String(issue.firstName || '').trim() &&
|
|
11
|
-
String(issue.lastName || '').trim() &&
|
|
12
|
-
String(issue.email || '').trim() &&
|
|
13
|
-
String(issue.issue || '').trim()
|
|
14
|
-
)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function resolveAccessibilityIssueEndpoint({
|
|
18
|
-
endpoint = null,
|
|
19
|
-
runtimeConfig = {},
|
|
20
|
-
} = {}) {
|
|
21
|
-
const explicitEndpoint = resolveRuntimeConfigValue({
|
|
22
|
-
explicitValue: endpoint,
|
|
23
|
-
runtimeConfig,
|
|
24
|
-
runtimeConfigKeys: ['accessibilityIssueEndpoint'],
|
|
25
|
-
envKeys: ['ACCESSIBILITY_ISSUE_ENDPOINT'],
|
|
26
|
-
})
|
|
27
|
-
if (explicitEndpoint) return String(explicitEndpoint).trim()
|
|
28
|
-
|
|
29
|
-
const functionsEndpoint = resolveRuntimeConfigValue({
|
|
30
|
-
runtimeConfig,
|
|
31
|
-
runtimeConfigKeys: ['functionsEndpoint'],
|
|
32
|
-
envKeys: ['FUNCTIONS_ENDPOINT'],
|
|
33
|
-
})
|
|
34
|
-
const baseUrl = normalizeBaseUrl(functionsEndpoint)
|
|
35
|
-
|
|
36
|
-
return baseUrl ? `${baseUrl}${ACCESSIBILITY_ISSUE_ROUTE}` : null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export default async function sendAccessibilityIssue(issue, endpoint) {
|
|
40
|
-
if (!issueIsValid(issue)) {
|
|
41
|
-
throw new Error('The issue object is incomplete or invalid.')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!endpoint) {
|
|
45
|
-
throw new Error('The accessibility issue endpoint is not configured.')
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const response = await axios.post(endpoint, issue)
|
|
50
|
-
return response.data
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (error.response) {
|
|
53
|
-
const submissionError = new Error(
|
|
54
|
-
`Accessibility submission error ${error.response.status}: ${JSON.stringify(
|
|
55
|
-
error.response.data
|
|
56
|
-
)}`
|
|
57
|
-
)
|
|
58
|
-
submissionError.response = error.response
|
|
59
|
-
throw submissionError
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
throw new Error(`Accessibility submission request error: ${error.message}`)
|
|
63
|
-
}
|
|
64
|
-
}
|