Skip to content

Commit

Permalink
Setup Wikipedia Pageviews homepage (#1)
Browse files Browse the repository at this point in the history
* pull articles into homepage

* add article pinning

* add Jest and simple test

* handle invalid date entry, add aria-labels

* update README
  • Loading branch information
AlyssaKirstine authored Jun 8, 2022
1 parent 5ec8010 commit e4feaf9
Show file tree
Hide file tree
Showing 19 changed files with 1,722 additions and 250 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ node_modules/
.cache/
public
src/gatsby-types.d.ts
.vscode/
78 changes: 47 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,70 @@
<p align="center">
<a href="https://www.gatsbyjs.com/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts">
<img alt="Gatsby" src="https://www.gatsbyjs.com/Gatsby-Monogram.svg" width="60" />
</a>
</p>
<h1 align="center">
Gatsby minimal TypeScript starter
<div align="center">
<h1>
Wikipedia Pageviews 👀
</h1>
<p>Wikipedia Pageviews is a single-page application that allows users to pick and date and see which Wikipedia pages were viewed the most that day. They can also save articles they want to come back to as pins.</p>
<p>Created by <a href="https://github.com/AlyssaKirstine">@AlyssaKirstine</a></p>
</div>

## 🚀 Quick start
## 🕵️ Technical Details

1. **Create a Gatsby site.**
Wikipedia Pageviews was built using the [Gatsby](https://www.gatsbyjs.com/) framework and deployed using [Netlify](https://www.netlify.com/).

Use the Gatsby CLI to create a new site, specifying the minimal TypeScript starter.
See it live 😍 https://wikipedia-pageviews.netlify.app/

```shell
# create a new Gatsby site using the minimal TypeScript starter
npm init gatsby
```
- The homepage is setup in [`src/pages/index.tsx`](https://github.com/AlyssaKirstine/wikipedia-pageviews/blob/main/src/pages/index.tsx).
- Testing
- This repo is setup to support [Jest tests](https://jestjs.io/). Check out an example test in [`src/utils/article/`](https://github.com/AlyssaKirstine/wikipedia-pageviews/tree/main/src/utils/article/test.ts).
- Code formatting
- This repo uses ESLint and Prettier to format the code automatically before any commits are made.

## 🚀 Build Instructions

1. **Setup your local copy of this repo.**

2. **Start developing.**
Use `git clone` to clone this repo to your local computer.

Navigate into your new site’s directory and start it up.
Using HTTPS:

```shell
cd my-gatsby-site/
npm run develop
git clone https://github.com/AlyssaKirstine/wikipedia-pageviews.git
```

3. **Open the code and start customizing!**
Using SSH:

Your site is now running at http://localhost:8000!
```shell
git clone [email protected]:AlyssaKirstine/wikipedia-pageviews.git
```

Edit `src/pages/index.tsx` to see your site update in real-time!
2. **Install node modules.**

4. **Learn more**
Navigate to the project directory and install node modules.

- [Documentation](https://www.gatsbyjs.com/docs/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
```shell
cd wikipedia-pageviews/
yarn install
```

3. **Build site**

- [Tutorials](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
To start a development build, run `yarn start`.

- [Guides](https://www.gatsbyjs.com/tutorial/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
```shell
yarn start
```

- [API Reference](https://www.gatsbyjs.com/docs/api-reference/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
To start a production build, run `yarn build`. This will generate bundle files via webpack in your `public` root folder.
Then run `yarn serve` to serve.

- [Plugin Library](https://www.gatsbyjs.com/plugins?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
```shell
yarn build
yarn serve
```

- [Cheat Sheet](https://www.gatsbyjs.com/docs/cheat-sheet/?utm_source=starter&utm_medium=readme&utm_campaign=minimal-starter-ts)
4. **Check it out!**

## 🚀 Quick start (Gatsby Cloud)
Visit the applicable URL in your browser of choice.

Deploy this starter with one click on [Gatsby Cloud](https://www.gatsbyjs.com/cloud/):
Development build: [`http://localhost:8000`](http://localhost:8000)

[<img src="https://www.gatsbyjs.com/deploynow.svg" alt="Deploy to Gatsby Cloud">](https://www.gatsbyjs.com/dashboard/deploynow?url=https://github.com/gatsbyjs/gatsby-starter-minimal-ts)
Production build: [`http://localhost:9000`](http://localhost:9000)
10 changes: 3 additions & 7 deletions gatsby-config.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
import type { GatsbyConfig } from "gatsby";
import type { GatsbyConfig } from 'gatsby';

const config: GatsbyConfig = {
siteMetadata: {
title: `Wikipedia Pageviews`,
siteUrl: `https://www.yourdomain.tld`
siteUrl: `https://wikipedia-pageviews.netlify.app/`,
},
// More easily incorporate content into your pages through automatic TypeScript type generation and better GraphQL IntelliSense.
// If you use VSCode you can also use the GraphQL plugin
// Learn more at: https://gatsby.dev/graphql-typegen
graphqlTypegen: true,
plugins: ["gatsby-plugin-emotion"]
plugins: ['gatsby-plugin-emotion'],
};

export default config;
10 changes: 10 additions & 0 deletions jest-preprocess.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const babelOptions = {
presets: ['babel-preset-gatsby', '@emotion/babel-preset-css-prop'],
env: {
test: {
presets: ['@babel/preset-typescript', '@emotion/babel-preset-css-prop'],
},
},
};

module.exports = require('babel-jest').default.createTransformer(babelOptions);
18 changes: 18 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
transform: {
'^.+\\.(t|j)sx?$': '<rootDir>/jest-preprocess.js',
},
moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss)$': `identity-obj-proxy`,
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': `<rootDir>/__mocks__/file-mock.js`,
},
testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
transformIgnorePatterns: [`node_modules/(?!(gatsby|gatsby-script)/)`],
globals: {
__PATH_PREFIX__: ``,
},
testEnvironmentOptions: {
url: `http://localhost`,
},
setupFiles: [`<rootDir>/loadershim.js`],
};
3 changes: 3 additions & 0 deletions loadershim.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
global.___loader = {
enqueue: jest.fn(),
};
18 changes: 16 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@
"name": "wikipedia-pageviews",
"version": "1.0.0",
"private": true,
"description": "wikipedia-pageviews",
"author": "Alyssa Melendez",
"description": "A Wikipedia pageviews application",
"repository": "[email protected]:AlyssaKirstine/wikipedia-pageviews.git",
"author": "Alyssa Melendez <[email protected]>",
"keywords": [
"gatsby"
],
"dependencies": {
"@emotion/react": "^11.9.0",
"@emotion/styled": "^11.8.1",
"axios": "^0.27.2",
"gatsby": "^4.15.2",
"gatsby-plugin-emotion": "^7.16.0",
"react": "^18.1.0",
"react-datepicker": "^4.8.0",
"react-dom": "^18.1.0"
},
"devDependencies": {
"@babel/preset-typescript": "^7.17.12",
"@emotion/babel-preset-css-prop": "^11.2.0",
"@emotion/eslint-plugin": "^11.7.0",
"@types/jest": "^28.1.1",
"@types/node": "^17.0.35",
"@types/react": "^18.0.9",
"@types/react-datepicker": "^4.4.2",
"@types/react-dom": "^18.0.5",
"@types/react-test-renderer": "^18.0.0",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.1",
"babel-jest": "^28.1.1",
"babel-preset-gatsby": "^2.16.0",
"eslint": "^8.17.0",
"eslint-config-prettier": "^8.5.0",
"eslint-import-resolver-webpack": "^0.13.2",
Expand All @@ -30,8 +40,11 @@
"eslint-plugin-react": "^7.30.0",
"eslint-webpack-plugin": "^3.1.1",
"husky": "^8.0.1",
"identity-obj-proxy": "^3.0.0",
"jest": "^28.1.1",
"lint-staged": "^13.0.0",
"prettier": "2.6.2",
"react-test-renderer": "^18.1.0",
"typescript": "^4.7.2"
},
"scripts": {
Expand All @@ -40,6 +53,7 @@
"build": "gatsby build",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "jest",
"typecheck": "tsc --noEmit",
"webpack-dev": "gatsby develop -H gatsby.wikipedia-pageviews.localhost -o",
"prepare": "husky install",
Expand Down
139 changes: 139 additions & 0 deletions src/components/articleList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import axios from 'axios';
import React, { useEffect, useState } from 'react';

import 'react-datepicker/dist/react-datepicker.css';
import { formatArticleName, getArticleUrl } from '../../utils/article';
import {
cardContainerStyles,
cardStyles,
nameStyles,
pinIconStyles,
pinnedArticlesStyles,
rankStyles,
} from './style';

interface ArticleType {
article: string;
views: number;
rank: number;
}

const ArticleList = ({
date,
numberOfResults,
}: {
date: Date;
numberOfResults: number;
}) => {
const [articles, setArticles] = useState<Array<ArticleType>>();
const [pinnedArticles, setPinnedArticles] = useState<Array<string>>();

useEffect(() => {
const fetchData = async () => {
const formattedDate = date
.toISOString()
.slice(0, 10)
.replaceAll('-', '/');

const response = await axios.get(
`https://wikimedia.org/api/rest_v1/metrics/pageviews/top/en.wikipedia/all-access/${formattedDate}`,
);

setArticles(response.data.items[0].articles.slice(0, numberOfResults));
};

fetchData().catch(() => {
setArticles([]);
return console.error;
});
}, [date, numberOfResults]);

useEffect(() => {
const storedArticles = localStorage.getItem('articles');
if (storedArticles) setPinnedArticles(JSON.parse(storedArticles));
}, []);

const handlePinClick = (name: string) => {
addArticlePin(name);
};

const addArticlePin = (name: string) => {
let newArticlesArray: Array<string> = [];
const storedArticles = localStorage.getItem('articles');

if (storedArticles) {
newArticlesArray = JSON.parse(storedArticles);
if (newArticlesArray.find((element) => element === name))
alert(
`You've already pinned this article. Why don't you explore something new?`,
);
else newArticlesArray.push(name);
}
setPinnedArticles(newArticlesArray);
localStorage.setItem('articles', JSON.stringify(newArticlesArray));
};

const removeArticlePin = (name: string) => {
let newArticlesArray: Array<string> = [];
const storedArticles = localStorage.getItem('articles');

if (storedArticles) {
newArticlesArray = JSON.parse(storedArticles);
newArticlesArray = newArticlesArray.filter((value) => value !== name);
setPinnedArticles(newArticlesArray);
localStorage.setItem('articles', JSON.stringify(newArticlesArray));
}
};

return (
<div>
<div css={pinnedArticlesStyles}>
<p>
<strong>Pins:</strong>
</p>
<p>Click the 📌 icon to save an article here.</p>
<ul>
{pinnedArticles?.map((article) => (
<li key={article}>
<a href={getArticleUrl(article)}>{formatArticleName(article)}</a>{' '}
<button
onClick={() => removeArticlePin(article)}
aria-label={`delete ${formatArticleName(article)} from pins`}
>
delete
</button>
</li>
))}
</ul>
</div>
<div css={cardContainerStyles}>
{!articles?.length ? (
<p>Bummer! 😞 There are no results for that date.</p>
) : (
articles.map(({ article: name, rank, views }) => {
return (
<div key={name} css={cardStyles}>
<button
onClick={() => handlePinClick(name)}
css={pinIconStyles}
aria-label={`Pin ${formatArticleName(name)} article`}
>
📌
</button>
<p css={rankStyles}>
<strong>#{rank}</strong>
</p>
<h2 css={nameStyles}>
<a href={getArticleUrl(name)}>{formatArticleName(name)}</a>
</h2>
<p>{views} views</p>
</div>
);
})
)}
</div>
</div>
);
};

export default ArticleList;
Loading

0 comments on commit e4feaf9

Please sign in to comment.