Skip to content

Commit

Permalink
ft(user): WIP: Setup Skeleton Loading to improve User Experience
Browse files Browse the repository at this point in the history
- Install react-skeleton loading component
- Create a CardSkeleton Component
  • Loading branch information
Blessing Makaraba authored and Blessing Makaraba committed Sep 25, 2021
1 parent 3ac9fca commit 393f93b
Show file tree
Hide file tree
Showing 26 changed files with 453 additions and 44 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"react-scripts": "4.0.3",
"react-skeleton-loading": "^1.0.0",
"styled-components": "^5.3.1",
"typescript": "^4.1.2",
"web-vitals": "^1.0.1"
Expand Down
16 changes: 13 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import { GlobalStyle } from "./index.styles";
import "./index.css";
import Home from "UI/pages/Home/Home";
import Contributors from "UI/pages/Contributors/Contributors";
import { Route, BrowserRouter, Switch } from "react-router-dom";
import { Suspense } from "react";
import Loader from "UI/molecules/Loader/Loader";

const App = () => (
<>
<GlobalStyle />

<Home />
<BrowserRouter>
<GlobalStyle />
<Switch>
<Suspense fallback={<Loader />}>
<Route path="/" exact component={Home} />
<Route path="/Products" component={Contributors} />
</Suspense>
</Switch>
</BrowserRouter>
</>
);

Expand Down
10 changes: 9 additions & 1 deletion src/UI/atoms/Avatar/Avatar.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import styled from "styled-components";

export const Avatar = styled.img``;
export const AvatarImg = styled.div`
width: 250px;
height: 80px;
overflow: hidden;
img {
display: block;
}
`;
6 changes: 5 additions & 1 deletion src/UI/atoms/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { AvatarImg } from "./Avatar.styles";

type AvatarProps = {
imgUrl: string;
fullName: string;
};

const Avatar: React.FC<AvatarProps> = ({ imgUrl, fullName }) => (
<img src={imgUrl} alt={`${fullName}-avatar`} />
<AvatarImg>
<img src={imgUrl} alt={`${fullName}-avatar`} />
</AvatarImg>
);

export default Avatar;
14 changes: 13 additions & 1 deletion src/UI/atoms/Button/Button.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
import styled from "styled-components";

export const Button = styled.button``;
export const StyledButton = styled.button`
appearance: none;
outline: none;
padding: 0.6rem;
color: white;
background-color: #0e1117;
border: none;
&:disabled {
background-color: gray;
cursor: none;
}
`;
10 changes: 9 additions & 1 deletion src/UI/atoms/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
const Button: React.FC<{}> = () => <button type="submit">Search</button>;
import { StyledButton } from "./Button.styles";

const Button: React.FC<{ disable: boolean }> = ({ disable }) => {
return (
<StyledButton type="submit" disabled={disable}>
Search
</StyledButton>
);
};

export default Button;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ type DescriptionProps = {
};

const Description: React.FC<DescriptionProps> = ({ label, content }) => (
<>
<span>{label}</span> <span>{content}</span>
</>
<p>
<span>{label}</span>: <span>{content}</span>
</p>
);

export default Description;
6 changes: 5 additions & 1 deletion src/UI/atoms/Input/Input.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import styled from "styled-components";

export const InputField = styled.input``;
export const InputField = styled.input`
padding: 0.5rem;
width: 90%;
margin: 0 auto;
`;
5 changes: 3 additions & 2 deletions src/UI/atoms/Input/Input.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { InputField } from "./Input.styles";

