NavBar
Tags: react, typescript, testing, tutorial
Published: 2020-01-17 04:47:57

In this post we will make the `NavBar`. In the next post we will make the `Writing` component, where the user can write new cards. The `NavBar` will let the user switch between `Answering` cards and `Writing` cards.

##User Story

* The user thinks of a new card. The user opens the card editor. The user clicks the button to create a new card. The user writes in the card subject, question prompt, and an answer to the question. The user saves their new card.

This user story has a lot of things going on. To make this user story possible we will need to make new component where the user can write cards. The `Writing` component will be a new 'scene' in the application. We will also need to give the user a way to get to the Writing scene. 

Let's make a NavBar (Navigation Bar) component to give the user a way to choose between the two scenes. The two scenes will be the `Writing` scene and the `Answering` scene. The `NavBar` will give the user a button to go to the `Writing` scene. The `NavBar` will also give the user a button to go to the Answering scene. 

We have not made the `NavBar` and the `Writing` scene yet. The `App` just shows the `Answering` scene all the time. The `NavBar` will go inside the `App`. The `Writing` scene will also go inside the `App`. The `App` will keep track of what to show the user. The `NavBar` will tell the `App` when the user wants to see a different scene. 

In this post, we will
* Make a placeholder for the Writing component
* Write a typescript enum for the different scenes
* Change the `App` component to keep track of what scene to show the user
* Make the `NavBar` component
* Show the `NavBar` component to the user

By the end of this post the `NavBar` component will show up on the screen and let the user choose between looking at the Answering component and the Writing component. In the next post we will actually make the real Writing component.

