Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

solution #1022

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
68 changes: 17 additions & 51 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,64 +1,30 @@
import React from 'react';
import 'bulma/bulma.sass';
import '@fortawesome/fontawesome-free/css/all.css';
import './App.scss';

import classNames from 'classnames';
import { PostsList } from './components/PostsList';
import { PostDetails } from './components/PostDetails';
import { UserSelector } from './components/UserSelector';
import { Loader } from './components/Loader';
import React from 'react';
import { UserSelector } from './components/Users/UserSelector';
import { MainContent } from './components/Posts/MainContent';
import { PostSlidebar } from './components/Posts/PostSlidebar';
import { MainProvider } from './components/MainContext';

export const App: React.FC = () => {
return (
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector />
</div>

<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">
No user selected
</p>

<Loader />

<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
<MainProvider>
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector />
</div>

<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
</div>

<PostsList />
<MainContent />
</div>
</div>
</div>

<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
)}
>
<div className="tile is-child box is-success ">
<PostDetails />
</div>
<PostSlidebar />
</div>
</div>
</div>
</main>
</main>
</MainProvider>
);
};
22 changes: 22 additions & 0 deletions src/components/Comments/AddButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, { useContext } from 'react';
import { MainContext } from '../MainContext';

export const AddButton: React.FC = () => {
const { setIsForm, setIsAddButton } = useContext(MainContext);

const handleClick = () => {
setIsForm(true);
setIsAddButton(false);
};

return (
<button
data-cy="WriteCommentButton"
type="button"
onClick={handleClick}
className="button is-link"
>
Write a comment
</button>
);
};
46 changes: 46 additions & 0 deletions src/components/Comments/CommentItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useContext } from 'react';
import { Comment } from '../../types/Comment';
import { deleteComment } from '../../utils/api/comments';
import { MainContext } from '../MainContext';

export const CommentItem: React.FC<{ comment: Comment }> = ({ comment }) => {
const { comments, setComments } = useContext(MainContext);
const {
name,
email,
id,
body,
} = comment;

const doDeleteComment = (commentId: number) => {
setComments(comments && comments.filter(item => item.id !== commentId));
deleteComment(commentId.toString());
};

return (
<article className="message is-small" data-cy="Comment">
<div className="message-header">
<a
href={`mailto:${email}`}
data-cy="CommentAuthor"
>
{name}
</a>

<button
data-cy="CommentDelete"
type="button"
className="delete is-small"
aria-label="delete"
onClick={() => doDeleteComment(id)}
>
delete button
</button>
</div>

<div className="message-body" data-cy="CommentBody">
{body}
</div>
</article>
);
};
19 changes: 19 additions & 0 deletions src/components/Comments/CommentsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useContext } from 'react';
import { MainContext } from '../MainContext';
import { CommentItem } from './CommentItem';

export const CommentsList: React.FC = () => {
const { comments } = useContext(MainContext);

return (
<>
{!!comments.length && <p className="title is-4">Comments:</p>}
{comments?.map(comment => (
<CommentItem
key={comment.id}
comment={comment}
/>
))}
</>
);
};
10 changes: 10 additions & 0 deletions src/components/Comments/NoteEmpty.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const NoteEmpty: React.FC = () => {
return (
<p
className="title is-4"
data-cy="NoCommentsMessage"
>
No comments yet
</p>
);
};
57 changes: 57 additions & 0 deletions src/components/Comments/PostDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, { useContext } from 'react';
import { MainContext } from '../MainContext';
import { CommentsList } from './CommentsList';
import { Loader } from '../Notices/Loader/Loader';
import { Load } from '../../types/Load';
import { Notification } from '../Notices/Notification';
import { Error } from '../../types/Message';
import { NewCommentForm } from '../Form/NewCommentForm';
import { AddButton } from './AddButton';
import { NoteEmpty } from './NoteEmpty';

export const PostDetails: React.FC = () => {
const {
isForm,
loadType,
comments,
isAddButton,
currentPost,
notification,
} = useContext(MainContext);

const isNoteOfEmpty = loadType === Load.None && !comments.length;
const isButtonHidden = (
notification === Error.getComments
|| loadType !== Load.None
|| !isAddButton
|| isForm
);

return (
<div className="content" data-cy="PostDetails">
<div className="content" data-cy="PostDetails">
<div className="block">
<h2 data-cy="PostTitle">
{`#${currentPost?.id}: ${currentPost?.title}`}
</h2>
<p data-cy="PostBody">
{currentPost?.body}
</p>
</div>

<div className="block">
{loadType === Load.Comments && <Loader />}

{notification === Error.getComments
? <Notification />
: isNoteOfEmpty && <NoteEmpty />}

<CommentsList />
{!isButtonHidden && <AddButton />}
{isForm && <NewCommentForm />}

</div>
</div>
</div>
);
};
107 changes: 107 additions & 0 deletions src/components/Form/FormInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { useContext, useEffect, useState } from 'react';
import classNames from 'classnames';
import {
Input,
HolderMap,
LabelMap,
ErrorMap,
} from '../../types/Input';
import { MainContext } from '../MainContext';

type Props = {
setValue: (val: string) => void
value: string
type: Input
};

export const FormInput: React.FC<Props> = ({ type, value, setValue }) => {
const { isError, setIsError, isReset } = useContext(MainContext);
const [hasError, setHasError] = useState(false);
const lowerCaseType = type.toLowerCase();

useEffect(() => {
if (value.length) {
return;
}

if (isError && !value.length) {
setHasError(true);
}

if (isReset) {
setHasError(false);
}
}, [isError, isReset, value]);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>
| React.ChangeEvent<HTMLTextAreaElement>) => {
setValue(e.target.value);
setIsError(false);
setHasError(false);
};

return (
<div className="field" data-cy={`${type}Field`}>
<label
className="label"
htmlFor={classNames(
{ 'comment-body': type === Input.Body },
{ 'comment-author-name': type === Input.Name },
{ 'comment-author-email': type === Input.Email },
)}
>
{LabelMap()[type]}
</label>

<div className={classNames('control',
{ 'has-icons-left has-icons-right': type !== Input.Body })}
>
{type !== Input.Body
? (
<>
<input
type="text"
name={lowerCaseType}
id={`comment-author-${lowerCaseType}`}
placeholder={HolderMap()[type]}
className={classNames('input', { 'is-danger': hasError })}
value={value}
onChange={(e) => handleChange(e)}
/>
<span className="icon is-small is-left">
<i className={classNames(
{ 'fas fa-user': type === Input.Name },
{ 'fas fa-envelope': type === Input.Email },
)}
/>
</span>

{hasError && (
<span
className="icon is-small is-right has-text-danger"
data-cy="ErrorIcon"
>
<i className="fas fa-exclamation-triangle" />
</span>
)}
</>
) : (
<textarea
name={lowerCaseType}
id="comment-body"
placeholder="Type comment here"
className={classNames('textarea', { 'is-danger': hasError })}
value={value}
onChange={(e) => handleChange(e)}
/>
)}

</div>
{hasError && (
<p className="help is-danger" data-cy="ErrorMessage">
{ErrorMap()[type]}
</p>
)}
</div>
);
};
Loading
Loading