refact: clean up (#12)

refact: clean up
This commit is contained in:
Jean-Philippe Sirois
2019-07-27 18:05:48 -04:00
committed by GitHub
24 changed files with 253 additions and 317 deletions
+6 -9
View File
@@ -1,12 +1,9 @@
import React from "react"
import React, { PropsWithChildren } from "react"
import * as SC from "./styles"
interface ContainerProps {
children: React.ReactNode
export function Container({
children,
...restProps
}: PropsWithChildren<{}>): JSX.Element {
return <SC.ContainerWrapper {...restProps}>{children}</SC.ContainerWrapper>
}
const Container = ({ children, ...restProps }: ContainerProps): JSX.Element => (
<SC.ContainerWrapper {...restProps}>{children}</SC.ContainerWrapper>
)
export default Container
+9 -17
View File
@@ -8,13 +8,11 @@
import React, { PropsWithChildren } from "react"
import { useStaticQuery, graphql } from "gatsby"
import Container from "../Container"
import DocsSidebar from "../DocsSidebar"
import { Main, MainContent } from "./styles"
import Scrollbar from "react-perfect-scrollbar"
import "react-perfect-scrollbar/dist/css/styles.css"
import { DocsSidebar } from "../DocsSidebar"
import { Footer } from "../Footer"
import * as SC from "./styles"
const DocsLayout = ({ children }: PropsWithChildren<{}>) => {
export function DocsLayout({ children }: PropsWithChildren<{}>) {
const data = useStaticQuery(graphql`
query {
site {
@@ -27,20 +25,14 @@ const DocsLayout = ({ children }: PropsWithChildren<{}>) => {
return (
<div>
<Main>
<SC.Main>
<DocsSidebar />
<MainContent>
<SC.MainContent>
<h1>{data.site.siteMetadata.title}</h1>
{children}
</MainContent>
</Main>
<footer>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.org">Gatsby</a>
</footer>
</SC.MainContent>
</SC.Main>
<Footer />
</div>
)
}
export default DocsLayout
-41
View File
@@ -1,41 +0,0 @@
// TODO: refactor this into styled components
html {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial,
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/**
* If you already use line highlighting
*/
/* Adjust the position of the line numbers */
.gatsby-highlight pre[class*="language-"].line-numbers {
padding-left: 2.8em;
}
/**
* If you only want to use line numbering
*/
.gatsby-highlight {
background-color: #fdf6e3;
border-radius: 0.3em;
margin: 0.5em 0;
padding: 1em;
overflow: auto;
}
.gatsby-highlight pre[class*="language-"].line-numbers {
padding: 0;
padding-left: 2.8em;
overflow: initial;
}
+10 -92
View File
@@ -1,99 +1,25 @@
import * as R from "ramda"
import React, { useState } from "react"
import Tree from "react-treeview"
import { useStaticQuery, graphql } from "gatsby"
import useBuildTree from "./useBuildTree"
import * as SC from "./styles"
interface IFile {
export interface IFile {
title: string
type: "file"
path: string
}
interface IFolder {
export interface IFolder {
title: string
type: "folder"
path: string
children: IFileOrFolder[]
}
type IFileOrFolder = IFile | IFolder
export type IFileOrFolder = IFile | IFolder
const traverse = (
[head, ...tail]: string[],
basePath = "/docs"
): IFileOrFolder => {
const path = basePath + "/" + head
const isFile = !tail.length
if (isFile) {
// probably not more than one dot
const [name] = head.split(".")
return {
title: name,
type: "file",
path,
}
}
return {
title: head,
type: "folder",
path,
children: [traverse(tail, path)],
}
}
const generateFolder = ({
title,
path,
targets,
}: {
title: IFolder["title"]
path: IFile["path"]
targets: IFolder[]
}): IFolder => {
const children = join(R.chain(target => target.children, targets))
return {
title,
type: "folder",
path,
children,
}
}
const generateFile = ({
title,
path,
}: {
title: IFile["title"]
path: IFile["path"]
}): IFile => {
return {
title,
path,
type: "file",
}
}
const join = ([head, ...tail]: IFileOrFolder[]): IFileOrFolder[] => {
if (!head) return []
const [similarFs, remaining] = R.partition(
obj => obj.title === head.title && obj.type === head.type,
tail
)
const targets = [head, ...similarFs]
const { title, path } = head
const current =
head.type === "folder"
? generateFolder({ title, path, targets: targets as IFolder[] })
: generateFile({ title, path })
return [current, ...join(remaining)]
}
interface IFileQuery {
export interface IFileQuery {
node: {
relativePath: string
childMarkdownRemark: {
@@ -105,7 +31,7 @@ interface IFileQuery {
}
}
interface IAllDocsQuery {
export interface IAllDocsQuery {
allFile: {
edges: IFileQuery[]
}
@@ -129,7 +55,7 @@ const ALL_DOCS = graphql`
}
`
const plantTree = (item: IFileOrFolder) => {
function plantTree(item: IFileOrFolder) {
if (item.type === "file") {
return (
<SC.PageLink to={item.path} activeClassName="active">
@@ -158,21 +84,13 @@ function Folder({ item }: { item: IFolder }) {
)
}
const DocsSidebar = () => {
export function DocsSidebar() {
const docs = useStaticQuery<IAllDocsQuery>(ALL_DOCS)
const objects = docs.allFile.edges.map(({ node: file }: IFileQuery) =>
traverse(file.relativePath.split("/"))
)
const results = join(objects)
console.log(results)
const tree = useBuildTree(docs)
return (
<SC.DocsSidebarWrapper>
{results.map(node => plantTree(node))}
{tree.map(node => plantTree(node))}
</SC.DocsSidebarWrapper>
)
}
export default DocsSidebar
@@ -0,0 +1,90 @@
import * as R from "ramda"
import {
IFileOrFolder,
IFile,
IFolder,
IAllDocsQuery,
IFileQuery,
} from "./index"
function traverse(
[head, ...tail]: string[],
basePath = "/docs"
): IFileOrFolder {
const path = basePath + "/" + head
const isFile = !tail.length
if (isFile) {
// probably not more than one dot
const [name] = head.split(".")
return {
title: name,
type: "file",
path,
}
}
return {
title: head,
type: "folder",
path,
children: [traverse(tail, path)],
}
}
function generateFolder({
title,
path,
targets,
}: {
title: IFolder["title"]
path: IFile["path"]
targets: IFolder[]
}): IFolder {
const children = join(R.chain(target => target.children, targets))
return {
title,
type: "folder",
path,
children,
}
}
function generateFile({
title,
path,
}: {
title: IFile["title"]
path: IFile["path"]
}): IFile {
return {
title,
type: "file",
path,
}
}
function join([head, ...tail]: IFileOrFolder[]): IFileOrFolder[] {
if (!head) return []
const [similarFs, remaining] = R.partition(
obj => obj.title === head.title && obj.type === head.type,
tail
)
const targets = [head, ...similarFs]
const { title, path } = head
const current =
head.type === "folder"
? generateFolder({ title, path, targets: targets as IFolder[] })
: generateFile({ title, path })
return [current, ...join(remaining)]
}
export default function useBuildTree(docs: IAllDocsQuery) {
const objects = docs.allFile.edges.map(({ node: file }: IFileQuery) =>
traverse(file.relativePath.split("/"))
)
return join(objects)
}
+5 -7
View File
@@ -1,20 +1,18 @@
import React from "react"
import { SidebarTitle } from "./style"
import React, { PropsWithChildren } from "react"
import * as SC from "./styles"
export interface DocsSidebarSectionProps {
readonly title: string
}
const DocsSidebarSection = ({
export function DocsSidebarSection({
title,
children,
}: React.PropsWithChildren<DocsSidebarSectionProps>) => {
}: PropsWithChildren<DocsSidebarSectionProps>) {
return (
<div>
<SidebarTitle>{title}</SidebarTitle>
<SC.SidebarTitle>{title}</SC.SidebarTitle>
{children}
</div>
)
}
export default DocsSidebarSection
@@ -1,5 +1,5 @@
import styled from "styled-components";
import styled from "styled-components"
export const SidebarTitle = styled.h3`
text-transform: uppercase;
`;
`
+15
View File
@@ -0,0 +1,15 @@
import React from "react"
import * as SC from "./styles"
import { Container } from "../Container"
export function Footer() {
return (
<SC.FooterWrapper>
<Container>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.org">Gatsby</a>
</Container>
</SC.FooterWrapper>
)
}
+4
View File
@@ -0,0 +1,4 @@
import { Link } from "gatsby"
import styled from "styled-components"
export const FooterWrapper = styled.footer``
+10 -18
View File
@@ -8,15 +8,15 @@
import React, { PropsWithChildren } from "react"
import { useStaticQuery, graphql } from "gatsby"
import Container from "../Container"
import Navbar from "../Navbar"
import Sidebar from "../Sidebar"
import { Container } from "../Container"
import { Footer } from "../Footer"
import { Sidebar } from "../Sidebar"
import "./layout.scss"
import { Main, MainContent } from "./styles"
import * as SC from "./styles"
import Scrollbar from "react-perfect-scrollbar"
import "react-perfect-scrollbar/dist/css/styles.css"
const Layout = ({ children }: PropsWithChildren<{}>) => {
export function Layout({ children }: PropsWithChildren<{}>) {
const data = useStaticQuery(graphql`
query {
site {
@@ -30,23 +30,15 @@ const Layout = ({ children }: PropsWithChildren<{}>) => {
return (
<Scrollbar>
<Container>
<Main>
<SC.Main>
<Sidebar />
<MainContent>
<SC.MainContent>
<h1>{data.site.siteMetadata.title}</h1>
{children}
</MainContent>
</Main>
</SC.MainContent>
</SC.Main>
</Container>
<footer>
<Container>
© {new Date().getFullYear()}, Built with
{` `}
<a href="https://www.gatsbyjs.org">Gatsby</a>
</Container>
</footer>
<Footer />
</Scrollbar>
)
}
export default Layout
+1
View File
@@ -6,6 +6,7 @@ html {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
-webkit-font-smoothing: antialiased;
-43
View File
@@ -1,43 +0,0 @@
import { Link } from "gatsby";
import * as React from "react";
import icon from "./icon.png";
import { HeaderWrapper } from "./styles";
// @todo maybe find alternative type for data
const Navbar = ({ siteTitle }: any) => (
<HeaderWrapper>
<div
style={{
margin: `0 auto`,
maxWidth: 960,
padding: `1.45rem 1.0875rem`,
}}
>
<div style={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
{/* <img src={icon} style={{ width: "70px" }} /> */}
<h1 style={{ margin: 0 }}>
<Link
to="/"
style={{
color: `white`,
textDecoration: `none`,
}}
>
{siteTitle}
</Link>
</h1>
</div>
</div>
</HeaderWrapper>
)
Navbar.propTypes = {
siteTitle: String
}
Navbar.defaultProps = {
siteTitle: ``,
}
export default Navbar;
-6
View File
@@ -1,6 +0,0 @@
import styled from "styled-components";
export const HeaderWrapper = styled.header`
background: #ba1a2e;
marginBottom: 1.45rem;
`;
+23 -9
View File
@@ -3,13 +3,20 @@ import Helmet from "react-helmet"
import { useStaticQuery, graphql } from "gatsby"
interface SEOProps {
readonly description: string;
readonly lang: string;
readonly meta: object[];
readonly title: string;
readonly description?: string
readonly lang?: string
readonly meta?: any[]
readonly keywords?: string[]
readonly title: string
}
const SEO = ({ description = "", lang = "en", meta = [], title }: SEOProps) => {
export function SEO({
description,
lang = "en",
meta = [],
keywords = [],
title,
}: SEOProps) {
const { site } = useStaticQuery(
graphql`
query {
@@ -66,9 +73,16 @@ const SEO = ({ description = "", lang = "en", meta = [], title }: SEOProps) => {
name: `twitter:description`,
content: metaDescription,
},
]}
]
.concat(
keywords.length > 0
? {
name: `keywords`,
content: keywords.join(`, `),
}
: []
)
.concat(meta)}
/>
)
};
export default SEO
}
+3 -5
View File
@@ -1,9 +1,9 @@
import React from "react"
import React, { PropsWithChildren } from "react"
import logo from "../../images/tph-logo.png"
import * as SC from "./styles"
import ArrowRight from "../../icons/arrow-right.svg"
function MenuItem({ children, to }: { children: React.ReactNode; to: string }) {
function MenuItem({ children, to }: PropsWithChildren<{ to: string }>) {
return (
<SC.MenuItem to={to}>
{children} <ArrowRight />
@@ -11,7 +11,7 @@ function MenuItem({ children, to }: { children: React.ReactNode; to: string }) {
)
}
const Sidebar = () => {
export function Sidebar() {
return (
<SC.SidebarWrapper>
<SC.Logo src={logo} />
@@ -27,5 +27,3 @@ const Sidebar = () => {
</SC.SidebarWrapper>
)
}
export default Sidebar
+14 -14
View File
@@ -31,8 +31,8 @@ called `getMembers` that retrieves all the members in a discord server. When we
function we see the following result.
```js
const members = getMembers("The Programmers Hangout");
console.log(members); // Promise {<pending>}
const members = getMembers("The Programmers Hangout")
console.log(members) // Promise {<pending>}
```
Normally, we would have expected to see an array of all the members but it takes time to
@@ -44,8 +44,8 @@ to access the actual members like so.
```js
getMembers("The Programmers Hangout").then(members => {
console.log(members); // (32k) [{...}, {...}, {...}]
});
console.log(members) // (32k) [{...}, {...}, {...}]
})
```
This way we are able to make sure that we only try to `console.log` when the `getMembers` function has resolved and ready to be used.
@@ -63,15 +63,15 @@ You may have tried doing something like this before.
```js
// Incorrect code, don't copy!
let results;
let results
getWeather("Los Angeles").then(weather => {
results = weather;
});
results = weather
})
console.log(results) // undefined
```
Why is `results` undefined? Because **Javascript doesn't wait**. Whenever a Promise is created,
your code will continue to run until there's no more code left in the stack. Only then
your code will continue to run until there's no more code left in the stack. Only then
will javascript try to run the `.then` callback of a Promise. Even if your Promise resolves
immediately you are going to have to wait until you've run all the code in the stack before
your `.then` callback has a chance to start running. This is due to the way the event loop works,
@@ -81,8 +81,8 @@ In order to fix this problem we need to move the `console.log` inside the `.then
```js
getWeather("Los Angeles").then(weather => {
console.log(weather); // Sunny, probably
});
console.log(weather) // Sunny, probably
})
```
## Real World Example
@@ -94,14 +94,14 @@ You can try it in your browser if you want to test it out.
function getCharacters() {
return fetch(`https://rickandmortyapi.com/api/character`)
.then(response => response.json())
.then(response => response.results);
.then(response => response.results)
}
getCharacters().then(characters => {
console.log(characters); // (20) [{...}, {...}, {...}]
});
console.log(characters) // (20) [{...}, {...}, {...}]
})
```
Let's break down what's happening in this function
// TODO: finish this
// TODO: finish this
@@ -15,9 +15,9 @@ function doAsync(number) {
return new Promise(function(resolve, reject) {
doDatabase().then(function(dbResult) {
otherDbFunction(dbResult).then(function(secondResult) {
resolve(secondResult + 10);
resolve(secondResult + 10)
})
});
})
})
}
```
@@ -29,9 +29,9 @@ already returns a promise, you can just return the original thing.
function doAsync(number) {
return doDatabase().then(function(dbResult) {
otherDbFunction(dbResult).then(function(secondResult) {
return secondResult + 10;
return secondResult + 10
})
});
})
}
```
@@ -40,11 +40,13 @@ is that they allow you do chain them sequentially.
```js
function doAsync(number) {
return doDatabase().then(function(dbResult) {
return otherDbFunction(dbResult)
}).then(function(secondResult) {
return secondResult + 10;
});
return doDatabase()
.then(function(dbResult) {
return otherDbFunction(dbResult)
})
.then(function(secondResult) {
return secondResult + 10
})
}
```
@@ -56,17 +58,18 @@ function doAsync(number) {
return doDatabase()
.then(otherDbFunction)
.then(function(secondResult) {
return secondResult + 10;
});
return secondResult + 10
})
}
```
And you don't need those returns if you just have ES6 arrow functions
```js
const doAsync = number => doDatabase()
.then(otherDbFunction)
.then(secondResult => secondResult + 10);
const doAsync = number =>
doDatabase()
.then(otherDbFunction)
.then(secondResult => secondResult + 10)
```
Wow, that last one looks a lot cleaner to me than the first. Keeping that in mind, maybe we could be making some of our other functions cleaner as well
@@ -13,7 +13,6 @@ There are multiple benefits to using a singleton class
- You're always going to pull the same instance of the class
- It's only instantiated once
- Use `$this` in a static-like context
-
By using a singleton instead of a static class we expose a cleaner class to use and we can use regular instance properties instead of static properties.
@@ -28,13 +27,13 @@ namespace Example;
class Singleton
{
/**
* The singleton instance
* @var Singleton
*/
private static $instance;
/**
* Get the instantiated singleton, or create it if it hasn't been instantiated yet.
* @return Singleton
@@ -44,7 +43,7 @@ class Singleton
// static::$instance =?? new static();
// The double ?'s is a null-coalesce operator. There's a link about it below.
static::$instance = static::$instance ?? new static();
return static::$instance;
}
@@ -57,12 +56,12 @@ To give a little functionality to our freshly baked Singleton we add these three
```php
private $word = "Pineapple";
public function getWord(): string {
// PHP_EOL is a constant for a new line (\r\n) or whichever your OS uses.
return $this->word . PHP_EOL;
}
public function setWord(string $word): void {
$this->word = $word;
}
+11 -9
View File
@@ -1,14 +1,16 @@
import React from "react"
import Layout from "../components/Layout"
import SEO from "../components/SEO"
import { Layout } from "../components/Layout"
import { SEO } from "../components/SEO"
const NotFoundPage = () => (
<Layout>
<SEO title="404: Not found" />
<h1>NOT FOUND</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
)
function NotFoundPage() {
return (
<Layout>
<SEO title="404: Not found" />
<h1>NOT FOUND</h1>
<p>You just hit a route that doesn&#39;t exist... the sadness.</p>
</Layout>
)
}
export default NotFoundPage
+10 -8
View File
@@ -1,13 +1,15 @@
import React from "react"
import DocsLayout from "../components/DocsLayout"
import SEO from "../components/SEO"
import { DocsLayout } from "../components/DocsLayout"
import { SEO } from "../components/SEO"
const DocsPage = () => (
<DocsLayout>
<SEO title="Docs" />
the docs
</DocsLayout>
)
function DocsPage() {
return (
<DocsLayout>
<SEO title="Docs" />
the docs
</DocsLayout>
)
}
export default DocsPage
-1
View File
@@ -1 +0,0 @@
# Hello
+14 -13
View File
@@ -1,19 +1,20 @@
import React from "react"
import { Link } from "gatsby"
import Layout from "../components/Layout"
import SEO from "../components/SEO"
import { Layout } from "../components/Layout"
import { SEO } from "../components/SEO"
const IndexPage = () => (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}>
</div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
function IndexPage() {
return (
<Layout>
<SEO title="Home" />
<h1>Hi people</h1>
<p>Welcome to your new Gatsby site.</p>
<p>Now go build something great.</p>
<div style={{ maxWidth: `300px`, marginBottom: `1.45rem` }}></div>
<Link to="/page-2/">Go to page 2</Link>
</Layout>
)
}
export default IndexPage
+3 -3
View File
@@ -1,10 +1,10 @@
import React from "react"
import { graphql } from "gatsby"
import SEO from "../components/SEO"
import DocsLayout from "../components/DocsLayout"
import { SEO } from "../components/SEO"
import { DocsLayout } from "../components/DocsLayout"
// @todo maybe find alternative type for data
const LanguagePost = ({ data }: any) => {
function LanguagePost({ data }: any) {
const { html, frontmatter } = data.file.post
console.log(data)
return (
+1
View File
@@ -1 +1,2 @@
declare module "*.svg"
declare module "*.png"