@asd20/ui-next 2.24.2 → 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 +15 -0
- package/components.js +0 -1
- package/helpers.js +0 -1
- package/package.json +1 -1
- package/src/components/atoms/Asd20Button/index.vue +17 -5
- package/src/components/molecules/Asd20PublicFormField/index.vue +6 -0
- package/src/components/molecules/Asd20TextAreaInput/index.vue +2 -2
- package/src/components/molecules/Asd20TextInput/index.vue +2 -2
- package/src/components/organisms/Asd20CampusHeader/index.vue +2 -0
- package/src/components/organisms/Asd20ImageHeader/index.vue +4 -0
- package/src/components/organisms/Asd20PageHeader/index.vue +2 -0
- package/src/components/organisms/Asd20PublicFormModal/index.vue +14 -6
- package/src/components/organisms/Asd20SchoolHomepageHeader/index.vue +2 -0
- package/src/components/organisms/Asd20SchoolHomepageVideoHeader/index.vue +2 -0
- package/src/components/organisms/Asd20SchoolPageHeader/index.vue +2 -0
- package/src/graphql/pageQueryExample.graphql +2 -0
- package/src/helpers/injectedContentInteractions.js +0 -26
- package/src/mixins/inputComponentMixin.js +9 -2
- 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,20 @@
|
|
|
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
|
+
|
|
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)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* fix accessibility form verbiage; add native trigger support for public forms ([54f5b47](https://github.com/academydistrict20/asd20-ui-next/commit/54f5b47b2bb80fbf807439fcfd376e911806e053))
|
|
16
|
+
* fix release verifier from last commit ([b7b8d8f](https://github.com/academydistrict20/asd20-ui-next/commit/b7b8d8f996c757ddd727ab2272f38ea11737ddb7))
|
|
17
|
+
|
|
3
18
|
## [2.24.2](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.24.1...ui-next-v2.24.2) (2026-09-17)
|
|
4
19
|
|
|
5
20
|
|
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
|
@@ -39,6 +39,7 @@ export default {
|
|
|
39
39
|
hideLabel: { type: Boolean, default: false },
|
|
40
40
|
link: { type: String, default: '' },
|
|
41
41
|
trackingCode: { type: String, default: '' }, // e.g., 'data-analytics-id'
|
|
42
|
+
publicFormId: { type: String, default: '' },
|
|
42
43
|
icon: { type: String, default: '' },
|
|
43
44
|
badge: { type: Number, default: 0 },
|
|
44
45
|
size: {
|
|
@@ -73,7 +74,13 @@ export default {
|
|
|
73
74
|
}
|
|
74
75
|
},
|
|
75
76
|
tag() {
|
|
76
|
-
return !this.link || this.link === ' '
|
|
77
|
+
return this.normalizedPublicFormId || !this.link || this.link === ' '
|
|
78
|
+
? 'button'
|
|
79
|
+
: 'a'
|
|
80
|
+
},
|
|
81
|
+
normalizedPublicFormId() {
|
|
82
|
+
const formId = String(this.publicFormId || '').trim()
|
|
83
|
+
return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(formId) ? formId : ''
|
|
77
84
|
},
|
|
78
85
|
isExternal() {
|
|
79
86
|
return shouldOpenInNewWindow(this.link)
|
|
@@ -109,16 +116,21 @@ export default {
|
|
|
109
116
|
|
|
110
117
|
// Optional tracking code (dynamic attribute key)
|
|
111
118
|
if (this.trackingCode) a[this.trackingCode] = ''
|
|
119
|
+
if (this.normalizedPublicFormId) {
|
|
120
|
+
a['data-public-form'] = this.normalizedPublicFormId
|
|
121
|
+
}
|
|
112
122
|
|
|
113
123
|
return a
|
|
114
124
|
},
|
|
115
125
|
},
|
|
116
126
|
methods: {
|
|
117
127
|
handleClick(event) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
128
|
+
if (!this.normalizedPublicFormId) {
|
|
129
|
+
navigateInternalHref(this, event, this.link, {
|
|
130
|
+
target: event.currentTarget?.getAttribute?.('target'),
|
|
131
|
+
download: event.currentTarget?.hasAttribute?.('download'),
|
|
132
|
+
})
|
|
133
|
+
}
|
|
122
134
|
this.forwardEvent('click', event)
|
|
123
135
|
},
|
|
124
136
|
forwardEvent(name, event) {
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
:required="field.required"
|
|
9
9
|
:minlength="field.minLength"
|
|
10
10
|
:maxlength="field.maxLength"
|
|
11
|
+
:show-character-count="showCharacterCount"
|
|
12
|
+
:show-pristine-validation-errors="showPristineValidationErrors"
|
|
11
13
|
:autocomplete="field.autocomplete || undefined"
|
|
12
14
|
:aria-describedby="describedBy"
|
|
13
15
|
:aria-invalid="hasErrors ? 'true' : undefined"
|
|
@@ -23,6 +25,8 @@
|
|
|
23
25
|
:required="field.required"
|
|
24
26
|
:minlength="field.minLength"
|
|
25
27
|
:maxlength="field.maxLength"
|
|
28
|
+
:show-character-count="showCharacterCount"
|
|
29
|
+
:show-pristine-validation-errors="showPristineValidationErrors"
|
|
26
30
|
:autocomplete="field.autocomplete || undefined"
|
|
27
31
|
:aria-describedby="describedBy"
|
|
28
32
|
:aria-invalid="hasErrors ? 'true' : undefined"
|
|
@@ -127,6 +131,8 @@ export default {
|
|
|
127
131
|
modelValue: { type: [String, Boolean], default: '' },
|
|
128
132
|
errors: { type: Array, default: () => [] },
|
|
129
133
|
inputId: { type: String, required: true },
|
|
134
|
+
showCharacterCount: { type: Boolean, default: true },
|
|
135
|
+
showPristineValidationErrors: { type: Boolean, default: true },
|
|
130
136
|
},
|
|
131
137
|
emits: ['update:modelValue'],
|
|
132
138
|
data() {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<slot name="suffix" />
|
|
26
26
|
</div>
|
|
27
27
|
<div
|
|
28
|
-
v-if="
|
|
28
|
+
v-if="renderedValidationErrors.length || hasCharacterCount"
|
|
29
29
|
class="asd20-text-area-input__validation-row"
|
|
30
30
|
>
|
|
31
31
|
<div class="asd20-text-area-input__validation-errors">
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
/>
|
|
38
38
|
</div>
|
|
39
39
|
<span
|
|
40
|
-
v-if="
|
|
40
|
+
v-if="hasCharacterCount"
|
|
41
41
|
class="asd20-text-area-input__character-count"
|
|
42
42
|
>{{ characterCountText }}</span>
|
|
43
43
|
</div>
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
<slot name="suffix" />
|
|
22
22
|
</div>
|
|
23
23
|
<div
|
|
24
|
-
v-if="
|
|
24
|
+
v-if="renderedValidationErrors.length || hasCharacterCount"
|
|
25
25
|
class="asd20-text-input__validation-row"
|
|
26
26
|
>
|
|
27
27
|
<div class="asd20-text-input__validation-errors">
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
/>
|
|
34
34
|
</div>
|
|
35
35
|
<span
|
|
36
|
-
v-if="
|
|
36
|
+
v-if="hasCharacterCount"
|
|
37
37
|
class="asd20-text-input__character-count"
|
|
38
38
|
>{{ characterCountText }}</span>
|
|
39
39
|
</div>
|
|
@@ -158,6 +158,8 @@
|
|
|
158
158
|
:label="callsToAction[0].label"
|
|
159
159
|
:icon="callsToAction[0].icon"
|
|
160
160
|
:link="callsToAction[0].url"
|
|
161
|
+
:tracking-code="callsToAction[0].trackingCode"
|
|
162
|
+
:public-form-id="callsToAction[0].publicFormId"
|
|
161
163
|
reversed
|
|
162
164
|
horizontal
|
|
163
165
|
centered
|
|
@@ -168,6 +170,8 @@
|
|
|
168
170
|
:label="callsToAction[1].label"
|
|
169
171
|
:icon="callsToAction[1].icon"
|
|
170
172
|
:link="callsToAction[1].url"
|
|
173
|
+
:tracking-code="callsToAction[1].trackingCode"
|
|
174
|
+
:public-form-id="callsToAction[1].publicFormId"
|
|
171
175
|
reversed
|
|
172
176
|
horizontal
|
|
173
177
|
centered
|
|
@@ -150,6 +150,7 @@
|
|
|
150
150
|
:icon="callsToAction[0].icon"
|
|
151
151
|
:link="callsToAction[0].url"
|
|
152
152
|
:tracking-code="callsToAction[0].trackingCode"
|
|
153
|
+
:public-form-id="callsToAction[0].publicFormId"
|
|
153
154
|
reversed
|
|
154
155
|
horizontal
|
|
155
156
|
centered
|
|
@@ -162,6 +163,7 @@
|
|
|
162
163
|
:icon="callsToAction[1].icon"
|
|
163
164
|
:link="callsToAction[1].url"
|
|
164
165
|
:tracking-code="callsToAction[1].trackingCode"
|
|
166
|
+
:public-form-id="callsToAction[1].publicFormId"
|
|
165
167
|
reversed
|
|
166
168
|
horizontal
|
|
167
169
|
centered
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<asd20-modal
|
|
3
3
|
:open="open"
|
|
4
4
|
:title="modalTitle"
|
|
5
|
-
icon="
|
|
5
|
+
icon="website"
|
|
6
6
|
dismissable
|
|
7
7
|
class="asd20-public-form-modal"
|
|
8
8
|
@dismiss="dismiss"
|
|
@@ -77,6 +77,8 @@
|
|
|
77
77
|
:input-id="fieldId(field.name)"
|
|
78
78
|
:model-value="values[field.name]"
|
|
79
79
|
:errors="validationErrors[field.name] || []"
|
|
80
|
+
:show-character-count="shouldShowCharacterCount(field)"
|
|
81
|
+
:show-pristine-validation-errors="showPristineValidationErrors"
|
|
80
82
|
@update:modelValue="updateValue(field.name, $event)"
|
|
81
83
|
/>
|
|
82
84
|
|
|
@@ -230,6 +232,9 @@ export default {
|
|
|
230
232
|
modalTitle() {
|
|
231
233
|
return this.formDefinition?.title || 'Public Form'
|
|
232
234
|
},
|
|
235
|
+
showPristineValidationErrors() {
|
|
236
|
+
return this.formDefinition?.id !== 'accessibility-issue'
|
|
237
|
+
},
|
|
233
238
|
errorEntries() {
|
|
234
239
|
if (!this.formDefinition) return []
|
|
235
240
|
return this.formDefinition.fields
|
|
@@ -321,6 +326,9 @@ export default {
|
|
|
321
326
|
fieldId(name) {
|
|
322
327
|
return `${this.instanceId}-${name}`
|
|
323
328
|
},
|
|
329
|
+
shouldShowCharacterCount(field) {
|
|
330
|
+
return this.formDefinition.id !== 'accessibility-issue' || field.name === 'issue'
|
|
331
|
+
},
|
|
324
332
|
updateValue(name, value) {
|
|
325
333
|
this.values[name] = value
|
|
326
334
|
if (this.validationAttempted) this.validate(false)
|
|
@@ -352,7 +360,7 @@ export default {
|
|
|
352
360
|
},
|
|
353
361
|
captchaFailed() {
|
|
354
362
|
this.showFeedback(
|
|
355
|
-
'The verification service could not start. Please try again or use the contact options
|
|
363
|
+
'The verification service could not start. Please try again or use the contact options listed.'
|
|
356
364
|
)
|
|
357
365
|
},
|
|
358
366
|
captchaExpired() {
|
|
@@ -368,15 +376,15 @@ export default {
|
|
|
368
376
|
if (status === 403)
|
|
369
377
|
return 'The verification was not accepted. Please try again.'
|
|
370
378
|
if (status === 429) {
|
|
371
|
-
return 'Too many submissions were received recently. Please wait and try again, or use the contact options
|
|
379
|
+
return 'Too many submissions were received recently. Please wait and try again, or use the contact options listed.'
|
|
372
380
|
}
|
|
373
381
|
if (status === 502) {
|
|
374
|
-
return 'The form could not be delivered right now. Please try again later or use the contact options
|
|
382
|
+
return 'The form could not be delivered right now. Please try again later or use the contact options listed.'
|
|
375
383
|
}
|
|
376
384
|
if (status === 503) {
|
|
377
|
-
return 'This form is temporarily unavailable. Please use the contact options
|
|
385
|
+
return 'This form is temporarily unavailable. Please use the contact options listed.'
|
|
378
386
|
}
|
|
379
|
-
return 'Something went wrong while submitting the form. Please try again later or use the contact options
|
|
387
|
+
return 'Something went wrong while submitting the form. Please try again later or use the contact options listed.'
|
|
380
388
|
},
|
|
381
389
|
showFeedback(message, role = 'alert') {
|
|
382
390
|
this.feedbackMessage = message
|
|
@@ -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
|
-
}
|
|
@@ -25,6 +25,8 @@ export default {
|
|
|
25
25
|
modelValue: { default: '' },
|
|
26
26
|
label: { type: String, default: '' },
|
|
27
27
|
hideLabel: { type: Boolean, default: false },
|
|
28
|
+
showCharacterCount: { type: Boolean, default: true },
|
|
29
|
+
showPristineValidationErrors: { type: Boolean, default: true },
|
|
28
30
|
helpText: { type: String, default: '' },
|
|
29
31
|
items: { type: Array, default: () => [] },
|
|
30
32
|
itemDefault: { type: Object, default: null },
|
|
@@ -101,6 +103,8 @@ export default {
|
|
|
101
103
|
return this.validationErrors.length === 0
|
|
102
104
|
},
|
|
103
105
|
renderedValidationErrors() {
|
|
106
|
+
if (!this.isDirty && !this.showPristineValidationErrors) return []
|
|
107
|
+
|
|
104
108
|
return this.validationErrors.map(error =>
|
|
105
109
|
!this.isDirty && error === REQUIRED_VALIDATION_ERROR
|
|
106
110
|
? 'Required'
|
|
@@ -127,13 +131,16 @@ export default {
|
|
|
127
131
|
},
|
|
128
132
|
|
|
129
133
|
computedHelpText() {
|
|
130
|
-
if (this.
|
|
134
|
+
if (this.hasCharacterCount) {
|
|
131
135
|
return [this.helpText, this.characterCountText].filter(Boolean).join(' ')
|
|
132
136
|
}
|
|
133
137
|
return this.helpText
|
|
134
138
|
},
|
|
139
|
+
hasCharacterCount() {
|
|
140
|
+
return this.showCharacterCount && this.maxLength !== null
|
|
141
|
+
},
|
|
135
142
|
characterCountText() {
|
|
136
|
-
if (this.
|
|
143
|
+
if (!this.hasCharacterCount) return ''
|
|
137
144
|
return `${(this.resolvedValue || '').length}/${this.maxLength}`
|
|
138
145
|
},
|
|
139
146
|
maxLength() {
|
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
|
-
}
|