type InputProps = {
content: string;
placeholder: string;
Expand All @@ -9,9 +11,8 @@ const Input: React.FC<InputProps> = ({
content,
onChangeFunction,
}) => {
console.log(onChangeFunction);
return (
<input
<InputField
placeholder={placeholder}
value={content}
onChange={onChangeFunction}
Expand Down
4 changes: 3 additions & 1 deletion src/UI/atoms/Title/Title.styles.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import styled from "styled-components";

export const Label = styled.h3``;
export const StyledTitle = styled.h1`
text-align: center;
`;
6 changes: 5 additions & 1 deletion src/UI/atoms/Title/Title.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
const Title: React.FC<{}> = () => <h1>GitHub Repository Search</h1>;
import { StyledTitle } from "./Title.styles";

const Title: React.FC<{ children: string }> = ({ children }) => (
<StyledTitle>{children}</StyledTitle>
);

export default Title;
23 changes: 23 additions & 0 deletions src/UI/molecules/Loader/Loader.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import styled, { keyframes } from "styled-components";

const rotate = keyframes`
to {
transform: rotate(360deg);
}
`;

export const SpinnerContainer = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
`;

export const Spinner = styled.div`
width: 100px;
height: 100px;
border: 3px solid #f5f5f5;
border-radius: 50%;
border-top-color: paleturquoise;
animation: ${rotate} 0.5s linear infinite;
`;
9 changes: 9 additions & 0 deletions src/UI/molecules/Loader/Loader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { SpinnerContainer, Spinner } from "./Loader.styles";

const Loader = () => (
<SpinnerContainer>
<Spinner />
</SpinnerContainer>
);

export default Loader;
13 changes: 12 additions & 1 deletion src/UI/molecules/Search/Search.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
import styled from "styled-components";

export const SearchComponent = styled.div``;
export const SearchComponent = styled.div`
background-color: white;
display: flex;
justify-content: space-between;
margin: 0 auto;
padding: 2rem;
`;

export const SearchBox = styled.div`
width: 60%;
align-self: center;
`;
25 changes: 15 additions & 10 deletions src/UI/molecules/Search/Search.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,27 @@
import Button from "UI/atoms/Button/Button";
import Input from "UI/atoms/Input/Input";
import Title from "UI/atoms/Title/Title";
import { SearchBox, SearchComponent } from "./Search.styles";

type SearchProps = {
loading: boolean;
value: string;
onChangeFunc: (event: React.ChangeEvent<HTMLDivElement>) => void;
};
const Search: React.FC<SearchProps> = ({ onChangeFunc, value }) => {
const Search: React.FC<SearchProps> = ({ onChangeFunc, value, loading }) => {
return (
<>
<Title />
<Input
placeholder="Search Repositories"
content={value}
onChangeFunction={onChangeFunc}
/>
<Button />
</>
<SearchComponent>
<Title>GitHub Repository Search</Title>

<SearchBox>
<Input
placeholder="Search Repositories"
content={value}
onChangeFunction={onChangeFunc}
/>
<Button disable={loading} />
</SearchBox>
</SearchComponent>
);
};

Expand Down
3 changes: 3 additions & 0 deletions src/UI/pages/Contributors/Contributors.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import styled from "styled-components";

export const ContributorsComponent = styled.div``;
6 changes: 6 additions & 0 deletions src/UI/pages/Home/Home.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import styled from "styled-components";

export const Home = styled.div``;

export const CardDisplayWrapper = styled.div`
display: flex;
justify-content: space-around;
flex-wrap: wrap;
`;
49 changes: 43 additions & 6 deletions src/UI/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,62 @@
import { useState } from "react";
import useRequest from "hooks/request";
import React, { useEffect, useState } from "react";
import Title from "UI/atoms/Title/Title";
import Search from "UI/molecules/Search/Search";
import Card from "UI/templates/Card/Card";
import CardSkeleton from "UI/templates/CardSkeleton/CardSkeleton";
import { CardDisplayWrapper } from "./Home.styles";

const Home: React.FC<{}> = () => {
const [searchValue, setSearchValue] = useState<string>("");

const [data, error, loading, makeRequest] = useRequest();

const handleChange = (event: React.ChangeEvent<HTMLDivElement>) => {
event.preventDefault();
const currentTarget = event.target as HTMLInputElement;

console.log(currentTarget.value);
setSearchValue(currentTarget.value);
};

const handleSubmit = (event: React.MouseEvent<HTMLFormElement>) => {
event.preventDefault();

console.log({ searchValue });

makeRequest(
`https://api.github.com/search/repositories?q=${searchValue}&sort=stars&order=desc&per_page=10`
);
};

useEffect(() => {
makeRequest(
"https://api.github.com/search/users?q=repos:%3E42+followers:%3E1000&sort=stars&order=desc&per_page=10"
);
}, [makeRequest]);

let cardDisplay;

if (loading) {
cardDisplay = Array(9)
.fill("9")
.map((item: any, index: number) => (
<CardSkeleton key={index} datum={item} />
));
} else {
cardDisplay = data.map((item: any) => <Card key={item.id} datum={item} />);
}

return (
<>
<form>
<Search onChangeFunc={handleChange} value={searchValue} />
<form onSubmit={handleSubmit}>
<Search
onChangeFunc={handleChange}
value={searchValue}
loading={loading}
/>
</form>

<Card />
<Title>Popular Repositories</Title>
<CardDisplayWrapper>{cardDisplay}</CardDisplayWrapper>
</>
);
};
Expand Down
29 changes: 28 additions & 1 deletion src/UI/templates/Card/Card.styles.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
import styled from "styled-components";
import { Link } from "react-router-dom";

export const CardDisplay = styled.div``;
export const StyledCard = styled.div`
background-color: white;
border-bottom: 1px solid #272b36;
padding: 1rem;
flex-basis: 80%;
margin-top: 2rem;
& p {
margin-top: 2rem;
}
@media screen and (min-width: 1200px) {
flex-basis: 30%;
}
`;

export const StyledLink = styled(Link)`
appearance: none;
text-decoration: none;
background-color: #0e1117;
display: inline-block;
color: white;
border-radius: 6px;
padding: 0.5rem;
margin-top: 2rem;
`;
20 changes: 13 additions & 7 deletions src/UI/templates/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Link } from "react-router-dom";
import Avatar from "UI/atoms/Avatar/Avatar";
import Description from "UI/atoms/Description/Description";
import { StyledCard, StyledLink } from "./Card.styles";

const Card: React.FC<{}> = () => (
<>
<div>
<Link to="/contributors" />
</div>
</>
const Card: React.FC<{ datum: any }> = ({ datum }) => (
<StyledCard>
<Avatar imgUrl="https://google.com" fullName="facebbok" />
<Description label="Repository" content="Facebook/React" />
<Description label="Owner" content="Facebook" />
<Description label="Number of Stars" content="12345" />
<Description label="Number of Forks" content="123485" />

<StyledLink to="/contributors">View Contributors</StyledLink>
</StyledCard>
);

export default Card;
6 changes: 6 additions & 0 deletions src/UI/templates/CardSkeleton/CardSkeleton.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import styled from "styled-components";

import Skeleton from "react-skeleton-loading";
import Description from "UI/atoms/Description/Description";

export const DescriptionSkeleton = styled(Skeleton)``;
Loading

0 comments on commit 393f93b

Please sign in to comment.