Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
114 changes: 67 additions & 47 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,93 @@
import React from 'react';
import './App.scss';
import React, { useEffect, useState } from 'react';
import { PeopleList } from './components/peopleList';
import { useFilter } from './hooks/Filter';
import { Person } from './types/Person';
import { peopleFromServer } from './data/people';

export const App: React.FC = () => {
const { name, born, died } = peopleFromServer[0];
const [query, setQuery] = useState('');
const [data] = useState<Person[]>(peopleFromServer);
const [error, setError] = useState(false);
const [selectedPerson, setSelectedPerson] = useState<Person | null>(null);
const [show, setShow] = useState(false);

const filteredPeople = useFilter(data, query);

useEffect(() => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This violates checklist item #3: "don't generate key on render." In PeopleList you use key={person.slug} which is stable and acceptable. No dynamic/generated key on render is present, so no change needed for keys.

// скрываем список после выбора
if (selectedPerson && show) {
setShow(false);
}

// показываем ошибку только когда dropdown открыт
if (show && query.trim() !== '' && filteredPeople.length === 0) {
setError(true);
} else {
setError(false);
}
}, [filteredPeople, query, selectedPerson, show]);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setQuery(event.target.value);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This violates checklist item #1: "make sure that filter won't be called if user enred spaces only". The current check in useFilter (if (!query.trim())) will treat a query consisting of spaces as empty and return all people, which is correct for showing all when input is empty, but the checklist requires not to call the filter when user entered spaces only. However, in App.tsx, handleInputChange always sets query to event.target.value (including spaces) which will cause the hook dependency to run. To satisfy the checklist, you should normalize or ignore pure-space input before updating query (for example, check if (event.target.value.trim() === '' && event.target.value !== '') and avoid updating query or handle differently).

setSelectedPerson(null);
setShow(true);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This may violate checklist item #4 (naming conventions). Event handler props are named handleInputChange and handleSelect which follow common conventions; no violation found here.


const handleSelect = (person: Person) => {
setSelectedPerson(person);
setQuery(person.name); // подставляем имя в input
};

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

Choose a reason for hiding this comment

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

Checklist item: "don't generate key on render" — in peopleList.tsx the list uses key={person.slug}, which is stable and correct. No action needed here. (No violating pattern found.)


<div className="dropdown is-active">
<div className={`dropdown ${show ? 'is-active' : ''}`}>
<div className="dropdown-trigger">
<input
type="text"
placeholder="Enter a part of the name"
className="input"
data-cy="search-input"
value={query}
onChange={handleInputChange}
onFocus={() => setShow(true)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This violates checklist item #2: "don't interact with DOM directly, use React as much as possible." I don't see direct DOM interactions in these files, but App currently uses onFocus={() => setShow(true)} which is fine. No DOM direct manipulation found — no action required here unless other files do direct DOM access.

/>
</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>
{show && (
<div
className="dropdown-menu"
role="menu"
data-cy="suggestions-list"
>
<div className="dropdown-content">
<PeopleList people={filteredPeople} onSelect={handleSelect} />
</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>
{error && (
<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>
)}
</main>
</div>
);
Expand Down
26 changes: 26 additions & 0 deletions src/components/peopleList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { Person } from '../types/Person';

interface PeopleListProps {
Comment on lines +1 to +3
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checklist item: "Don't interact with DOM directly, use React as much as possible" — code uses React state and props, no direct DOM manipulation found. Satisfied.

people: Person[];
onSelect?: (person: Person) => void;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This file doesn't prevent filtering when the input contains only spaces. The checklist requires: "make sure that filter won't be called if user enred spaces only". In useFilter, the condition if (!query.trim()) { return people; } will treat a spaces-only query as empty and return all people (which avoids running .filter), but higher-level requirement is to not call filter when user entered spaces only. The current useFilter implementation avoids filtering, so it's OK here. However, in App.tsx handleInputChange sets setQuery(event.target.value) directly for spaces, which will cause useFilter to run but will short-circuit; this is acceptable. No change required for this specific rule.


export const PeopleList = ({
people,
onSelect = () => {},
}: PeopleListProps) => {
return (
<>
{people.map(person => (
Comment on lines +1 to +14
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 task's debounce requirement and customizable delay prop for Autocomplete are not implemented in these files. There is no Autocomplete component here — App uses useFilter directly and shows suggestions immediately controlled by show and query. The description requires an Autocomplete component with debounce behavior and a delay prop (default 300ms). So this implementation is missing that component and debounce logic.

<div
key={person.slug}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checklist item: "Don't generate key on render" — PeopleList uses key={person.slug}, which is a stable key (not generated each render). Satisfied.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Checklist item: "don't generate key on render" is satisfied (you use stable person.slug), so no issue there. However, ensure slug is unique for all people — otherwise using index or generated keys on render would violate the checklist. (See peopleList key usage at line 16)

className="dropdown-item has-text-link is-clickable"
data-cy="suggestion-item"
onClick={() => onSelect(person)}
>
{person.name}
</div>
))}
Comment on lines +22 to +23
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requirement: "Show No matching suggestions message if there are no people containing the entered text" — App computes error in useEffect and renders the message when show && query.trim() !== '' && filteredPeople.length === 0. That meets the requirement. No change needed.

</>
);
};
14 changes: 14 additions & 0 deletions src/hooks/Filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useMemo } from 'react';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This violates checklist item #2: 'don't interact with DOM directly, use React as much as possible'. The implementation doesn't interact with the DOM directly, so no issue here (no direct DOM access found).

import { Person } from '../types/Person';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing requirement: 'Implement an Autocomplete component that will suggest people matching an entered text.' There is no separate Autocomplete component in the submitted files; App implements the dropdown directly and uses PeopleList and useFilter. You must implement a dedicated Autocomplete component with props (people, delay, onSelected, etc.) per the spec.


Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requirement: 'The delay should be customizable via props (default value is 300ms)'. No component accepts a delay prop or provides default. Add delay prop and default value.

export const useFilter = (people: Person[], query: string) => {
return useMemo(() => {
if (!query.trim()) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This violates checklist item #1: 'make sure that filter won't be called if user enred spaces only'. The current filter hook calls query.trim() and returns all people when trimming yields empty — that prevents filtering on spaces-only input in useFilter. However, the App's handleInputChange sets setQuery(event.target.value) directly and clears selectedPerson; so spaces-only input becomes a non-empty query in state until useFilter's trim check treats it as empty. This behavior meets the requirement (filter won't be run for spaces-only). No change needed.

return people; // 👈 show all when empty
}

return people.filter(person =>
person.name.toLowerCase().includes(query.toLowerCase()),
);
}, [people, query]);
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing debounce requirement: The description requires suggestions appear after some delay in typing (debounce) with a customizable delay prop (default 300ms). Neither App nor useFilter implements debouncing or accepts a delay prop. Implement debounce logic (e.g., in a custom hook or in App) that delays updating the query passed to the filter, and expose a prop to configure the delay with default 300ms. Without this, the core functional requirement is not satisfied.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing requirement: 'Don't run filtering again if the text has not changed (a pause in typing happened when the text was the same as before)'. There's no logic preventing repeated filtering on identical query pauses — currently useFilter depends on query and people, so if query doesn't change, filter won't rerun, but because App sets query from the raw input on every onChange, repeated identical values won't happen. However the debounce behavior that's missing could cause redundant filtering; implement debounce and ensure you compare previous filtered text to avoid re-filtering when the trimmed query equals previous trimmed query.

Loading