Skip to content

Commit

Permalink
Merge e458d83 into 196da9d
Browse files Browse the repository at this point in the history
  • Loading branch information
vnglst committed Feb 9, 2020
2 parents 196da9d + e458d83 commit c6d6cc5
Show file tree
Hide file tree
Showing 19 changed files with 261 additions and 97 deletions.
File renamed without changes.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"eslint.enable": true
"eslint.enable": true,
"prettier.useEditorConfig": false
}
2 changes: 1 addition & 1 deletion cypress/integration/finding-nora.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("Basic Happy flow", function() {
.type("MAMAPAPA");

cy.get(".overlay-content")
.contains("Save")
.contains("Add")
.click();

cy.get(".grid-item").each(function($el, index, $list) {
Expand Down
24 changes: 13 additions & 11 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import {
addCorrect,
addAlmost,
addWrong,
addSolution,
restart
addAnswer,
restart,
newGame,
reset
} from "redux/actions";
import "./App.css";

Expand All @@ -38,7 +40,6 @@ export default function App() {
const dispatch: AppDispatch = useDispatch();
const grid = useSelector(state => state.grid);
const remaining = useSelector(state => state.remaining);
const solution = useSelector(state => state.solution);
const solutions = useSelector(state => state.solutions);
const didWin = useSelector(state => state.remaining.length === 0);

Expand Down Expand Up @@ -92,31 +93,31 @@ export default function App() {
<BottomBar.Item
aria-label="New game"
value="new-game"
label="New game"
icon={<FontAwesomeIcon icon={faRedo} />}
/>
<BottomBar.Item
aria-label="Settings"
value="settings"
label="Add name"
icon={<FontAwesomeIcon icon={faCog} />}
/>
<BottomBar.Item
aria-label="About this app"
value="about"
label="About"
icon={<FontAwesomeIcon icon={faInfoCircle} />}
/>
</BottomBar>
{page === "new-game" && (
<NewGamePage
onNavigate={setPage}
restart={() => dispatch(restart())}
/>
<NewGamePage setPage={setPage} restart={() => dispatch(newGame())} />
)}
{page === "settings" && (
<SettingsPage
solution={solution}
addSolution={newSolution => dispatch(addSolution(newSolution))}
addSolution={newSolution => dispatch(addAnswer(newSolution))}
restart={() => dispatch(restart())}
onNavigate={setPage}
reset={() => dispatch(reset())}
setPage={setPage}
/>
)}
{page === "about" && (
Expand All @@ -131,8 +132,9 @@ export default function App() {
<p>YOU WON</p>
<Button
onMouseDown={() => {
dispatch(restart());
dispatch(newGame());
}}
testId="play-again"
>
Play again?
</Button>
Expand Down
33 changes: 19 additions & 14 deletions src/__tests__/App.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
import { applyMiddleware, createStore } from "redux";
import { Provider } from "react-redux";
import { fireEvent, render } from "@testing-library/react";
import { fireEvent, render, queryByTestId } from "@testing-library/react";
import "@testing-library/jest-dom/extend-expect";
import { mockRandomForEach, resetMockRandom } from "jest-mock-random";
import { reducers } from "../redux/reducers";
import { middleware } from "../redux/middleware";
import { audioMiddleware } from "../redux/middleware-audio";
import App from "../App";

function renderWithRedux(
ui,
{ store = createStore(reducers, applyMiddleware(middleware)) } = {}
{ store = createStore(reducers, applyMiddleware(audioMiddleware)) } = {}
) {
return {
...render(<Provider store={store}>{ui}</Provider>),
Expand Down Expand Up @@ -115,7 +115,7 @@ describe("App", () => {

fireEvent.change(input, { target: { value: "TIBO" } });

fireEvent.click(screen.getByText("Save"));
fireEvent.click(screen.getByTestId("settings-add"));

const heading = screen.getByRole("heading");
expect(heading).toHaveTextContent("TIBO");
Expand Down Expand Up @@ -170,7 +170,12 @@ describe("App", () => {
});

it("should be NOT possible to change to a wrong name", async () => {
const { getByLabelText, getByText, getByRole } = renderWithRedux(<App />);
const {
getByLabelText,
getByText,
getByRole,
getByTestId
} = renderWithRedux(<App />);

const button = getByLabelText("Settings");

Expand All @@ -182,31 +187,31 @@ describe("App", () => {

fireEvent.change(input, { target: { value: "TIB" } });

fireEvent.click(getByText("Save"));
fireEvent.click(getByTestId("settings-add"));

expect(getByRole("textbox")).toHaveClass("invalid");

expect(getByText("Save")).toBeDisabled();
expect(getByTestId("settings-add")).toBeDisabled();

expect(getByRole("heading")).toHaveTextContent("NORA");

fireEvent.change(input, { target: { value: "THIS IS TO LONG A NAME" } });

fireEvent.click(getByText("Save"));
fireEvent.click(getByTestId("settings-add"));

expect(getByRole("textbox")).toHaveClass("invalid");

expect(getByText("Save")).toBeDisabled();
expect(getByTestId("settings-add")).toBeDisabled();

expect(getByRole("heading")).toHaveTextContent("NORA");
});

it("should be possible to restart the game", async () => {
const {
getByLabelText,
getByText,
getByTestId,
getAllByText,
queryByText
queryByTestId
} = renderWithRedux(<App />);

const A = getAllByText("A")[0];
Expand All @@ -217,11 +222,11 @@ describe("App", () => {

fireEvent.mouseDown(button);

expect(getByText(/New game/)).toBeInTheDocument();
expect(getByTestId("new-game-resume")).toBeInTheDocument();

fireEvent.mouseDown(getByText("New game"));
fireEvent.mouseDown(getByTestId("new-game-restart"));

expect(queryByText(/New game/)).not.toBeInTheDocument();
expect(queryByTestId(/new-game-resume/)).not.toBeInTheDocument();
expect(getAllByText("A")[0]).not.toHaveClass("orange");
});
});
9 changes: 8 additions & 1 deletion src/components/BottomBarItem.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
color: #022b37;
}

.bottom-bar .label {
padding: 5px 0 0 0;
margin: 0;
font-size: 1em;
color: #022b37;
}

.bottom-bar button {
min-height: 6em;
min-width: 6em;
max-width: 12em;
background: none;
border: none;
padding: 0;
padding: 10px 0 0 0;
margin: 0;
cursor: pointer;
outline: inherit;
Expand Down
2 changes: 1 addition & 1 deletion src/components/BottomBarItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function BottomBarItem({
return (
<button className={classes} onMouseDown={() => onChange(value)} {...other}>
{icon}
{label && <p>{label}</p>}
{label && <p className="label">{label}</p>}
</button>
);
}
3 changes: 3 additions & 0 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,20 @@ interface Props {
onMouseDown?: React.EventHandler<React.MouseEvent<HTMLElement>>;
onTouchStart?: React.EventHandler<React.TouchEvent<HTMLElement>>;
onClick?: React.EventHandler<React.MouseEvent<HTMLElement>>;
testId?: string;
}

export default function Button({
children,
className,
isSecondary,
testId,
...other
}: Props) {
return (
<button
className={cx("button", { secondary: isSecondary }, className)}
data-testid={testId}
{...other}
>
{children}
Expand Down
11 changes: 8 additions & 3 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ import * as ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { applyMiddleware, compose, createStore } from "redux";
import App from "./App";
import { middleware } from "./redux/middleware";
import { reducers } from "./redux/reducers";
import { audioMiddleware } from "./redux/middleware-audio";
import { storageMiddleware, loadState } from "./redux/middleware-storage";
import { reducers, generateNewGame } from "./redux/reducers";
import register from "./registerServiceWorker";
import { BugsnagErrorBoundary } from "./utils/bugsnag";
import "./index.css";
Expand All @@ -17,7 +18,11 @@ const composeEnhancers =

const store = createStore(
reducers,
composeEnhancers(applyMiddleware(middleware))
{
...generateNewGame(),
...loadState()
},
composeEnhancers(applyMiddleware(audioMiddleware, storageMiddleware))
);

export type AppDispatch = typeof store.dispatch;
Expand Down
10 changes: 6 additions & 4 deletions src/pages/NewGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@ import Button from "../components/Button";
import Overlay from "../components/Overlay";

interface Props {
onNavigate: (screen: string) => void;
setPage: (screen: string) => void;
restart: () => void;
}

export default function NewGamePage({ onNavigate, restart }: Props) {
export default function NewGamePage({ setPage, restart }: Props) {
return (
<Overlay>
<Button
onMouseDown={() => {
restart();
onNavigate("home");
setPage("home");
}}
testId="new-game-restart"
>
New game
</Button>
<Button
isSecondary
onMouseDown={() => {
onNavigate("home");
setPage("home");
}}
testId="new-game-resume"
>
Resume game
</Button>
Expand Down
48 changes: 29 additions & 19 deletions src/pages/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,23 @@ const MIN_NAME_LENGTH = 4;
const MAX_NAME_LENGTH = 9;
interface Props {
className?: string;
solution: string;
addSolution: (solution: string) => void;
onNavigate: (screen: string) => void;
setPage: (screen: string) => void;
restart: () => void;
reset: () => void;
}

function isValid(value: string) {
return value.length >= MIN_NAME_LENGTH && value.length <= MAX_NAME_LENGTH;
}

export default function Settings({
solution,
addSolution,
restart,
onNavigate
reset,
setPage: onNavigate
}: Props) {
const [value, setValue] = React.useState(solution);
const [value, setValue] = React.useState("NORA");

return (
<Overlay>
Expand All @@ -34,25 +34,35 @@ export default function Settings({
name="solution"
maxLength={9}
value={value}
placeholder={solution}
placeholder="NORA"
onChange={e => {
const newValue = e.currentTarget.value.toUpperCase();
setValue(newValue);
if (isValid(newValue)) {
// only update solution in redux state if valid
addSolution(newValue);
}
}}
/>
<Button
disabled={!isValid(value)}
onClick={() => {
restart();
onNavigate("home");
}}
>
Save
</Button>
<span>
<Button
disabled={!isValid(value)}
onClick={() => {
addSolution(value);
restart();
onNavigate("home");
}}
testId="settings-add"
>
Add
</Button>
<Button
isSecondary
onClick={() => {
reset();
onNavigate("home");
}}
testId="settings-reset"
>
Reset
</Button>
</span>
</Overlay>
);
}
Loading

0 comments on commit c6d6cc5

Please sign in to comment.