Skip to content

Commit

Permalink
solution
Browse files Browse the repository at this point in the history
  • Loading branch information
YuriyBilskiy committed Nov 1, 2024
1 parent 1d2a025 commit 2a6dd8a
Show file tree
Hide file tree
Showing 10 changed files with 348 additions and 143 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ loaded and show them using `TodoList` (check the code in the `api.ts`);
- 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).
- Open one more terminal and run tests with `npm test` to ensure your solution is correct.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://<your_account>.github.io/react_dynamic-list-of-todos/) and add it to the PR description.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://yuriybilskiy.github.io/react_dynamic-list-of-todos/) and add it to the PR description.
20 changes: 15 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
"@fortawesome/fontawesome-free": "^6.5.2",
"bulma": "^1.0.1",
"classnames": "^2.5.1",
"lodash": "^4.17.21",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
"devDependencies": {
"@cypress/react18": "^2.0.1",
"@mate-academy/scripts": "^1.8.5",
"@mate-academy/scripts": "^1.9.12",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/lodash": "^4.17.13",
"@types/node": "^20.14.10",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
Expand Down
65 changes: 59 additions & 6 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,50 @@
/* eslint-disable max-len */
import React from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';

import { TodoList } from './components/TodoList';
import { TodoFilter } from './components/TodoFilter';
import { TodoModal } from './components/TodoModal';
import { Loader } from './components/Loader';
import { Todo } from './types/Todo';
import { getTodos } from './api';
import { FilterType } from './types/Filter';
import { filteredTodos } from './functions/filteredTodos';
import { useLoading } from './hooks/useLoading';

export const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [filter, setFilter] = useState<FilterType>('all');
const [query, setQuery] = useState('');
const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);
const { isLoading, startLoading, finishLoading } = useLoading();

const visibleTodos = filteredTodos(todos, filter, query);

const handleClickOfPost = useCallback(() => {
setSelectedTodo(null);
}, []);

const handleChangeSelect = useCallback((newValue: FilterType) => {
setFilter(newValue);
}, []);

useEffect(() => {
startLoading();
const fetchData = async () => {
try {
const repsonse = await getTodos();

setTodos(repsonse);
} finally {
finishLoading();
}
};

fetchData();
}, []);

Check warning on line 46 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

React Hook useEffect has missing dependencies: 'finishLoading' and 'startLoading'. Either include them or remove the dependency array

return (
<>
<div className="section">
Expand All @@ -17,18 +53,35 @@ export const App: React.FC = () => {
<h1 className="title">Todos:</h1>

<div className="block">
<TodoFilter />
<TodoFilter
setQuery={setQuery}
query={query}
handleChangeSelect={handleChangeSelect}
filter={filter}
setFilter={setFilter}
/>
</div>

<div className="block">
<Loader />
<TodoList />
{isLoading ? (
<Loader />
) : (
<TodoList
visibleTodos={visibleTodos}
setSelectedTodo={setSelectedTodo}
selectedTodo={selectedTodo}
/>
)}
</div>
</div>
</div>
</div>

<TodoModal />
{selectedTodo && (
<TodoModal
selectedTodo={selectedTodo}
handleClickOfPost={handleClickOfPost}
/>
)}
</>
);
};
125 changes: 95 additions & 30 deletions src/components/TodoFilter/TodoFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,95 @@
export const TodoFilter = () => (
<form className="field has-addons">
<p className="control">
<span className="select">
<select data-cy="statusSelect">
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
</span>
</p>

<p className="control is-expanded has-icons-left has-icons-right">
<input
data-cy="searchInput"
type="text"
className="input"
placeholder="Search..."
/>
<span className="icon is-left">
<i className="fas fa-magnifying-glass" />
</span>

<span className="icon is-right" style={{ pointerEvents: 'all' }}>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<button data-cy="clearSearchButton" type="button" className="delete" />
</span>
</p>
</form>
);
import { debounce } from 'lodash';
import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react';
import { FilterType } from '../../types/Filter';

type Props = {
setQuery: (value: string) => void;
query: string;
handleChangeSelect: (value: FilterType) => void;
filter: FilterType;
setFilter: (value: FilterType) => void;
};
export const TodoFilter: React.FC<Props> = ({
setQuery,
query,
handleChangeSelect,
filter,
setFilter,
}) => {
const [inputValue, setInputValue] = useState(query);

const debouncedSetQuery = useCallback(

Check warning on line 21 in src/components/TodoFilter/TodoFilter.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead
debounce((value: string) => {
setQuery(value);
}, 500),
[],
);

const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const value = event.target.value as FilterType;

handleChangeSelect(value);
};

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

setInputValue(value);
debouncedSetQuery(value);
};

const inputRef = useRef<HTMLInputElement>(null);

const clearInput = () => {
setInputValue('');
setQuery('');
setFilter('all');
};

useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);

return (
<form className="field has-addons">
<p className="control">
<span className="select">
<select data-cy="statusSelect" onChange={handleChange} value={filter}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
</span>
</p>

<p className="control is-expanded has-icons-left has-icons-right">
<input
ref={inputRef}
onChange={handleChangeInput}
value={inputValue}
data-cy="searchInput"
type="text"
className="input"
placeholder="Search..."
/>
<span className="icon is-left">
<i className="fas fa-magnifying-glass" />
</span>

{query && (
<span className="icon is-right" style={{ pointerEvents: 'all' }}>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<button
data-cy="clearSearchButton"
type="button"
className="delete"
onClick={clearInput}
/>
</span>
)}
</p>
</form>
);
};
Loading

0 comments on commit 2a6dd8a

Please sign in to comment.