@github/auto-complete-element
Advanced tools
Comparing version 3.0.2 to 3.1.0
export default class AutocompleteElement extends HTMLElement { | ||
constructor(); | ||
connectedCallback(): void; | ||
@@ -14,1 +13,9 @@ disconnectedCallback(): void; | ||
} | ||
declare global { | ||
interface Window { | ||
AutocompleteElement: typeof AutocompleteElement; | ||
} | ||
interface HTMLElementTagNameMap { | ||
'auto-complete': AutocompleteElement; | ||
} | ||
} |
@@ -5,5 +5,2 @@ import AutocompleteEvent from './auto-complete-event'; | ||
export default class AutocompleteElement extends HTMLElement { | ||
constructor() { | ||
super(); | ||
} | ||
connectedCallback() { | ||
@@ -17,3 +14,4 @@ const listId = this.getAttribute('for'); | ||
return; | ||
state.set(this, new Autocomplete(this, input, results)); | ||
const autoselectEnabled = this.getAttribute('data-autoselect') === 'true'; | ||
state.set(this, new Autocomplete(this, input, results, autoselectEnabled)); | ||
results.setAttribute('role', 'listbox'); | ||
@@ -76,1 +74,5 @@ } | ||
} | ||
if (!window.customElements.get('auto-complete')) { | ||
window.AutocompleteElement = AutocompleteElement; | ||
window.customElements.define('auto-complete', AutocompleteElement); | ||
} |
@@ -8,12 +8,18 @@ import type AutocompleteElement from './auto-complete-element'; | ||
combobox: Combobox; | ||
feedback: HTMLElement | null; | ||
autoselectEnabled: boolean; | ||
clientOptions: NodeListOf<HTMLElement> | null; | ||
clearButton: HTMLElement | null; | ||
interactingWithList: boolean; | ||
constructor(container: AutocompleteElement, input: HTMLInputElement, results: HTMLElement); | ||
constructor(container: AutocompleteElement, input: HTMLInputElement, results: HTMLElement, autoselectEnabled?: boolean); | ||
destroy(): void; | ||
handleClear(event: Event): void; | ||
onKeydown(event: KeyboardEvent): void; | ||
onInputFocus(): void; | ||
onInputBlur(): void; | ||
onCommit({ target }: Event): void; | ||
onCommit({ target }: Pick<Event, 'target'>): void; | ||
onResultsMouseDown(): void; | ||
onInputChange(): void; | ||
identifyOptions(): void; | ||
updateFeedbackForScreenReaders(inputString: string): void; | ||
fetchResults(): void; | ||
@@ -20,0 +26,0 @@ open(): void; |
import debounce from './debounce'; | ||
import { fragment } from './send'; | ||
import Combobox from '@github/combobox-nav'; | ||
const SCREEN_READER_DELAY = window.testScreenReaderDelay || 100; | ||
export default class Autocomplete { | ||
constructor(container, input, results) { | ||
constructor(container, input, results, autoselectEnabled = false) { | ||
var _a; | ||
this.container = container; | ||
@@ -10,3 +12,20 @@ this.input = input; | ||
this.combobox = new Combobox(input, results); | ||
this.feedback = document.getElementById(`${this.results.id}-feedback`); | ||
this.autoselectEnabled = autoselectEnabled; | ||
this.clearButton = document.getElementById(`${this.input.id || this.input.name}-clear`); | ||
this.clientOptions = results.querySelectorAll('[role=option]'); | ||
if (this.feedback) { | ||
this.feedback.setAttribute('aria-live', 'polite'); | ||
this.feedback.setAttribute('aria-atomic', 'true'); | ||
} | ||
if (this.clearButton && !this.clearButton.getAttribute('aria-label')) { | ||
const labelElem = document.querySelector(`label[for="${this.input.name}"]`); | ||
this.clearButton.setAttribute('aria-label', `clear:`); | ||
this.clearButton.setAttribute('aria-labelledby', `${this.clearButton.id} ${(labelElem === null || labelElem === void 0 ? void 0 : labelElem.id) || ''}`); | ||
} | ||
if (!this.input.getAttribute('aria-expanded')) { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
} | ||
this.results.hidden = true; | ||
this.results.setAttribute('aria-label', 'results'); | ||
this.input.setAttribute('autocomplete', 'off'); | ||
@@ -21,2 +40,3 @@ this.input.setAttribute('spellcheck', 'false'); | ||
this.onCommit = this.onCommit.bind(this); | ||
this.handleClear = this.handleClear.bind(this); | ||
this.input.addEventListener('keydown', this.onKeydown); | ||
@@ -28,2 +48,3 @@ this.input.addEventListener('focus', this.onInputFocus); | ||
this.results.addEventListener('combobox-commit', this.onCommit); | ||
(_a = this.clearButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', this.handleClear); | ||
} | ||
@@ -38,3 +59,23 @@ destroy() { | ||
} | ||
handleClear(event) { | ||
event.preventDefault(); | ||
if (this.input.getAttribute('aria-expanded') === 'true') { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
this.updateFeedbackForScreenReaders('Results hidden.'); | ||
} | ||
this.input.value = ''; | ||
this.container.value = ''; | ||
this.input.focus(); | ||
this.input.dispatchEvent(new Event('change')); | ||
this.container.open = false; | ||
} | ||
onKeydown(event) { | ||
if (event.key === 'Enter' && this.container.open && this.autoselectEnabled) { | ||
const firstOption = this.results.children[0]; | ||
if (firstOption) { | ||
event.stopPropagation(); | ||
event.preventDefault(); | ||
this.onCommit({ target: firstOption }); | ||
} | ||
} | ||
if (event.key === 'Escape' && this.container.open) { | ||
@@ -76,3 +117,7 @@ this.container.open = false; | ||
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent; | ||
this.updateFeedbackForScreenReaders(`${selected.textContent || ''} selected.`); | ||
this.container.value = value; | ||
if (!value) { | ||
this.updateFeedbackForScreenReaders(`Results hidden.`); | ||
} | ||
} | ||
@@ -83,2 +128,5 @@ onResultsMouseDown() { | ||
onInputChange() { | ||
if (this.feedback && this.feedback.innerHTML) { | ||
this.feedback.innerHTML = ''; | ||
} | ||
this.container.removeAttribute('value'); | ||
@@ -93,2 +141,9 @@ this.fetchResults(); | ||
} | ||
updateFeedbackForScreenReaders(inputString) { | ||
setTimeout(() => { | ||
if (this.feedback) { | ||
this.feedback.innerHTML = inputString; | ||
} | ||
}, SCREEN_READER_DELAY); | ||
} | ||
fetchResults() { | ||
@@ -112,3 +167,13 @@ const query = this.input.value.trim(); | ||
this.identifyOptions(); | ||
const hasResults = !!this.results.querySelector('[role="option"]'); | ||
const allNewOptions = this.results.querySelectorAll('[role="option"]'); | ||
const hasResults = !!allNewOptions.length; | ||
const numOptions = allNewOptions.length; | ||
const [firstOption] = allNewOptions; | ||
const firstOptionValue = firstOption === null || firstOption === void 0 ? void 0 : firstOption.textContent; | ||
if (this.autoselectEnabled && firstOptionValue) { | ||
this.updateFeedbackForScreenReaders(`${numOptions} results. ${firstOptionValue} is the top result: Press Enter to activate.`); | ||
} | ||
else { | ||
this.updateFeedbackForScreenReaders(`${numOptions || 'No'} results.`); | ||
} | ||
this.container.open = hasResults; | ||
@@ -115,0 +180,0 @@ this.container.dispatchEvent(new CustomEvent('load')); |
@@ -223,4 +223,6 @@ const ctrlBindings = !!navigator.userAgent.match(/Macintosh/); | ||
const SCREEN_READER_DELAY = window.testScreenReaderDelay || 100; | ||
class Autocomplete { | ||
constructor(container, input, results) { | ||
constructor(container, input, results, autoselectEnabled = false) { | ||
var _a; | ||
this.container = container; | ||
@@ -230,3 +232,20 @@ this.input = input; | ||
this.combobox = new Combobox(input, results); | ||
this.feedback = document.getElementById(`${this.results.id}-feedback`); | ||
this.autoselectEnabled = autoselectEnabled; | ||
this.clearButton = document.getElementById(`${this.input.id || this.input.name}-clear`); | ||
this.clientOptions = results.querySelectorAll('[role=option]'); | ||
if (this.feedback) { | ||
this.feedback.setAttribute('aria-live', 'polite'); | ||
this.feedback.setAttribute('aria-atomic', 'true'); | ||
} | ||
if (this.clearButton && !this.clearButton.getAttribute('aria-label')) { | ||
const labelElem = document.querySelector(`label[for="${this.input.name}"]`); | ||
this.clearButton.setAttribute('aria-label', `clear:`); | ||
this.clearButton.setAttribute('aria-labelledby', `${this.clearButton.id} ${(labelElem === null || labelElem === void 0 ? void 0 : labelElem.id) || ''}`); | ||
} | ||
if (!this.input.getAttribute('aria-expanded')) { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
} | ||
this.results.hidden = true; | ||
this.results.setAttribute('aria-label', 'results'); | ||
this.input.setAttribute('autocomplete', 'off'); | ||
@@ -241,2 +260,3 @@ this.input.setAttribute('spellcheck', 'false'); | ||
this.onCommit = this.onCommit.bind(this); | ||
this.handleClear = this.handleClear.bind(this); | ||
this.input.addEventListener('keydown', this.onKeydown); | ||
@@ -248,2 +268,3 @@ this.input.addEventListener('focus', this.onInputFocus); | ||
this.results.addEventListener('combobox-commit', this.onCommit); | ||
(_a = this.clearButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', this.handleClear); | ||
} | ||
@@ -258,3 +279,23 @@ destroy() { | ||
} | ||
handleClear(event) { | ||
event.preventDefault(); | ||
if (this.input.getAttribute('aria-expanded') === 'true') { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
this.updateFeedbackForScreenReaders('Results hidden.'); | ||
} | ||
this.input.value = ''; | ||
this.container.value = ''; | ||
this.input.focus(); | ||
this.input.dispatchEvent(new Event('change')); | ||
this.container.open = false; | ||
} | ||
onKeydown(event) { | ||
if (event.key === 'Enter' && this.container.open && this.autoselectEnabled) { | ||
const firstOption = this.results.children[0]; | ||
if (firstOption) { | ||
event.stopPropagation(); | ||
event.preventDefault(); | ||
this.onCommit({ target: firstOption }); | ||
} | ||
} | ||
if (event.key === 'Escape' && this.container.open) { | ||
@@ -296,3 +337,7 @@ this.container.open = false; | ||
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent; | ||
this.updateFeedbackForScreenReaders(`${selected.textContent || ''} selected.`); | ||
this.container.value = value; | ||
if (!value) { | ||
this.updateFeedbackForScreenReaders(`Results hidden.`); | ||
} | ||
} | ||
@@ -303,2 +348,5 @@ onResultsMouseDown() { | ||
onInputChange() { | ||
if (this.feedback && this.feedback.innerHTML) { | ||
this.feedback.innerHTML = ''; | ||
} | ||
this.container.removeAttribute('value'); | ||
@@ -313,2 +361,9 @@ this.fetchResults(); | ||
} | ||
updateFeedbackForScreenReaders(inputString) { | ||
setTimeout(() => { | ||
if (this.feedback) { | ||
this.feedback.innerHTML = inputString; | ||
} | ||
}, SCREEN_READER_DELAY); | ||
} | ||
fetchResults() { | ||
@@ -332,3 +387,13 @@ const query = this.input.value.trim(); | ||
this.identifyOptions(); | ||
const hasResults = !!this.results.querySelector('[role="option"]'); | ||
const allNewOptions = this.results.querySelectorAll('[role="option"]'); | ||
const hasResults = !!allNewOptions.length; | ||
const numOptions = allNewOptions.length; | ||
const [firstOption] = allNewOptions; | ||
const firstOptionValue = firstOption === null || firstOption === void 0 ? void 0 : firstOption.textContent; | ||
if (this.autoselectEnabled && firstOptionValue) { | ||
this.updateFeedbackForScreenReaders(`${numOptions} results. ${firstOptionValue} is the top result: Press Enter to activate.`); | ||
} | ||
else { | ||
this.updateFeedbackForScreenReaders(`${numOptions || 'No'} results.`); | ||
} | ||
this.container.open = hasResults; | ||
@@ -359,5 +424,2 @@ this.container.dispatchEvent(new CustomEvent('load')); | ||
class AutocompleteElement extends HTMLElement { | ||
constructor() { | ||
super(); | ||
} | ||
connectedCallback() { | ||
@@ -371,3 +433,4 @@ const listId = this.getAttribute('for'); | ||
return; | ||
state.set(this, new Autocomplete(this, input, results)); | ||
const autoselectEnabled = this.getAttribute('data-autoselect') === 'true'; | ||
state.set(this, new Autocomplete(this, input, results, autoselectEnabled)); | ||
results.setAttribute('role', 'listbox'); | ||
@@ -430,3 +493,2 @@ } | ||
} | ||
if (!window.customElements.get('auto-complete')) { | ||
@@ -433,0 +495,0 @@ window.AutocompleteElement = AutocompleteElement; |
import AutocompleteElement from './auto-complete-element'; | ||
export { AutocompleteElement as default }; | ||
export { default as AutocompleteEvent } from './auto-complete-event'; | ||
declare global { | ||
interface Window { | ||
AutocompleteElement: typeof AutocompleteElement; | ||
} | ||
interface HTMLElementTagNameMap { | ||
'auto-complete': AutocompleteElement; | ||
} | ||
} |
@@ -53,4 +53,6 @@ import Combobox from '@github/combobox-nav'; | ||
const SCREEN_READER_DELAY = window.testScreenReaderDelay || 100; | ||
class Autocomplete { | ||
constructor(container, input, results) { | ||
constructor(container, input, results, autoselectEnabled = false) { | ||
var _a; | ||
this.container = container; | ||
@@ -60,3 +62,20 @@ this.input = input; | ||
this.combobox = new Combobox(input, results); | ||
this.feedback = document.getElementById(`${this.results.id}-feedback`); | ||
this.autoselectEnabled = autoselectEnabled; | ||
this.clearButton = document.getElementById(`${this.input.id || this.input.name}-clear`); | ||
this.clientOptions = results.querySelectorAll('[role=option]'); | ||
if (this.feedback) { | ||
this.feedback.setAttribute('aria-live', 'polite'); | ||
this.feedback.setAttribute('aria-atomic', 'true'); | ||
} | ||
if (this.clearButton && !this.clearButton.getAttribute('aria-label')) { | ||
const labelElem = document.querySelector(`label[for="${this.input.name}"]`); | ||
this.clearButton.setAttribute('aria-label', `clear:`); | ||
this.clearButton.setAttribute('aria-labelledby', `${this.clearButton.id} ${(labelElem === null || labelElem === void 0 ? void 0 : labelElem.id) || ''}`); | ||
} | ||
if (!this.input.getAttribute('aria-expanded')) { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
} | ||
this.results.hidden = true; | ||
this.results.setAttribute('aria-label', 'results'); | ||
this.input.setAttribute('autocomplete', 'off'); | ||
@@ -71,2 +90,3 @@ this.input.setAttribute('spellcheck', 'false'); | ||
this.onCommit = this.onCommit.bind(this); | ||
this.handleClear = this.handleClear.bind(this); | ||
this.input.addEventListener('keydown', this.onKeydown); | ||
@@ -78,2 +98,3 @@ this.input.addEventListener('focus', this.onInputFocus); | ||
this.results.addEventListener('combobox-commit', this.onCommit); | ||
(_a = this.clearButton) === null || _a === void 0 ? void 0 : _a.addEventListener('click', this.handleClear); | ||
} | ||
@@ -88,3 +109,23 @@ destroy() { | ||
} | ||
handleClear(event) { | ||
event.preventDefault(); | ||
if (this.input.getAttribute('aria-expanded') === 'true') { | ||
this.input.setAttribute('aria-expanded', 'false'); | ||
this.updateFeedbackForScreenReaders('Results hidden.'); | ||
} | ||
this.input.value = ''; | ||
this.container.value = ''; | ||
this.input.focus(); | ||
this.input.dispatchEvent(new Event('change')); | ||
this.container.open = false; | ||
} | ||
onKeydown(event) { | ||
if (event.key === 'Enter' && this.container.open && this.autoselectEnabled) { | ||
const firstOption = this.results.children[0]; | ||
if (firstOption) { | ||
event.stopPropagation(); | ||
event.preventDefault(); | ||
this.onCommit({ target: firstOption }); | ||
} | ||
} | ||
if (event.key === 'Escape' && this.container.open) { | ||
@@ -126,3 +167,7 @@ this.container.open = false; | ||
const value = selected.getAttribute('data-autocomplete-value') || selected.textContent; | ||
this.updateFeedbackForScreenReaders(`${selected.textContent || ''} selected.`); | ||
this.container.value = value; | ||
if (!value) { | ||
this.updateFeedbackForScreenReaders(`Results hidden.`); | ||
} | ||
} | ||
@@ -133,2 +178,5 @@ onResultsMouseDown() { | ||
onInputChange() { | ||
if (this.feedback && this.feedback.innerHTML) { | ||
this.feedback.innerHTML = ''; | ||
} | ||
this.container.removeAttribute('value'); | ||
@@ -143,2 +191,9 @@ this.fetchResults(); | ||
} | ||
updateFeedbackForScreenReaders(inputString) { | ||
setTimeout(() => { | ||
if (this.feedback) { | ||
this.feedback.innerHTML = inputString; | ||
} | ||
}, SCREEN_READER_DELAY); | ||
} | ||
fetchResults() { | ||
@@ -162,3 +217,13 @@ const query = this.input.value.trim(); | ||
this.identifyOptions(); | ||
const hasResults = !!this.results.querySelector('[role="option"]'); | ||
const allNewOptions = this.results.querySelectorAll('[role="option"]'); | ||
const hasResults = !!allNewOptions.length; | ||
const numOptions = allNewOptions.length; | ||
const [firstOption] = allNewOptions; | ||
const firstOptionValue = firstOption === null || firstOption === void 0 ? void 0 : firstOption.textContent; | ||
if (this.autoselectEnabled && firstOptionValue) { | ||
this.updateFeedbackForScreenReaders(`${numOptions} results. ${firstOptionValue} is the top result: Press Enter to activate.`); | ||
} | ||
else { | ||
this.updateFeedbackForScreenReaders(`${numOptions || 'No'} results.`); | ||
} | ||
this.container.open = hasResults; | ||
@@ -189,5 +254,2 @@ this.container.dispatchEvent(new CustomEvent('load')); | ||
class AutocompleteElement extends HTMLElement { | ||
constructor() { | ||
super(); | ||
} | ||
connectedCallback() { | ||
@@ -201,3 +263,4 @@ const listId = this.getAttribute('for'); | ||
return; | ||
state.set(this, new Autocomplete(this, input, results)); | ||
const autoselectEnabled = this.getAttribute('data-autoselect') === 'true'; | ||
state.set(this, new Autocomplete(this, input, results, autoselectEnabled)); | ||
results.setAttribute('role', 'listbox'); | ||
@@ -260,3 +323,2 @@ } | ||
} | ||
if (!window.customElements.get('auto-complete')) { | ||
@@ -263,0 +325,0 @@ window.AutocompleteElement = AutocompleteElement; |
{ | ||
"name": "@github/auto-complete-element", | ||
"version": "3.0.2", | ||
"version": "3.1.0", | ||
"description": "Auto-complete input values from server results", | ||
@@ -12,2 +12,4 @@ "repository": "github/auto-complete-element", | ||
"clean": "rm -rf dist", | ||
"example": "http-server -c-1 ./examples", | ||
"preexample": "mkdir -p ./examples/dist && npm run build && cp ./dist/bundle.js ./examples/dist/bundle.js", | ||
"lint": "eslint . --ext .ts,.js && tsc --noEmit", | ||
@@ -17,3 +19,3 @@ "prebuild": "npm run clean && npm run lint", | ||
"pretest": "npm run build", | ||
"test": "karma start test/karma.config.cjs", | ||
"test": "karma start karma.config.cjs", | ||
"prepublishOnly": "npm run build", | ||
@@ -28,3 +30,4 @@ "postpublish": "npm publish --ignore-scripts --@github:registry='https://npm.pkg.github.com'" | ||
"files": [ | ||
"dist" | ||
"dist", | ||
"validator.js" | ||
], | ||
@@ -36,6 +39,10 @@ "dependencies": { | ||
"@github/prettier-config": "0.0.4", | ||
"chai": "^4.2.0", | ||
"eslint": "^6.6.0", | ||
"eslint-plugin-github": "^4.0.1", | ||
"karma": "^5.0.4", | ||
"axe-core": "^4.4.0", | ||
"chai": "^4.3.6", | ||
"chromium": "^3.0.3", | ||
"eslint": "^7.25.0", | ||
"eslint-plugin-custom-elements": "^0.0.2", | ||
"eslint-plugin-github": "^4.1.3", | ||
"http-server": "^14.0.0", | ||
"karma": "^6.3.2", | ||
"karma-chai": "^0.1.0", | ||
@@ -45,10 +52,7 @@ "karma-chrome-launcher": "^3.1.0", | ||
"karma-mocha-reporter": "^2.2.5", | ||
"mocha": "^7.1.2", | ||
"rollup": "^2.12.0", | ||
"mocha": "^8.3.2", | ||
"rollup": "^2.45.2", | ||
"rollup-plugin-node-resolve": "^5.2.0", | ||
"typescript": "^3.9.3" | ||
}, | ||
"eslintIgnore": [ | ||
"dist/" | ||
] | ||
"typescript": "^4.2.4" | ||
} | ||
} |
@@ -31,7 +31,27 @@ # <auto-complete> element | ||
<auto-complete src="/users/search" for="users-popup"> | ||
<input type="text"> | ||
<input type="text" name="users"> | ||
<!-- | ||
Optional clear button: | ||
- id must match the id of the input or the name of the input plus "-clear" | ||
- recommended to be *before* UL elements to avoid conflicting with their blur logic | ||
Please see Note below on this button for more details | ||
--> | ||
<button id="users-clear">X</button> | ||
<ul id="users-popup"></ul> | ||
<!-- | ||
Optional div for screen reader feedback. Note the ID matches the ul, but with -feedback appended. | ||
Recommended: Use a "Screen Reader Only" class to position the element off the visual boundary of the page. | ||
--> | ||
<div id="users-popup-feedback" class="sr-only"></div> | ||
</auto-complete> | ||
``` | ||
If you want to enable auto-select (pressing Enter in the input will select the first option), using the above example: | ||
```html | ||
<auto-complete data-autoselect="true" src="/users/search" for="users-popup"> | ||
... | ||
</auto-complete> | ||
``` | ||
The server response should include the items that matched the search query. | ||
@@ -53,2 +73,9 @@ | ||
### A Note on Clear button | ||
While `input type="search"` comes with an `x` that clears the content of the field and refocuses it on many browsers, the implementation for this control is not keyboard accessible, and so we've opted to enable a customizable clear button so that your keyboard users will be able to interact with it. | ||
As an example: | ||
> In Chrome, this 'x' isn't a button but a div with a pseudo="-webkit-search-cancel-button". It doesn't have a tab index or a way to navigate to it without a mouse. Additionally, this control is only visible on mouse hover. | ||
## Attributes | ||
@@ -113,4 +140,39 @@ | ||
To view changes locally, run `npm run examples`. | ||
In `examples/index.html`, uncomment `<!--<script type="module" src="./dist/bundle.js"></script>-->` and comment out the script referencing the `unpkg` version. This allows you to use the `src` code in this repo. Otherwise, you will be pulling the latest published code, which will not reflect the local changes you are making. | ||
## Accessibility Testing | ||
We have included some custom rules that assist in providing guardrails to confirm this component is being used accessibly. | ||
If you are using the `axe-core` library in your project, | ||
```js | ||
import axe from 'axe-core' | ||
import autoCompleteRulesBuilder from '@github/auto-complete-element/validator' | ||
const autoCompleteRules = autoCompleteRulesBuilder() // optionally, pass in your app's custom rules object, it will build and return the full object | ||
axe.configure(autoCompleteRules) | ||
axe.run(document) | ||
``` | ||
## Validate usage in your project | ||
To confirm your usage is working as designed, | ||
```js | ||
import {validate} from '@github/auto-complete-element/validator' | ||
validate(document) | ||
``` | ||
Passes and failures may be determined by the length of the `passes` and `violations` arrays on the returned object: | ||
```js | ||
{ | ||
passes: [], | ||
violations: [] | ||
} | ||
``` | ||
## License | ||
Distributed under the MIT license. See LICENSE for details. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
54334
17
1258
176
17
1