Skip to content

Commit f795c58

Browse files
committed
wip, but it works!
1 parent fde00ab commit f795c58

6 files changed

+107
-12
lines changed

README.md

+5
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# TODO: Should we switch back out of always-on validation once the input passes validation, or stay in always-on?
2+
13
# <auto-check> element
24

35
An input element that validates its value against a server endpoint.
@@ -158,6 +160,9 @@ npm install
158160
npm test
159161
```
160162
163+
TODO: Add note about uncommenting line at bottom of examples for local development
164+
Input something other than 422 for error response?
165+
161166
## License
162167
163168
Distributed under the MIT license. See LICENSE for details.

custom-elements.json

+16
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,22 @@
198198
"text": "string"
199199
},
200200
"readonly": true
201+
},
202+
{
203+
"kind": "field",
204+
"name": "validateAfterFirstBlur",
205+
"type": {
206+
"text": "boolean"
207+
},
208+
"readonly": true
209+
},
210+
{
211+
"kind": "field",
212+
"name": "shouldValidate",
213+
"type": {
214+
"text": "boolean"
215+
},
216+
"readonly": true
201217
}
202218
],
203219
"attributes": [

examples/index.html

+15-7
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111
<body>
1212
<main>
1313
<h1>auto-check-element</h1>
14-
<h2>Simple form</h2>
15-
<p>Input 422 for an error response.</p>
14+
<h2>old behavior</h2>
1615
<h2 tabindex="-1" id="success1" class="success" hidden>Your submission was successful</h2>
1716
<form>
18-
<p>All fields marked with * are required</p>
19-
2017
<label for="simple-field">Desired username*:</label>
2118
<auto-check csrf="foo" src="/demo" required>
2219
<input id="simple-field" autofocus name="foo" required aria-describedby="state1" />
@@ -25,7 +22,7 @@ <h2 tabindex="-1" id="success1" class="success" hidden>Your submission was succe
2522
<button value="1" name="form">submit</button>
2623
</form>
2724

28-
<h2>Form that has custom validity messages</h2>
25+
<!-- <h2>Form that has custom validity messages</h2>
2926
<p>Input 422 for an error response.</p>
3027
<h2 tabindex="-1" id="success2" class="success" hidden>Your submission was successful</h2>
3128
<form id="custom">
@@ -37,6 +34,17 @@ <h2 tabindex="-1" id="success2" class="success" hidden>Your submission was succe
3734
<p id="state2" aria-atomic="true" aria-live="polite" class="state"></p>
3835
</auto-check>
3936
<button value="2" name="form">submit</button>
37+
</form> -->
38+
39+
<h2>validate-after-first-blur</h2>
40+
<h2 tabindex="-1" id="success3" class="success" hidden>Your submission was successful</h2>
41+
<form>
42+
<label for="simple-field2">Desired username*:</label>
43+
<auto-check csrf="foo" src="/demo" required validate-after-first-blur>
44+
<input id="simple-field2" autofocus name="foo" required aria-describedby="state3" />
45+
<p id="state3" aria-atomic="true" aria-live="polite" class="state"></p>
46+
</auto-check>
47+
<button value="3" name="form">submit</button>
4048
</form>
4149
</main>
4250

@@ -96,7 +104,7 @@ <h2 tabindex="-1" id="success2" class="success" hidden>Your submission was succe
96104
}
97105
</script>
98106

99-
<script type="module" src="https://unpkg.com/@github/auto-check-element@latest"></script>
100-
<!-- <script type="module" src="../dist/index.js" defer></script> -->
107+
<!-- <script type="module" src="https://unpkg.com/@github/auto-check-element@latest"></script> -->
108+
<script type="module" src="../dist/bundle.js" defer></script>
101109
</body>
102110
</html>

package-lock.json

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/auto-check-element.ts

+38-2
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,18 @@ export class AutoCheckElement extends HTMLElement {
101101
const input = this.input
102102
if (!input) return
103103

104+
if (!this.validateAfterFirstBlur) {
105+
this.setAttribute('should-validate', '')
106+
}
107+
104108
const checker = debounce(check.bind(null, this), 300)
105109
const state = {check: checker, controller: null}
106110
states.set(this, state)
107111

108-
input.addEventListener('input', setLoadingState)
109-
input.addEventListener('input', checker)
112+
const changeHandler = handleChange.bind(null, checker)
113+
114+
input.addEventListener('blur', changeHandler)
115+
input.addEventListener('input', changeHandler)
110116
input.autocomplete = 'off'
111117
input.spellcheck = false
112118
}
@@ -185,6 +191,36 @@ export class AutoCheckElement extends HTMLElement {
185191
get httpMethod(): string {
186192
return AllowedHttpMethods[this.getAttribute('http-method') as keyof typeof AllowedHttpMethods] || 'POST'
187193
}
194+
195+
get validateAfterFirstBlur(): boolean {
196+
const value = this.getAttribute('validate-after-first-blur')
197+
return value === 'true' || value === ''
198+
}
199+
200+
get shouldValidate(): boolean {
201+
const value = this.getAttribute('should-validate')
202+
return value === 'true' || value === ''
203+
}
204+
}
205+
206+
function handleChange(checker: () => void, event: Event) {
207+
const input = event.currentTarget
208+
if (!(input instanceof HTMLInputElement)) return
209+
210+
const autoCheckElement = input.closest('auto-check')
211+
if (!(autoCheckElement instanceof AutoCheckElement)) return
212+
213+
if (event.type === 'blur') {
214+
if (autoCheckElement.validateAfterFirstBlur && !autoCheckElement.shouldValidate) {
215+
autoCheckElement.setAttribute('should-validate', '')
216+
217+
checker()
218+
setLoadingState(event)
219+
}
220+
} else if (autoCheckElement.shouldValidate) {
221+
checker()
222+
setLoadingState(event)
223+
}
188224
}
189225

190226
function setLoadingState(event: Event) {

test/auto-check.js

+30
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,36 @@ describe('auto-check element', function () {
2222
})
2323
})
2424

25+
describe('when validate-after-first-blur is true', function () {
26+
let checker
27+
let input
28+
29+
beforeEach(function () {
30+
const container = document.createElement('div')
31+
container.innerHTML = `
32+
<auto-check csrf="foo" src="/success" validate-after-first-blur>
33+
<input>
34+
</auto-check>`
35+
document.body.append(container)
36+
37+
checker = document.querySelector('auto-check')
38+
input = checker.querySelector('input')
39+
})
40+
41+
it('does not emit on initial input change', async function () {
42+
const events = []
43+
input.addEventListener('auto-check-start', event => events.push(event.type))
44+
triggerInput(input, 'hub')
45+
assert.deepEqual(events, [])
46+
})
47+
48+
afterEach(function () {
49+
document.body.innerHTML = ''
50+
checker = null
51+
input = null
52+
})
53+
})
54+
2555
describe('required attribute', function () {
2656
let checker
2757
let input

0 commit comments

Comments
 (0)