# Team humble
- dan running the UX hype
- judith devving the ops
- kin mastering the scrum
- reggie keeping the quality high

[](https://codecov.io/gh/fac18/humble)
---
## About Us
Welcome to humble. Our goal is to promote community solidarity. We hope to help communities become kinder, more supportive and joyful. This platform has been built to facilitate real-life human interactions that non-transactional in nature.
---
## Platform Interactions

---
## Scrum Master - Kin
---
## Kanban board
* [Trello board](https://trello.com/b/0R23JWfh/kanban)
* Team 0 velocity
* Chores
* Tech spikes
---
## at the end of Sprint 1

---
## Sprint 2 Planning
* Still tech spikes
* User stories
* Estimated 21 velocity
* Actual 21
---
## at the end of Sprint 2

---
## Daily standups, Sprint retro
* Focus
* The Pen Of Talking
* Non technical
* Self-and-team reflection
---
## DevOps - Judith
---
## Stack and Architecture

---
- React frontend (client)
- node/Express backend
- pSQL store accessible to the frontend as an API
- deployment to heroku
---
## Schema
[](https://dbdiagram.io/d/5e2844749e76504e0ef0896f)
---
## UX/UI - Dan
In our [style guide](https://github.com/fac18/humble/issues/2) we define:
- general heuristics
- colours
- typography
- spacing
- styled components
---
### CSS Variables
As declared in the index.css file in _client/src_ i.e. the first CSS to be brought into the app.
#### Colours
```css
/* off white for backgrounds */
--background: #f7fff7;
/* otter brown for text */
--primary: #583e23;
/* leaf green as secondary colour */
--secondary: #ecd444;
/* sand yellow for detail and accenting */
--detail: #bed558;
```
---
#### Spacing
```css
--space-unit: 1em;
--space-xs: calc(0.5 * var(--space-unit));
--space-sm: calc(0.75 * var(--space-unit));
--space-md: calc(1.25 * var(--space-unit));
--space-lg: calc(2 * var(--space-unit));
--space-xl: calc(3.25 * var(--space-unit));
--component-padding: var(--space-sm);
--component-margin: var(--space-xxs);
--border-width: calc(0.5 * var(--space-xxxxs));
```
We did a similar thing with font, including a _--font-unit_ variable.
---
### Styled components

---
They come with some neat tricks, like merging in new props or adjusting existing ones as they get passed through:
```javascript=
import styled from "styled-components";
// styled is a factory with helper methods for all (most?) html tags
// the css is declared immediately adter within backticks `...`, and will be preprocessed by stylis if non-standard
// .attrs is a constructor on these which adjusts or adds attributes to props via merge
// props can then be accessed via interpolating functions within the template literal like ${...}
const P = styled.p.attrs(props => ({
size: props.size || "--font-p"
}))`
color: var(--primary);
font-size: var(${props => props.size});
padding: var(--space-xxxs) var(--component-padding);
margin: var(--space-xxxs) var(--component-margin);
`;
export default P;
```
---
And including a stylis CSS preprocessor to allow self-reference of the component being defined, via the amphersand:
```javascript=
import styled from "styled-components";
// the amphersand & is read by stylin as a reference to the parent component (i.e. Button)
// this is the method by which we apply pseudo-classes to the styled component
// it's also the means by which we might select its siblings (adjacent or otherwise)
// cf. https://styled-components.com/docs/basics#coming-from-css (scroll down to 'Pseudoelements...')
const Button = styled.button`
${"" /* font-size: var(--font-h4); */}
font-weight: 400;
padding: var(--component-padding) var(--space-lg);
margin: var(--component-margin);
border-radius: var(--space-md);
border: var(--border-width) solid var(--detail);
background: none;
&:hover {
background-color: var(--secondary);
}
`;
export default Button;
```
---
They also made for great general use flex containers:
```javascript=
import styled from "styled-components";
const Container = styled.div.attrs(props => ({
direction: props.direction || "column",
justify: props.justify || "space-between",
border: props.border || null,
padding: props.padding || "var(--component-padding)"
}))`
display: flex;
flex-flow: ${props => props.direction} nowrap;
align-items: center;
justify-content: ${props => props.justify};
padding: ${props => props.padding};
border: ${props => props.border};
`;
export default Container;
```
---
## QA - Reggie
---
* TDD
* Travis/Codecov
* Code Uniformity
---
### TDD
* Prioritising tests prior to making the:
* client
* DOM - rendering
* pure functions
* server
* database queries
* routes
---
### client

---
### server

---
### DOM
```javascript=
import React from "react";
import { render } from "@testing-library/react";
import App from "./App";
import { createMemoryHistory } from "history";
import { Router } from "react-router-dom";
test("component works as expected", () => {
const history = createMemoryHistory();
const { getByText } = render(
<Router history={history}>
<App />
</Router>
);
getByText("What is humble?");
});
```
---
### pure fx
```javascript=
import getRequest from "./getRequest";
describe("getRequest function works as expected", () => {
test("/get-member endpoint returns a member", () => {
const mockResponse = {
member_name: "Dan"
};
global.fetch = jest.fn().mockImplementation(() =>
Promise.resolve({
json: () => Promise.resolve(mockResponse)
}).catch(console.error)
);
getRequest("/get-member?member_id=3").then(res => {
expect(res.member_name).toBe("Dan");
});
});
});
```
---
### database queries
```javascript=
const fs = require("fs");
const dbQuery = require("../model/dbQuery");
const schema = fs.readFileSync(`${__dirname}/../model/schema.sql`).toString();
const {
getMember,
getOffer,
getRequest,
categoryList,
searchOfferAll,
searchOfferCategory,
searchRequestAll
} = require("../queries/getData");
beforeEach(() => {
return dbQuery(schema);
});
test("get a specific member from members table", () => {
return getMember(3).then(member => {
expect(member.member_name).toBe("Dan");
});
});
```
---
### routes
```javascript=
const supertest = require("supertest");
const app = require("../app");
let server;
let request;
beforeAll(done => {
server = app.listen(done);
request = supertest(server);
});
afterAll(done => {
server.close(done);
});
test("test get-member endpoint path works", () => {
return request.get("/get-member?member_id=3").then(response => {
expect(response.statusCode).toBe(200);
});
});
```
---
### Travis and Codecov

---
#### CI - travis

```javascript=
language: node_js
node_js:
- "stable"
env:
- NODE_ENV=travis
cache:
directories:
- node_modules
- server/node_modules
- client/node_modules
script:
- cd client && npm i && npm run travis:test -- --coverage --watchAll=false && codecov && cd ../server && npm i && npm run travis:test -- --coverage --watchAll=false && codecov
```
---
#### Coverage - codecov
[](https://codecov.io/gh/fac18/humble)

---
### Code Uniformity
* ES6 format
* <React.Fragment> vs <> vs \<div>
* Prettier & Linter
---
### Accessibility

---
### ES7 React/Redux/GraphQL/React-Native snippets
rfce

---
## Nice things that we did:
---
### Branch naming

---
### Actually reviewing Pull Requests

---
## Technical Spike: React Router
```javascript =
return (
<main className="app">
<Switch>
<Route path="/" exact render={props => <Landing {...props} />} />
<Route path="/about" render={props => <About {...props} />} />
<Route path="/search" render={props => <Search {...props} />} />
<Route
path="/profile"
render={props => (
<Profile
{...props}
memberName={memberName}
memberAvatar={memberAvatar}
memberEmail={memberEmail}
memberPostcode={memberPostcode}
memberOffers={memberOffers}
memberRequests={memberRequests}
/>
)}
/>
<Route render={props => <Error />} />
</Switch>
</main>
);
}
```
---
## Technical Spike: React Router
```javascript=
function Navbar() {
return (
<NavContainer direction="row" justify="space-around">
<Link to="/profile">
<NavIcon>
<i className="fas fa-user" />
</NavIcon>
</Link>
<Link to="/search">
<NavIcon>
<i className="fas fa-search" />
</NavIcon>
</Link>
<Link to="/about">
<NavIcon>
<i className="fas fa-info" />
</NavIcon>
</Link>
<Link to="/">
<NavIcon>
<i className="fas fa-external-link-alt" />
</NavIcon>
</Link>
</NavContainer>
);
}
```
---
## Technical Spikes: Google Maps API
```javascript=
const Map = ({ mapCentre, activeCategory, toggleShare, cards }) => {
const googleMapRef = createRef();
const googleMap = useRef(null);
const marker = useRef(null);
useEffect(() => {
const googleMapScript = document.createElement("script");
googleMapScript.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`;
window.document.body.appendChild(googleMapScript);
googleMapScript.addEventListener("load", () => {
googleMap.current = createGoogleMap(googleMapRef, 13, mapCentre);
cards.forEach(member => {
if (activeCategory) {
if (member.category_id === activeCategory)
createMapMarker(googleMap, member.postcode);
} else {
createMapMarker(googleMap, member.postcode);
}
});
});
}, [activeCategory, toggleShare, mapCentre, cards]);
return <div id="google-map" ref={googleMapRef} style={mapStyles} />;
};
```
---
## If we had more time?
* Authentication
* JWT cookie
* Interactions history
* Feedback and comments
{"metaMigratedAt":"2023-06-15T03:53:13.626Z","metaMigratedFrom":"Content","title":"Team humble","breaks":true,"contributors":"[{\"id\":\"5ae2520e-a235-4412-9708-bef9037d4b47\",\"add\":3761,\"del\":1469},{\"id\":\"1a1a3fa3-501b-4210-a70b-c7f45079c34a\",\"add\":5397,\"del\":1180},{\"id\":\"7ad725eb-1bf0-4249-9695-68fbe998367c\",\"add\":1432,\"del\":680},{\"id\":\"b4645149-fe35-4695-95c6-3355ab3f4fc8\",\"add\":11532,\"del\":7872}]"}