Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ that will suggest people matching an entered text.
- Install Prettier Extention and use this [VSCode settings](https://mate-academy.github.io/fe-program/tools/vscode/settings.json) to enable format on save.
- Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline).
- Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript).
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://<your_account>.github.io/react_autocomplete/) and add it to the PR description.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://Yevhen-Srdk.github.io/react_autocomplete/) and add it to the PR description.
- Don't remove the `data-qa` attributes. It is required for tests.

## Troubleshooting
Expand Down
71 changes: 11 additions & 60 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,24 @@
import React from 'react';
import './App.scss';
import { peopleFromServer } from './data/people';
import { Person } from './types/Person';
import { Autocomplete } from './components/Autocomplete';
import { useState } from 'react';

export const App: React.FC = () => {
const { name, born, died } = peopleFromServer[0];
const [selectedPerson, setSelectedPerson] = useState<Person | null>(null);

return (
<div className="container">
<main className="section is-flex is-flex-direction-column">
<h1 className="title" data-cy="title">
{`${name} (${born} - ${died})`}
{selectedPerson
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The app's title uses data-cy="title". Tests and the task description require data-qa attributes. Please replace the attribute with data-qa="title". (Requirement: "Don't remove the data-qa attributes. It is required for tests.")

? `${selectedPerson.name} (${selectedPerson.born} - ${selectedPerson.died})`
: `No selected person`}
</h1>

<div className="dropdown is-active">
<div className="dropdown-trigger">
<input
type="text"
placeholder="Enter a part of the name"
className="input"
data-cy="search-input"
/>
</div>

<div className="dropdown-menu" role="menu" data-cy="suggestions-list">
<div className="dropdown-content">
<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-link">Pieter Haverbeke</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-link">Pieter Bernard Haverbeke</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-link">Pieter Antone Haverbeke</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-danger">Elisabeth Haverbeke</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-link">Pieter de Decker</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-danger">Petronella de Decker</p>
</div>

<div className="dropdown-item" data-cy="suggestion-item">
<p className="has-text-danger">Elisabeth Hercke</p>
</div>
</div>
</div>
</div>

<div
className="
notification
is-danger
is-light
mt-3
is-align-self-flex-start
"
role="alert"
data-cy="no-suggestions-message"
>
<p className="has-text-danger">No matching suggestions</p>
</div>
<Autocomplete
people={peopleFromServer}
onSelected={setSelectedPerson}
/>
</main>
</div>
);
Expand Down
106 changes: 106 additions & 0 deletions src/components/Autocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React, { useEffect, useRef, useState } from 'react';
import { Person } from '../types/Person';
import classNames from 'classnames';

type Props = {
people: Person[];
delay?: number;
onSelected: (person: Person | null) => void;
};

export const Autocomplete: React.FC<Props> = ({
people,
delay = 300,
onSelected,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The project instructions say: "Don't remove the data-qa attributes. It is required for tests." The

title currently uses data-cy="title" instead of data-qa="title". Please replace data-cy with data-qa on the title so tests/selectors depending on data-qa="title" will work.

}) => {
const [query, setQuery] = useState('');
const [isOpen, setIsOpen] = useState(false);
const [suggestions, setSuggestions] = useState<Person[]>(people);
const prevQuery = useRef('');

useEffect(() => {
const timeout = setTimeout(() => {
if (prevQuery.current === query) {
return;
}

prevQuery.current = query;

if (!query.trim()) {
setSuggestions(people);

return;
}

const filtered = people.filter(person =>
person.name.toLowerCase().includes(query.toLowerCase()),
);

setSuggestions(filtered);
}, delay);

return () => clearTimeout(timeout);
}, [query, delay, people]);

const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;

setQuery(value);
onSelected(null);
};

const handleFocus = () => {
setIsOpen(true);
};

return (
<div className={classNames('dropdown', { 'is-active': isOpen })}>
<div className="dropdown-trigger">
<input
type="text"
placeholder="Enter a part of the name"
className="input"
data-qa="search-input"
value={query}
onChange={handleQueryChange}
onFocus={handleFocus}
/>
</div>

<div className="dropdown-menu" role="menu" data-qa="suggestions-list">
<div className="dropdown-content">
{suggestions.map((human: Person) => (
<div
key={human.name + human.born}
className="dropdown-item"
data-qa="suggestion-item"
onMouseDown={() => {
setQuery(human.name);
setIsOpen(false);
onSelected(human);
}}
>
<p className="has-text-link">{human.name}</p>
</div>
))}
</div>
</div>

{query.trim() && suggestions.length === 0 && (
<div
className="
notification
is-danger
is-light
mt-3
is-align-self-flex-start
"
role="alert"
data-qa="no-suggestions-message"
>
<p className="has-text-danger">No matching suggestions</p>
</div>
)}
</div>
);
};
Loading