Here's the NavBar in action:
![NavBar2](https://thepracticaldev.s3.amazonaws.com/i/dg91p85x7nfiu8gwiwub.gif)

##Placeholder for the Writing component
>File: src/scenes/Writing/index.tsx
>Will Match: [src/scenes/Writing/complete/index-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/scenes/Writing/complete/index-1.tsx)

We haven't made `Writing` yet. But we need to have something to show on the screen when we select `Writing`. So we are going to make a placeholder component. This will just be a div with the word 'writing' in it. Because this is a placeholder we aren't going to take the time to write tests first.

The `Writing` component is one of our 'scenes.' So its folder is src/scenes/Writing.


```javascript
import React from 'react';

const Writing = () => <div>Writing</div>

export default Writing;
```
That's it!

##Make the sceneTypes type
>File: src/types.ts
>Will Match: [src/complete/types-5.ts](https://github.com/jacobwicks/flashcard/blob/master/src/complete/types-5.ts)

Add a new enum named 'SceneTypes' in `src/types.ts`:
```javascript
//defines the scenes that the user can navigate to
export enum SceneTypes {
    
    //where the user answers questions
    answering = "answering",

    //where the user writes questions
    writing = "writing"
};
```

##Making the App Keep Track of the Scenes
Right now the `App` just shows the `Answering` scene all the time. But to make the user story possible we need to let the user choose the `Writing` scene. We need to keep track of what scene the user is looking at. We are going to keep track of what scene the user is looking at inside the `App` component. We'll keep track of what scene the user is looking at with `useState`. 

###Features
* There is a NavBar

###Choose Components
We'll use the custom `NavBar` that we'll write later in this post

###Decide What to Test
Let's test whether the `NavBar` shows up.

##App Test 1: Has the NavBar
>File: src/App.test.tsx
>Will Match: [src/complete/test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/test-5.tsx)

Add a test that checks for the `NavBar`. The `NavBar` will have a `Header` with the text 'Flashcard App.'

```javascript
//shows the NavBar
it('shows the NavBar', () => {
  const { getByText } = render(<App/>);

  //the navbar has a header with the words "Flashcard App" in it
  const navBar = getByText(/flashcard app/i);

  //if we find the header text, we know the NavBar is showing up
  expect(navBar).toBeInTheDocument();
});
```

##Pass App Test 1: Has the NavBar
>File: src/App.tsx
>Will Match: [src/complete/app-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/app-5.tsx)

The `App` component will keep track of which scene to show. We will use the `useState()` hook from React to keep track of which scene to show. The `NavBar` component will let the user choose the scene. The `App` won't pass the test for showing the `NavBar` until later in this post, after we have written the `NavBar` and imported it into the `App`.

Import the `useState` hook from React.

```javascript
import React, { useState } from 'react';
```

Import the `SceneTypes` enum from types.
```javascript
import { SceneTypes } from './types/';
```

Import the `Writing` component.
```javascript
import Writing from './scenes/Writing';
```

We haven't made the `NavBar` yet, so we won't import it. After we make the `NavBar`, we will come back to the `App` and add the `NavBar` to it.

Change the `App` to this:
```javascript
const App: React.FC = () => {
  
const [showScene, setShowScene] = useState(SceneTypes.answering);

  return (
    <CardProvider>
      <StatsProvider>
        {showScene === SceneTypes.answering && <Answering />}
        {showScene === SceneTypes.writing && <Writing/>}
      </StatsProvider>
    </CardProvider>
  )};
```
Here's why the code for the `App` component looks so different now.

###Curly Brackets and `return`
Before these changes the App function just returned JSX. The App had a 'concise body.' A function with a concise body only has an expression that gives the return value. But now we have added an expression before the expression that gives the return value. The new expression sets up `useState` to track what scene to show. Because we have added an expression besides the return value to the function, we have to add curly brackets so the compiler knows to look for expressions and not just a return value. This is called a function with a 'block body.'

###return()
This is the return method of your function. This tells the function to return the value inside the parentheses. The parentheses are not required. But if you don't have the parentheses, you have to start your JSX on the same line. So it would look like this:
```javascript
//this would work
return <CardProvider>
      <StatsProvider>
        {showScene === SceneTypes.answering && <Answering />}
        {showScene === SceneTypes.writing && <Writing/>}
      </StatsProvider>
    </CardProvider>;
```
But if you don't have parentheses, starting your JSX return value on the next line will not work.
```javascript
//this won't work
return 
    <CardProvider>
      <StatsProvider>
        {showScene === SceneTypes.answering && <Answering />}
        {showScene === SceneTypes.writing && <Writing />}
      </StatsProvider>
    </CardProvider>;
```

I think it is easier to read with the return value starting on the next line. So I put parentheses around the return value.

##UseState
The [useState](https://reactjs.org/docs/hooks-state.html) hook gives us a place to keep a variable, and a function to change it. 

```javascript
const [showScene, setShowScene] = useState(SceneTypes.answering);
``` 

`useState(SceneTypes.answering)` is the call to the `useState` hook. `SceneTypes.answering` is the starting value. TypeScript can figure out from this that the type of the variable `showScene` will be `SceneTypes`. You can also explicitly declare that you are using a type. Explicit declaration of a type on `useState` looks like this:
```javascript
useState<SceneTypes>(SceneTypes.answering);
``` 

`const [showScene, setShowScene]` is the declaration of two const variables, `showScene` and `setShowScene`. 

`showScene` is a variable of type `SceneTypes`. So `showScene` will either be `SceneTypes.answering` or `SceneTypes.writing`. Remember when we wrote the enum `SceneTypes` earlier? `SceneTypes.answering` is the string 'answering' and `SceneTypes.writing` is the string 'writing'. The variable `showScene` can only equal one of those two strings.

`setShowScene` is a function. It takes one argument. The argument that `setShowScene` takes is of the type `SceneTypes`. So you can only invoke `setShowScene` with `SceneTypes.answering` or `SceneTypes.writing`. After you invoke `setShowScene`, the value of `showScene` will be set to the value that you passed to `setShowScene`.

We will pass the function `setShowScene` to the `NavBar`. Nothing calls `setShowScene` yet. But after we make the `NavBar`, we will import it into the `App`. Then we will pass the `setShowScene` function to the `NavBar`. The `Navbar` will use `setShowScene` to change the value of `showScene` in App. When the value of `showScene` changes, App will change what scene it shows to the user.

##Conditional Rendering of Answering and Writing
[Conditional Rendering](https://www.robinwieruch.de/conditional-rendering-react) is how you tell React that if some condition is true, you want to show this component to the user. Rendering a component means showing it to the user.

```javascript
        {showScene === SceneTypes.answering && <Answering />}
```
`{}`: The curly brackets tell the compiler that this is an expression. The compiler will evaluate the expression to figure out what value it has before rendering it to the screen.

`showScene === SceneTypes.answering`: this is an expression that will return a boolean value. It will return *true* or it will return *false*.

`&&`: [This is the logical AND operator](https://www.robinwieruch.de/conditional-rendering-react#logical--operator-in-react). It tells the compiler that if the condition to the left of it is true, it should evaluate and return the expression to the right.

`&& <Answering/>`: The logical && operator followed by the JSX for the `Answering` component means 'if the condition to the left of `&&` is true, show the `Answering` component on the screen.'

There is one conditional rendering expression for each scene.
```javascript
        {showScene === SceneTypes.answering && <Answering />}
        {showScene === SceneTypes.writing && <Writing/>}
```
This code means if `showScene` is 'answering' show the `Answering` component, and if `showScene` is 'writing' show the Writing component.

You are done with the `App` for now. The `App` won't pass the test for the `NavBar` until later in this post, after we have written the `NavBar` and imported it into the `App`.

#The `NavBar`
Now we are ready to make the `NavBar`. Once we have written the `NavBar`, we will import it into the `App` so it shows up on screen and lets the user choose which scene they want to see.

##Features
* The user can click a button to go to the `Writing` scene
* The user can click a button to go to the `Answering` scene

##Choose Components
The `NavBar` is a menu, so we will use the [Menu](https://react.semantic-ui.com/collections/menu/) component from Semantic UI React. 

##Decide What to Test
* menu
* header
* button loads `Answering`
* button loads `Writing`

##Write the tests
>File: src/components/NavBar/index.test.tsx
>Will Match: [src/components/NavBar/complete/test-1.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/test-1.tsx)

Write a comment for each test.
```javascript
//has a menu component
//has a header
//has a menu item button that loads the answering scene
//clicking answer invokes setShowScene
//has a menu item button that loads the writing scene
//clicking edit invokes setShowScene
```

Imports and `afterEach`.
```javascript
import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import NavBar from './index';
import { SceneTypes } from '../../types';

afterEach(cleanup);
```

Write a helper function to render the `NavBar`. The helper function takes an optional prop function `setShowScene`. We'll use this prop to make sure that the `NavBar` calls the function `setShowScene` when the user clicks the buttons.
 
```javascript
const renderNavBar = (setShowScene?: (scene: SceneTypes) => void) => render(
    <NavBar 
    showScene={SceneTypes.answering} 
    setShowScene={setShowScene ? setShowScene : (scene: SceneTypes) => undefined}
    />);
```

##NavBar Test 1: Has a Menu
>File: src/components/NavBar/index.tsx
>Will Match: [src/components/NavBar/complete/index-1.tsx]
(https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/index-1.tsx)

`NavBar` takes two props. `setShowScene` is a function that accepts a `SceneType` as a parameter. `showScene` is the `SceneType` that is currently being shown.

Clicking the Menu Items will invoke `setShowScene` with the appropriate `SceneType`.

```javascript
import React from 'react';
import { Menu } from 'semantic-ui-react';
import { SceneTypes } from '../../types';

const NavBar = ({
    setShowScene,
    showScene
}:{
    setShowScene: (scene: SceneTypes) => void,
    showScene: SceneTypes
}) => <Menu data-testid='menu'/>

export default NavBar;
```
Now `NavBar` has a menu.

![Has a Menu](https://thepracticaldev.s3.amazonaws.com/i/hleq33iq3cy0rxln2bvx.png)

##NavBar Test 2: Has a Header
>File: src/components/NavBar/index.test.tsx
>Will Match: [src/components/NavBar/complete/test-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/test-2.tsx)

If this weren't a tutorial, and you were designing the `NavBar` yourself, maybe you wouldn't test if `NavBar` has a header. You might decide that the header on the NavBar is not an important enough feature to test. The reason we are testing for the header is that the `App`'s test checks for the `NavBar` by finding its header. So we want to be sure when we test `NavBar` that it has a header, so that when we add it to the `App` the tests will pass. 

```javascript
//has a header
it('has a header', () => {
    const { getByText } = renderNavBar();
    const header = getByText(/flashcard app/i);
    expect(header).toBeInTheDocument();
});
```

##Pass NavBar Test 2: Has a Header
>File: src/components/NavBar/index.tsx
>Will Match: [src/components/NavBar/complete/index-2.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/index-2.tsx)

Add the `Menu.Item` header.
```javascript
    <Menu data-testid='menu'>
        <Menu.Item header content='Flashcard App'/>
    </Menu>
```
![Header Passes](https://thepracticaldev.s3.amazonaws.com/i/vjtvn73icfl6o81yw0il.png)

##NavBar Test 3: Answering Button
>File: src/components/NavBar/index.test.tsx
>Will Match: [src/components/NavBar/complete/test-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/test-3.tsx)


```javascript
//has a menu item button that loads the answering scene
it('has a button to get you to the answering scene', () => {
    const { getByText } = renderNavBar();
    const answering = getByText(/answer/i)
    expect(answering).toBeInTheDocument();
});
```

##Pass NavBar Test 3: Answering Button
>File: src/components/NavBar/index.tsx
>Will Match: [src/components/NavBar/complete/index-3.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/index-3.tsx)

The `active` prop will highlight the `Menu Item` when the expression evaluates to *true*. This `Menu Item` will be active when the `showScene` prop is `SceneTypes.answering`.

```javascript
    <Menu data-testid='menu'>
        <Menu.Item header content='Flashcard App'/>
        <Menu.Item content='Answer Flashcards' 
            active={showScene === SceneTypes.answering}/>
    </Menu>
```
![Answer Button](https://thepracticaldev.s3.amazonaws.com/i/c00cxaaiqzkuwiqq7ton.png)


##NavBar Test 4: Clicking Answering Button
>File: src/components/NavBar/index.test.tsx
>Will Match: [src/components/NavBar/complete/test-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/test-4.tsx)

```javascript
//clicking answer invokes setShowScene
it('clicking answer invokes setShowScene', () => {
    const setShowScene = jest.fn();
    const { getByText } = renderNavBar(setShowScene);
    const answering = getByText(/answer/i)

    fireEvent.click(answering);
    expect(setShowScene).toHaveBeenLastCalledWith(SceneTypes.answering);
});
```
##Pass NavBar Test 4: Clicking Answering Button
>File: src/components/NavBar/index.tsx
>Will Match: [src/components/NavBar/complete/index-4.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/index-4.tsx)

Add the onClick function to the `Answering` button.

```javascript
 <Menu.Item content='Answer Flashcards' 
            active={showScene === SceneTypes.answering}
            onClick={() => setShowScene(SceneTypes.answering)}/>
```
![onClick](https://thepracticaldev.s3.amazonaws.com/i/suzr0mv3v9kh4qr0gwt1.png)

##NavBar Tests 5-6: Writing Button
>File: src/components/NavBar/index.test.tsx
>Will Match: [src/components/NavBar/complete/test-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/test-5.tsx)

```javascript
//has a menu item button that loads the writing scene
it('has a button to get you to the writing scene', () => {
    const { getByText } = renderNavBar();
    const writing = getByText(/edit/i)
    expect(writing).toBeInTheDocument();
});

//clicking edit invokes setShowScene
it('clicking edit invokes setShowScene', () => {
    const setShowScene = jest.fn();
    const { getByText } = renderNavBar(setShowScene);
    const writing = getByText(/edit/i)

    fireEvent.click(writing);
    expect(setShowScene).toHaveBeenLastCalledWith(SceneTypes.writing);
});
```
##Pass NavBar Tests 5-6: Writing Button
>File: src/components/NavBar/index.tsx
>Will Match: [src/components/NavBar/complete/index-5.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/components/NavBar/complete/index-5.tsx)

```javascript
    <Menu data-testid='menu'>
        <Menu.Item header content='Flashcard App'/>
        <Menu.Item content='Answer Flashcards' 
            active={showScene === SceneTypes.answering}
            onClick={() => setShowScene(SceneTypes.answering)}/>
        <Menu.Item content='Edit Flashcards'
            active={showScene === SceneTypes.writing}
            onClick={() => setShowScene(SceneTypes.writing)}/>
    </Menu>
```

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/6m9tdxtnn0ogxh0lraoj.png)

Ok, now we have a `NavBar` that passes all the tests! Let's import it into the `App` and show it to the user.

##Import NavBar into App
>File: src/App.tsx
>Will Match: [src/complete/app-6.tsx](https://github.com/jacobwicks/flashcard/blob/master/src/complete/app-6.tsx)

Now let's import the `NavBar` into the `App`. This will make `App` pass the tests we wrote earlier. It will also make the `NavBar` show up on screen. Once the user can see the `NavBar`, they will be able to switch between the two scenes. The user will be able to look at the `Answering` scene. The user will also be able to look at the `Writing` scene. The `Writing` scene that the user can see will be the placeholder that you wrote earlier in this post. In the next post we will make the actual `Writing` component. 

```javascript
import NavBar from './components/NavBar';
```

Add the `NavBar` component into the `App`.
```javascript
//rest of app component stays the same
  return (
    <CardProvider>
      <StatsProvider>
//add the NavBar here
        <NavBar setShowScene={setShowScene} showScene={showScene} />
        {showScene === SceneTypes.answering && <Answering />}
        {showScene === SceneTypes.writing && <Writing/>}
      </StatsProvider>
    </CardProvider>
  )};
```

Save the App. Most of the tests will pass, but the snapshot test will fail because you have changed what shows up on the screen. Update the snapshot by pressing 'u'. Now all tests should pass.

Run the app with `npm start`. You will see the `Answering` scene with the `NavBar` above it.

![NavBar1](https://thepracticaldev.s3.amazonaws.com/i/cnoj7nu2ak1nt7lp7xab.png)

Click on 'Edit Flashcards'. You will see the placeholder `Writing` scene.

![NavBar2](https://thepracticaldev.s3.amazonaws.com/i/dg91p85x7nfiu8gwiwub.gif)
Great job! 

####Next Post 
In the next post we will make the actual `Writing` component.