Skip to content
Open
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
79 changes: 71 additions & 8 deletions Autocomplete.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'regenerator-runtime/runtime'

export default class Autocomplete {
constructor(rootEl, options = {}) {
constructor(rootEl, url, id, options = {}) {
this.rootEl = rootEl;
this.url = url;
this.id = id;
this.options = {
numOfResults: 10,
data: [],
Expand All @@ -22,12 +26,34 @@ export default class Autocomplete {
});
}

onQueryChange(query) {
async onQueryChange(query) {
let results;

// Get data for the dropdown
let results = this.getResults(query, this.options.data);
results = results.slice(0, this.options.numOfResults);

this.updateDropdown(results);
if (query.length > 0) {
await this.fetchData(query).then((data)=> {
this.results = data;
});
} else {
this.results = [];
}

this.updateDropdown(this.results);
}

async fetchData (query) {
return fetch(this.url + query + "&per_page=" + this.options.numOfResults)
.then((resp) => resp.json())
.then(function(data) {
let items = data.items
return items.map(item => ({
text: item[Object.keys(item)[0]],
value: item[Object.keys(item)[1]],
}));
})
.catch(function(error) {
console.log(error);
});
}

updateDropdown(results) {
Expand All @@ -37,31 +63,67 @@ export default class Autocomplete {

createResultsEl(results) {
const fragment = document.createDocumentFragment();
results.forEach((result) => {
results.forEach((result, i) => {
const el = document.createElement('li');
el.classList.add('result');
el.textContent = result.text;
el.setAttribute('tabindex', i);

// Pass the value to the onSelect callback
el.addEventListener('click', () => {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.value);
});

el.addEventListener('keypress', (e) => {
if(e.keyCode === 13) {
const { onSelect } = this.options;
if (typeof onSelect === 'function') onSelect(result.value);
}
});


fragment.appendChild(el);
});
return fragment;
}

scrollList() {
var list = document.getElementById(this.id + "list");
var first = list.firstChild;
var input = document.getElementById(this.id);
document.onkeydown = function(e) {
switch (e.keyCode) {
case 38: //up
if (document.activeElement == first) {
break;
}
else {
document.activeElement.previousSibling.focus();
}
break;
case 40: //down
if (document.activeElement == input) {
first.focus();
}
else {
document.activeElement.nextSibling.focus();
}
break;
}
}
}

createQueryInputEl() {
const inputEl = document.createElement('input');
inputEl.setAttribute('type', 'search');
inputEl.setAttribute('name', 'query');
inputEl.setAttribute('id', this.id);
inputEl.setAttribute('autocomplete', 'off');

inputEl.addEventListener('input',
event => this.onQueryChange(event.target.value));

inputEl.addEventListener('keydown', this.scrollList);
return inputEl;
}

Expand All @@ -73,6 +135,7 @@ export default class Autocomplete {
// Build results dropdown
this.listEl = document.createElement('ul');
this.listEl.classList.add('results');
this.listEl.setAttribute('id', this.id + "list");
this.rootEl.appendChild(this.listEl);
}
}
4 changes: 4 additions & 0 deletions SOLUTION.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Solution Docs

<!-- Include documentation, additional setup instructions, notes etc. here -->
To esure multiple instances of the component can be used on the same page, I gave the component an option for a unique id.
I added an eventlistener to the input to allow for the movement with arrow keys. Added a tabindex to get the focus funtion to work.
To allow selection, I added an eventlistener to each list item to listen for the enter key and call the onselection function already provided.
Allowed the url to be a parameter for the component.
15 changes: 11 additions & 4 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@
</head>
<body>
<!-- Using an array object -->
<div class="state-group form-group">
<!-- <div class="state-group form-group">
<label>State:</label>
<div id="state"></div>
</div>
</div> -->

<!-- Using a HTTP endpoint -->
<!-- <div class="gh-users-group form-group">
<div class="gh-users-group form-group">
<label>Github User:</label>
<div id="gh-user"></div>
</div> -->
</div>

<!-- Using a HTTP endpoint -->
<div class="gh-users-group form-group">
<label>Github Topics:</label>
<div id="gh-topic"></div>
</div>

<script src="/dist/bundle.js"></script>

</body>
</html>
27 changes: 10 additions & 17 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,16 @@ import Autocomplete from './Autocomplete';
import usStates from './us-states';
import './main.css';


// US States
const data = usStates.map(state => ({
text: state.name,
value: state.abbreviation,
}));
new Autocomplete(document.getElementById('state'), {
data,
onSelect: (stateCode) => {
console.log('selected state:', stateCode);
// Github Users
new Autocomplete(document.getElementById('gh-user'), "https://api.github.com/search/users?q=", "users",{
onSelect: (ghUserId) => {
console.log('selected github user id:', ghUserId);
},
});


// Github Users
// new Autocomplete(document.getElementById('gh-user'), {
// onSelect: (ghUserId) => {
// console.log('selected github user id:', ghUserId);
// },
// });
// Github Topics
new Autocomplete(document.getElementById('gh-topic'), "https://api.github.com/search/topics?q=", "topics", {
onSelect: (ghTopic) => {
console.log('selected github topic:', ghTopic);
},
});
6 changes: 5 additions & 1 deletion main.css
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ ul {
background-color: #eee;
}

.result:focus {
background-color: #eee;
}

form {
display: flex;
}
Expand All @@ -46,4 +50,4 @@ input {
width: 100%;
padding: 5px;
border: 1px solid #ccc;
}
}
Loading