Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Align behavior with Primer guidance behind validate-after-first-blur toggle #76

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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