Skip to content

Commit

Permalink
wip, but it works!
Browse files Browse the repository at this point in the history
  • Loading branch information
joelhawksley committed Nov 22, 2024
1 parent fde00ab commit f795c58
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 12 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# TODO: Should we switch back out of always-on validation once the input passes validation, or stay in always-on?

# <auto-check> element

An input element that validates its value against a server endpoint.
Expand Down Expand Up @@ -158,6 +160,9 @@ npm install
npm test
```
TODO: Add note about uncommenting line at bottom of examples for local development
Input something other than 422 for error response?
## License
Distributed under the MIT license. See LICENSE for details.
16 changes: 16 additions & 0 deletions custom-elements.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,22 @@
"text": "string"
},
"readonly": true
},
{
"kind": "field",
"name": "validateAfterFirstBlur",
"type": {
"text": "boolean"
},
"readonly": true
},
{
"kind": "field",
"name": "shouldValidate",
"type": {
"text": "boolean"
},
"readonly": true
}
],
"attributes": [
Expand Down
22 changes: 15 additions & 7 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@
<body>
<main>
<h1>auto-check-element</h1>
<h2>Simple form</h2>
<p>Input 422 for an error response.</p>
<h2>old behavior</h2>
<h2 tabindex="-1" id="success1" class="success" hidden>Your submission was successful</h2>
<form>
<p>All fields marked with * are required</p>

<label for="simple-field">Desired username*:</label>
<auto-check csrf="foo" src="/demo" required>
<input id="simple-field" autofocus name="foo" required aria-describedby="state1" />
Expand All @@ -25,7 +22,7 @@ <h2 tabindex="-1" id="success1" class="success" hidden>Your submission was succe
<button value="1" name="form">submit</button>
</form>

<h2>Form that has custom validity messages</h2>
<!-- <h2>Form that has custom validity messages</h2>
<p>Input 422 for an error response.</p>
<h2 tabindex="-1" id="success2" class="success" hidden>Your submission was successful</h2>
<form id="custom">
Expand All @@ -37,6 +34,17 @@ <h2 tabindex="-1" id="success2" class="success" hidden>Your submission was succe
<p id="state2" aria-atomic="true" aria-live="polite" class="state"></p>
</auto-check>
<button value="2" name="form">submit</button>
</form> -->

<h2>validate-after-first-blur</h2>
<h2 tabindex="-1" id="success3" class="success" hidden>Your submission was successful</h2>
<form>
<label for="simple-field2">Desired username*:</label>
<auto-check csrf="foo" src="/demo" required validate-after-first-blur>
<input id="simple-field2" autofocus name="foo" required aria-describedby="state3" />
<p id="state3" aria-atomic="true" aria-live="polite" class="state"></p>
</auto-check>
<button value="3" name="form">submit</button>
</form>
</main>

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

<script type="module" src="https://unpkg.com/@github/auto-check-element@latest"></script>
<!-- <script type="module" src="../dist/index.js" defer></script> -->
<!-- <script type="module" src="https://unpkg.com/@github/auto-check-element@latest"></script> -->
<script type="module" src="../dist/bundle.js" defer></script>
</body>
</html>
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 38 additions & 2 deletions src/auto-check-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,18 @@ export class AutoCheckElement extends HTMLElement {
const input = this.input
if (!input) return

if (!this.validateAfterFirstBlur) {
this.setAttribute('should-validate', '')
}

const checker = debounce(check.bind(null, this), 300)
const state = {check: checker, controller: null}
states.set(this, state)

input.addEventListener('input', setLoadingState)
input.addEventListener('input', checker)
const changeHandler = handleChange.bind(null, checker)

input.addEventListener('blur', changeHandler)
input.addEventListener('input', changeHandler)
input.autocomplete = 'off'
input.spellcheck = false
}
Expand Down Expand Up @@ -185,6 +191,36 @@ export class AutoCheckElement extends HTMLElement {
get httpMethod(): string {
return AllowedHttpMethods[this.getAttribute('http-method') as keyof typeof AllowedHttpMethods] || 'POST'
}

get validateAfterFirstBlur(): boolean {
const value = this.getAttribute('validate-after-first-blur')
return value === 'true' || value === ''
}

get shouldValidate(): boolean {
const value = this.getAttribute('should-validate')
return value === 'true' || value === ''
}
}

function handleChange(checker: () => void, event: Event) {
const input = event.currentTarget
if (!(input instanceof HTMLInputElement)) return

const autoCheckElement = input.closest('auto-check')
if (!(autoCheckElement instanceof AutoCheckElement)) return

if (event.type === 'blur') {
if (autoCheckElement.validateAfterFirstBlur && !autoCheckElement.shouldValidate) {
autoCheckElement.setAttribute('should-validate', '')

checker()
setLoadingState(event)
}
} else if (autoCheckElement.shouldValidate) {
checker()
setLoadingState(event)
}
}

function setLoadingState(event: Event) {
Expand Down
30 changes: 30 additions & 0 deletions test/auto-check.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,36 @@ describe('auto-check element', function () {
})
})

describe('when validate-after-first-blur is true', function () {
let checker
let input

beforeEach(function () {
const container = document.createElement('div')
container.innerHTML = `
<auto-check csrf="foo" src="/success" validate-after-first-blur>
<input>
</auto-check>`
document.body.append(container)

checker = document.querySelector('auto-check')
input = checker.querySelector('input')
})

it('does not emit on initial input change', async function () {
const events = []
input.addEventListener('auto-check-start', event => events.push(event.type))
triggerInput(input, 'hub')
assert.deepEqual(events, [])
})

afterEach(function () {
document.body.innerHTML = ''
checker = null
input = null
})
})

describe('required attribute', function () {
let checker
let input
Expand Down

0 comments on commit f795c58

Please sign in to comment.