@asd20/ui-next 2.24.3 → 2.25.0
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/components.js +0 -1
- package/helpers.js +0 -1
- package/package.json +1 -1
- package/src/components/molecules/Asd20PublicFormField/index.vue +19 -0
- package/src/components/organisms/Asd20PublicFormModal/index.vue +4 -1
- package/src/helpers/injectedContentInteractions.js +0 -26
- package/src/helpers/publicFormDefinition.js +34 -1
- 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,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
# [2.25.0](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.24.4...ui-next-v2.25.0) (2026-09-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* support public form date limits and fallback guidance ([4ef8551](https://github.com/academydistrict20/asd20-ui-next/commit/4ef8551aaa98cf66f48395a9bf98d68f107a0486))
|
|
9
|
+
|
|
10
|
+
## [2.24.4](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.24.3...ui-next-v2.24.4) (2026-09-17)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* remove legacy accessibility form templates and components ([3e9fa07](https://github.com/academydistrict20/asd20-ui-next/commit/3e9fa0749a9000f9c8970bbb8d1a81ed665a94e8))
|
|
16
|
+
|
|
3
17
|
## [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
18
|
|
|
5
19
|
|
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
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
:required="field.required"
|
|
26
26
|
:minlength="field.minLength"
|
|
27
27
|
:maxlength="field.maxLength"
|
|
28
|
+
:max="dateMaximum"
|
|
28
29
|
:show-character-count="showCharacterCount"
|
|
29
30
|
:show-pristine-validation-errors="showPristineValidationErrors"
|
|
30
31
|
:autocomplete="field.autocomplete || undefined"
|
|
@@ -123,6 +124,19 @@ import Asd20Checkbox from '../../atoms/Asd20Checkbox'
|
|
|
123
124
|
import Asd20TextAreaInput from '../Asd20TextAreaInput'
|
|
124
125
|
import Asd20TextInput from '../Asd20TextInput'
|
|
125
126
|
|
|
127
|
+
function districtToday() {
|
|
128
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
129
|
+
timeZone: 'America/Denver',
|
|
130
|
+
year: 'numeric',
|
|
131
|
+
month: '2-digit',
|
|
132
|
+
day: '2-digit',
|
|
133
|
+
}).formatToParts(new Date())
|
|
134
|
+
const values = Object.fromEntries(
|
|
135
|
+
parts.map(({ type, value }) => [type, value])
|
|
136
|
+
)
|
|
137
|
+
return `${values.year}-${values.month}-${values.day}`
|
|
138
|
+
}
|
|
139
|
+
|
|
126
140
|
export default {
|
|
127
141
|
name: 'Asd20PublicFormField',
|
|
128
142
|
components: { Asd20Checkbox, Asd20TextAreaInput, Asd20TextInput },
|
|
@@ -141,6 +155,11 @@ export default {
|
|
|
141
155
|
}
|
|
142
156
|
},
|
|
143
157
|
computed: {
|
|
158
|
+
dateMaximum() {
|
|
159
|
+
return this.field.type === 'date' && this.field.maxDate === 'today'
|
|
160
|
+
? districtToday()
|
|
161
|
+
: undefined
|
|
162
|
+
},
|
|
144
163
|
displayLabel() {
|
|
145
164
|
return this.field.required
|
|
146
165
|
? `${this.field.label} (required)`
|
|
@@ -102,7 +102,10 @@
|
|
|
102
102
|
class="asd20-public-form-modal__fallback"
|
|
103
103
|
>
|
|
104
104
|
<p>
|
|
105
|
-
|
|
105
|
+
{{
|
|
106
|
+
formDefinition.fallbackContact.instructions ||
|
|
107
|
+
'If you cannot submit this form, use one of these contact options:'
|
|
108
|
+
}}
|
|
106
109
|
</p>
|
|
107
110
|
<a
|
|
108
111
|
v-if="formDefinition.fallbackContact.email"
|
|
@@ -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
|
-
}
|
|
@@ -34,6 +34,12 @@ function optionalLength(value, label) {
|
|
|
34
34
|
return value
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function optionalMaxDate(value, type) {
|
|
38
|
+
if (value == null) return undefined
|
|
39
|
+
if (type === 'date' && value === 'today') return 'today'
|
|
40
|
+
throw new Error('Maximum date is invalid.')
|
|
41
|
+
}
|
|
42
|
+
|
|
37
43
|
function normalizeOptions(field) {
|
|
38
44
|
if (!['select', 'radio'].includes(field.type)) return undefined
|
|
39
45
|
if (
|
|
@@ -81,6 +87,7 @@ function normalizeField(field) {
|
|
|
81
87
|
required: field.required === true,
|
|
82
88
|
minLength: optionalLength(field.minLength, 'Minimum length'),
|
|
83
89
|
maxLength: optionalLength(field.maxLength, 'Maximum length'),
|
|
90
|
+
maxDate: optionalMaxDate(field.maxDate, type),
|
|
84
91
|
autocomplete: boundedText(field.autocomplete, 'Autocomplete value', 100),
|
|
85
92
|
options: undefined,
|
|
86
93
|
}
|
|
@@ -134,9 +141,14 @@ export function normalizePublicFormDefinition(definition) {
|
|
|
134
141
|
}
|
|
135
142
|
const email = boundedText(fallbackContact.email, 'Fallback email', 320)
|
|
136
143
|
const phone = boundedText(fallbackContact.phone, 'Fallback phone', 50)
|
|
144
|
+
const instructions = boundedText(
|
|
145
|
+
fallbackContact.instructions,
|
|
146
|
+
'Fallback instructions',
|
|
147
|
+
1000
|
|
148
|
+
)
|
|
137
149
|
if (email && !EMAIL_PATTERN.test(email))
|
|
138
150
|
throw new Error('The public form fallback email is invalid.')
|
|
139
|
-
normalizedFallback = { email, phone }
|
|
151
|
+
normalizedFallback = { email, phone, instructions }
|
|
140
152
|
}
|
|
141
153
|
|
|
142
154
|
const id = boundedText(definition.id, 'Form ID', 100, true)
|
|
@@ -183,6 +195,19 @@ function validDate(value) {
|
|
|
183
195
|
)
|
|
184
196
|
}
|
|
185
197
|
|
|
198
|
+
function districtToday() {
|
|
199
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
|
200
|
+
timeZone: 'America/Denver',
|
|
201
|
+
year: 'numeric',
|
|
202
|
+
month: '2-digit',
|
|
203
|
+
day: '2-digit',
|
|
204
|
+
}).formatToParts(new Date())
|
|
205
|
+
const values = Object.fromEntries(
|
|
206
|
+
parts.map(({ type, value }) => [type, value])
|
|
207
|
+
)
|
|
208
|
+
return `${values.year}-${values.month}-${values.day}`
|
|
209
|
+
}
|
|
210
|
+
|
|
186
211
|
export function validatePublicFormValues(definition, sourceValues = {}) {
|
|
187
212
|
const errors = {}
|
|
188
213
|
const values = {}
|
|
@@ -220,6 +245,14 @@ export function validatePublicFormValues(definition, sourceValues = {}) {
|
|
|
220
245
|
if (value && field.type === 'date' && !validDate(value)) {
|
|
221
246
|
fieldErrors.push(`Enter a valid ${field.label.toLowerCase()}.`)
|
|
222
247
|
}
|
|
248
|
+
if (
|
|
249
|
+
value &&
|
|
250
|
+
field.type === 'date' &&
|
|
251
|
+
field.maxDate === 'today' &&
|
|
252
|
+
value > districtToday()
|
|
253
|
+
) {
|
|
254
|
+
fieldErrors.push(`${field.label} cannot be in the future.`)
|
|
255
|
+
}
|
|
223
256
|
if (value && ['select', 'radio'].includes(field.type)) {
|
|
224
257
|
const allowedValues = new Set(
|
|
225
258
|
(field.options || []).map(option => option.value)
|
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
|
-
}
|