
In this post we will make theNavBar
. In the next post we will make theWriting
component, where the user can write new cards. TheNavBar
will let the user switch betweenAnswering
cards andWriting
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. TheWriting
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 theWriting
scene and theAnswering
scene. TheNavBar
will give the user a button to go to theWriting
scene. TheNavBar
will also give the user a button to go to the Answering scene.
We have not made theNavBar
and theWriting
scene yet. TheApp
just shows theAnswering
scene all the time. TheNavBar
will go inside theApp
. TheWriting
scene will also go inside theApp
. TheApp
will keep track of what to show the user. TheNavBar
will tell theApp
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 theNavBar
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.
Placeholder for the Writing component
File: src/scenes/Writing/index.tsx
Will Match:src/scenes/Writing/complete/index-1.tsx
We haven't madeWriting
yet. But we need to have something to show on the screen when we selectWriting
. 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.
TheWriting
component is one of our 'scenes.' So its folder is src/scenes/Writing.
importReactfrom'react';constWriting=()=><div>Writing</div>exportdefaultWriting;
That's it!
Make the sceneTypes type
File: src/types.ts
Will Match:src/complete/types-5.ts
Add a new enum named 'SceneTypes' insrc/types.ts
:
//defines the scenes that the user can navigate toexportenumSceneTypes{//where the user answers questionsanswering="answering",//where the user writes questionswriting="writing"};
Making the App Keep Track of the Scenes
Right now theApp
just shows theAnswering
scene all the time. But to make the user story possible we need to let the user choose theWriting
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 theApp
component. We'll keep track of what scene the user is looking at withuseState
.
Features
- There is a NavBar
Choose Components
We'll use the customNavBar
that we'll write later in this post
Decide What to Test
Let's test whether theNavBar
shows up.
App Test 1: Has the NavBar
File: src/App.test.tsx
Will Match:src/complete/test-5.tsx
Add a test that checks for theNavBar
. TheNavBar
will have aHeader
with the text 'Flashcard App.'
//shows the NavBarit('shows the NavBar',()=>{const{getByText}=render(<App/>);//the navbar has a header with the words "Flashcard App" in itconstnavBar=getByText(/flashcard app/i);//if we find the header text, we know the NavBar is showing upexpect(navBar).toBeInTheDocument();});
Pass App Test 1: Has the NavBar
File: src/App.tsx
Will Match:src/complete/app-5.tsx
TheApp
component will keep track of which scene to show. We will use theuseState()
hook from React to keep track of which scene to show. TheNavBar
component will let the user choose the scene. TheApp
won't pass the test for showing theNavBar
until later in this post, after we have written theNavBar
and imported it into theApp
.
Import theuseState
hook from React.
importReact,{useState}from'react';
Import theSceneTypes
enum from types.
import{SceneTypes}from'./types/';
Import theWriting
component.
importWritingfrom'./scenes/Writing';
We haven't made theNavBar
yet, so we won't import it. After we make theNavBar
, we will come back to theApp
and add theNavBar
to it.
Change theApp
to this:
constApp: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 theApp
component looks so different now.
Curly Brackets andreturn
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 upuseState
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:
//this would workreturn<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.
//this won't workreturn<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
TheuseState hook gives us a place to keep a variable, and a function to change it.
const[showScene,setShowScene]=useState(SceneTypes.answering);
useState(SceneTypes.answering)
is the call to theuseState
hook.SceneTypes.answering
is the starting value. TypeScript can figure out from this that the type of the variableshowScene
will beSceneTypes
. You can also explicitly declare that you are using a type. Explicit declaration of a type onuseState
looks like this:
useState<SceneTypes>(SceneTypes.answering);
const [showScene, setShowScene]
is the declaration of two const variables,showScene
andsetShowScene
.
showScene
is a variable of typeSceneTypes
. SoshowScene
will either beSceneTypes.answering
orSceneTypes.writing
. Remember when we wrote the enumSceneTypes
earlier?SceneTypes.answering
is the string 'answering' andSceneTypes.writing
is the string 'writing'. The variableshowScene
can only equal one of those two strings.
setShowScene
is a function. It takes one argument. The argument thatsetShowScene
takes is of the typeSceneTypes
. So you can only invokesetShowScene
withSceneTypes.answering
orSceneTypes.writing
. After you invokesetShowScene
, the value ofshowScene
will be set to the value that you passed tosetShowScene
.
We will pass the functionsetShowScene
to theNavBar
. Nothing callssetShowScene
yet. But after we make theNavBar
, we will import it into theApp
. Then we will pass thesetShowScene
function to theNavBar
. TheNavbar
will usesetShowScene
to change the value ofshowScene
in App. When the value ofshowScene
changes, App will change what scene it shows to the user.
Conditional Rendering of Answering and Writing
Conditional Rendering 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.
{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 returntrue or it will returnfalse.
&&
:This is the logical AND operator. 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 theAnswering
component means 'if the condition to the left of&&
is true, show theAnswering
component on the screen.'
There is one conditional rendering expression for each scene.
{showScene===SceneTypes.answering&&<Answering/>}{showScene===SceneTypes.writing&&<Writing/>}
This code means ifshowScene
is 'answering' show theAnswering
component, and ifshowScene
is 'writing' show the Writing component.
You are done with theApp
for now. TheApp
won't pass the test for theNavBar
until later in this post, after we have written theNavBar
and imported it into theApp
.
TheNavBar
Now we are ready to make theNavBar
. Once we have written theNavBar
, we will import it into theApp
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
TheNavBar
is a menu, so we will use theMenu 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
Write a comment for each test.
//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 andafterEach
.
importReactfrom'react';import{render,cleanup,fireEvent}from'@testing-library/react';import'@testing-library/jest-dom/extend-expect';importNavBarfrom'./index';import{SceneTypes}from'../../types';afterEach(cleanup);
Write a helper function to render theNavBar
. The helper function takes an optional prop functionsetShowScene
. We'll use this prop to make sure that theNavBar
calls the functionsetShowScene
when the user clicks the buttons.
constrenderNavBar=(setShowScene?:(scene:SceneTypes)=>void)=>render(<NavBarshowScene={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
NavBar
takes two props.setShowScene
is a function that accepts aSceneType
as a parameter.showScene
is theSceneType
that is currently being shown.
Clicking the Menu Items will invokesetShowScene
with the appropriateSceneType
.
importReactfrom'react';import{Menu}from'semantic-ui-react';import{SceneTypes}from'../../types';constNavBar=({setShowScene,showScene}:{setShowScene:(scene:SceneTypes)=>void,showScene:SceneTypes})=><Menudata-testid='menu'/>exportdefaultNavBar;
NowNavBar
has a menu.
NavBar Test 2: Has a Header
File: src/components/NavBar/index.test.tsx
Will Match:src/components/NavBar/complete/test-2.tsx
If this weren't a tutorial, and you were designing theNavBar
yourself, maybe you wouldn't test ifNavBar
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 theApp
's test checks for theNavBar
by finding its header. So we want to be sure when we testNavBar
that it has a header, so that when we add it to theApp
the tests will pass.
//has a headerit('has a header',()=>{const{getByText}=renderNavBar();constheader=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
Add theMenu.Item
header.
<Menudata-testid='menu'><Menu.Itemheadercontent='Flashcard App'/></Menu>
NavBar Test 3: Answering Button
File: src/components/NavBar/index.test.tsx
Will Match:src/components/NavBar/complete/test-3.tsx
//has a menu item button that loads the answering sceneit('has a button to get you to the answering scene',()=>{const{getByText}=renderNavBar();constanswering=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
Theactive
prop will highlight theMenu Item
when the expression evaluates totrue. ThisMenu Item
will be active when theshowScene
prop isSceneTypes.answering
.
<Menudata-testid='menu'><Menu.Itemheadercontent='Flashcard App'/><Menu.Itemcontent='Answer Flashcards'active={showScene===SceneTypes.answering}/></Menu>
NavBar Test 4: Clicking Answering Button
File: src/components/NavBar/index.test.tsx
Will Match:src/components/NavBar/complete/test-4.tsx
//clicking answer invokes setShowSceneit('clicking answer invokes setShowScene',()=>{constsetShowScene=jest.fn();const{getByText}=renderNavBar(setShowScene);constanswering=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
Add the onClick function to theAnswering
button.
<Menu.Itemcontent='Answer Flashcards'active={showScene===SceneTypes.answering}onClick={()=>setShowScene(SceneTypes.answering)}/>
NavBar Tests 5-6: Writing Button
File: src/components/NavBar/index.test.tsx
Will Match:src/components/NavBar/complete/test-5.tsx
//has a menu item button that loads the writing sceneit('has a button to get you to the writing scene',()=>{const{getByText}=renderNavBar();constwriting=getByText(/edit/i)expect(writing).toBeInTheDocument();});//clicking edit invokes setShowSceneit('clicking edit invokes setShowScene',()=>{constsetShowScene=jest.fn();const{getByText}=renderNavBar(setShowScene);constwriting=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
<Menudata-testid='menu'><Menu.Itemheadercontent='Flashcard App'/><Menu.Itemcontent='Answer Flashcards'active={showScene===SceneTypes.answering}onClick={()=>setShowScene(SceneTypes.answering)}/><Menu.Itemcontent='Edit Flashcards'active={showScene===SceneTypes.writing}onClick={()=>setShowScene(SceneTypes.writing)}/></Menu>
Ok, now we have aNavBar
that passes all the tests! Let's import it into theApp
and show it to the user.
Import NavBar into App
File: src/App.tsx
Will Match:src/complete/app-6.tsx
Now let's import theNavBar
into theApp
. This will makeApp
pass the tests we wrote earlier. It will also make theNavBar
show up on screen. Once the user can see theNavBar
, they will be able to switch between the two scenes. The user will be able to look at theAnswering
scene. The user will also be able to look at theWriting
scene. TheWriting
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 actualWriting
component.
importNavBarfrom'./components/NavBar';
Add theNavBar
component into theApp
.
//rest of app component stays the samereturn(<CardProvider><StatsProvider>//add the NavBar here<NavBarsetShowScene={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 withnpm start
. You will see theAnswering
scene with theNavBar
above it.
Click on 'Edit Flashcards'. You will see the placeholderWriting
scene.
Next Post
In the next post we will make the actualWriting
component.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse
Read next

How to Backup a Drupal Site in 2025?
{η!б€£ $!£¤η€я¤}• -

How to Migrate to Drupal From Another Cms in 2025?
Anna Golubkova -

Developing Hyper-Personalized AI Assistants
Kartik Mehta -

"Mastering Misinformation: Enhancing LLMs with Innovative Techniques"
Gilles Hamelink -