React by Example: Build a Calculator with Typescript

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right i think we're live um my name is kevin this is my first ever live stream and uh thanks for joining me uh go easy on me today uh today we're going to build a calculator with react and typescript i would say this is probably like an intermediate level video i won't be teaching you react or teaching you typescript today so i'm going to assume you have a little bit of familiarity with those but if you do then this will be a great walkthrough together everything will be live so warts and all i i always find it very helpful to watch other developers work and so uh this will be uh we'll do this together see how it goes so let me cut over to my screen here so this is uh really all that i've done so far is i've kind of mocked up what we're going to build today and we're going to build this calculator interface with react and all of the logic that would be required to make this function so we're going to have a fairly typical number keypad i have dialed back the functionality of a calculator so far so we're not going to have decimal places and we're not going to do multiplication or division just addition and subtraction today but we'll have the number keypad we'll have our operations over here we have an ac which i had to look that up that's an all clear button and then i have this idea of a function i'd actually like to add to the calculator called oops anytime you i'm sure you used a calculator and you hit the wrong button and you're not even really sure what the which clear button does what uh oops is going to uh undo the very last button press that you had um and then we have our display up top a little bit about how we're going to build this is we're going to use a very functional style and for the most part an immutable data style of programming with typescript javascript of course has no uh safe immutability but we're going to use a functional style as much as possible so here is our very detailed plan of attack first thing i'm going to do is build out the ui with react so that'll be all react all ui uh then we'll totally switch gears and build the calculator logic or brain uh using typescript and then we'll hook it all together depending on how long this takes i may split this into multiple videos or multiple streams but we'll see how it goes so let's dive in alrighty so i am going to use a create react app to build this up so i'll do an mpx which will help me execute that package i'll do create react app and here's a here's the first tip for you i always find that npx sometimes uses the version cached on my machine which isn't always the most up-to-date version so i will oftentimes do npx package name at latest so that it forces it to fetch the latest version if that's not the same one on my machine so i'm going to do a will be the project name and we'll do dash dash type script i hope that is still the flag for making this typescript project so here we go i must have had the latest version there uh and it's installing let me see if the stream is still working looks like um to build up the ui we're going to use css grid i'm i'm very familiar uh with flexbox and i'm pretty proficient in flexbox grid is newer to me but it just makes a lot of sense for this sort of calculator interface that's very grid like so we're going to try to use a grid for this and the strategy here is i'm going to create a grid for everything except for the display because this is a you know essentially one two three four by five grid four columns and five rows and then the display since i've made it a little bigger we're just going to do that outside of the grid and stack those on top of each other so let's see we're here okay awesome so let's uh open up let me go into my directory and open this up in vs code and we will start this yarn start and make sure we're running here pull that up okay and we're running and with the latest version of react um 17 has an even faster hot reloading functionality in it i forgot what that's called it's like fast loading or fast reloading or something and so that'll be really nice for this as we build out the ui so let me install um a package here i'm going to install style components to use for styling so i'll do yarn add styled components there we go and i'll just restart the server for good measure all right so let's go into our app file and start building this out now the first problem i see is i must not have used the right flag to generate a typescript project so because i have all javascript files in here so create react app type script template type script okay we'll start that over we'll blow away our calculator directory here and try that again i told you this was live okay calculator template typescript yes let's try that do doo doo okay it is for sure typescript now i'm having coffee midday here we go excellent okay cd calculator open it up in bs code and i will start my server now we have typescript files yes i'm gonna wait for this page to fully load make sure everything's good excellent okay so let's just edit something calculator hit save excellent that's what we want to see okay i'm going to blow away all of this we don't need the logo uh i might keep that class name i don't know why that's uh it's not liking my jsx that's going to be annoying let me restart vs code hopefully that fixes it here okay so let us build out this calculator interface let me bring in figma somehow bring that down here pigment doesn't go very small okay we'll leave that here so i think what i will do is i'm going to go ahead and leave the app component pretty lean here and call in a calculator component that will house this calculator as a whole and then i'll probably create a button component um to render all these buttons and i did pull up a cheat sheet for myself as far as css grid goes because i'm not as proficient in that uh just so we can make sure we have all the proper properties okay so i'm going to make a new file and we're on one really cool trick in vs code if you don't know is you can make folders and files at the same time so i'm going to make my components folder and then you can just do a slash and make a calculator.tsx great so i will um actually have i have snippets set up but they're under javascript so i'll type all this out um so import react from react okay great and we'll do a calculator and i'm going to go ahead and start typing this a little bit so react function component uh we're not gonna we'll add props as we go here and then we'll return just a div that says calculator component just so we can make sure that this is working and then from here i'm going to import that component i need to export it on the previous page i don't know why that is yelling at me argument for jsx must be preserve react native react hmm i wonder if i make this react if it will be happy with me argument for jsx must be preserve react native react no quick fixes that's weird okay since i changed this i am going to restart vs code one more time uh this is what happens when you try to do things live i guess i will import that and save before i go now that's not complaining at me and this isn't either okay i won't restart anything okay we have our main app component okay we're going now we have our main app component it is essentially just bringing in calculator just so i can keep the app component clean then we have our calculator component and that is rendering calculator comp into the screen so now we can start uh implementing the ui here so i'm kind of glad that i've mocked this up a little bit because now i can go in here and just pull colors and whatnot uh as we go so the first thing i will do is let's build up some sort of container and i do have a okay i don't have a snippet for this and i'm going to be adding style component components all along the way so let's go in and create a snippet for this i have one that i use in javascript quite a bit and so i want to copy that same thing into typescript typescript react let me open up my javascript on first and here's the one that i want to use and let me pull up uh snippets configure typescript react uh and then add in this snippet so what this snippet is going to do is if i type in sty for like the beginning of style uh then it will add this line const and then a placeholder i can type in a name style.div and bring me into if i hit tab bring me down to the next line so let me save that and i will demonstrate that here so we just created a snippet for sty so i can hit tab that's inserted our code here and so i'm going to do container container is just typically the name i use for a um the top most component or topmost div within a component so we have container here and i'll do background and i will insert this color now i just remember that since we had to reinitialize create react app to get typescript working i haven't installed style components yet so let me flip over to my terminal and do a yarn ad styled components and i'll restart my server for good measure that may not be required in create react app i'm not sure um and then i'm going to import styled from styled components and we will add container as our containing element and jsx is mad at me again that flips back see if we can get this working here okay that's calculated let me save this uh cannot find declaration file for module styled components alright so this happens in typescript if the package wasn't natively created in typescript or if they didn't add type declarations within their package so what you can do for most popular packages out there is also add a package from a group or a repo called definitely typed and so we'll do yarn add at types slash the name of our package style components and i really just need this in development so i will do a dash dash dev here and maybe this time we'll test if we don't have to restart create react app server um i think i will uh well let me save okay that did work okay excellent okay so we've initialized create react app for typescript we've installed style components we've installed at slash style components i have jiggled the wire on typescript here uh i'm not sure what's going on here i'd probably have to research that later but it's not complaining at me right now so let us keep building since this is uh let's see so this is calculator component um [Music] i'm thinking about how to get this uh completely uh filling the browser here what i'm gonna do is i'm going to set this calculator component to a flex of one and i'm going to assume that it's going to be inserted in a flexbox environment so what do i mean by that if i pull up my app component here i am also going to create a component within the app component called container which will be my main global container for the whole app and so i'm going to make this a display of flex and i'm going to manually set the height and width on this um so i will do actually min height of 100 vh that's 100 of the viewport height and i'm going to set the width to be 100 vw and we'll add in we'll import styles there we will wrap the calculator component in our container component and we got mostly there okay um maybe i don't need the width at all oh yes i do um okay so what's happening here is this is fine what's happening here is uh it's um not stretching in the proper way so i want to make sure that the flex direction here is actually a column i'm going to comment out with for now i'm going to make sure that the flex direction is a column because any time i'm adding components to this container i want that to go from top to bottom so i'm going to make sure this is a column hit save here it flipped back it looks visually like it did at the beginning but we're going to fix that here shortly so if i add components to this layout then we have they will add in on top of each other so let me inspect this and let's see if we can make a little sense of this so we have our main div this is our app container div and this is as you can see filling the screen and then if we go here then this is not filling the screen and i actually think what i what happened is i didn't save with the flex one here so now with the flex 1 this means that it will take up all of the space since it is the only component within this container um there are a lot more things to say about that but this is not a flexbox video so we're just going to continue on there let's start building up our buttons these buttons are 80 that one's different these buttons are 80 by 80 by default so what we're going to do is in the calculator um in the calculator component here we're going to make this calculator uh a grid but like i said we're only going to do the grid for all of the buttons here the display we're going to do a little bit differently so i'm going to create a component called grid and we'll do a display grid and start adding in buttons there so let me go ahead so that we can see something let me go ahead and create a calculator button component here this is very simple app so we're just going to call this button import react from react i should probably stop and make a snippet for this but we won't right now we will make this a button tag but i'm going to style it so let's do a styled button and then for the contents of this button you could do this two ways you could either pass in children and add the children for this um or you could make a prop for this that's going to be a stylistic decision on your part i think what i'm going to do here is create a prop called label and add that in here so i'm going to make sure that this is a button i'm going to make sure that styled components is imported and uh for now we'll get to props in a moment but let's uh let's just add this as one and then we will export default button there we go and i'm going to drag this below so we can keep that visible right now i'm going to hide my left panel so let us in our main calculator component here let's add a grid by the way i use prettier uh for uh formatting uh quite liberally so sometimes i'll just type a mess and hit save and it'll reformat for me then let me add a button that will import for me and we don't need any props right now so let me save that will reformat nicely and we should be on our way excellent here we go so great there we go so calculator component we don't need this label anymore i'm going to get rid of that and we're going to be left with this one button now there are a couple of different things here you might initially think well i will set the button width and height to 80 pixels here but i think that with the grid um since we're going to be defining the size of the grid in the grid itself i think we won't have to actually attach a size to the button component itself so let me go into the grid here and this is where i'm going to pull in my grid cheat sheet i pulled up before we started here and essentially to start a grid you need two things you need um display grid uh and then you need to i well technically that may be the only thing you need but we're going to make sure that we are defining how big each of our rows and columns are and then we need to manually say where the button placement goes so we'll take this in a couple of different steps let's first uh add in uh the sizing for our columns like i said uh the buttons are 80 by 80. so i'm going to program that into the grid here to make each cell of the grid 80 by 80. and since that's going to repeat there's a really nice function and grid for repeating things so if i look at my design we have four columns one two three four and then you know don't count the display right now we have one two three four five rows so that's what we're going to do so up in our grid we're going to say grid template columns will repeat uh what did i say 4 columns of 80 pixels i hope this works and then for rows same thing but there will be five rows so if i hit save will this work yes okay excellent excellent okay so that's a button one let me add a couple of buttons in here one two three four five six seven eight nine if i hit save then there we go we're getting closer we're starting to have a grid of buttons here um this is exciting alrighty let's uh do something simple let's add in a label here and like i said you could make this the children of the button um i think in my case i i'm going to add in a label prop so i can specify what i'm wanting here i think just stylistically in the code too i might prefer to see an attribute called label here but again that would be up to you so the first thing i'm going to do is i'm going to define this in the button component and since we're using typescript i'm going to make sure that we're adding the proper prop types for that so i'm going to create a type in button called props it doesn't really need to be a complicated name you could call it button props but i'm going to name this all props for now and let's add a label and make it a string and now that we have this type we can pass that into react.function component to say that these are the props we are expecting for this component and you can see instantly when i did that all of these buttons up here in my calculator component turn red because they're no longer uh they're no longer valid they're not passing in all of the proper and expected types so let me let me finish this up down here so let's pull in our label we will display that here and that's all i need in the button component now we need to add in these labels up here in our calculator grid so i'm going to do on mac it's option select or option drag and do a column select here so i can type in label everywhere and we will go down and add in all of the numbers and i'm doing these in reverse order kind of like is on your number pad hit save here fail okay oh let me save all i didn't save the button component uh there we go okay so we have nine buttons they're not in the right order they're not in the right place that's okay we're getting closer this is looking better let me style these real fast the buttons themselves just to make things a little prettier and then we'll go and worry about actually placing these in the proper place on the grid so let me come in here and pull whatever random orange i chose earlier uh that's on the rectangle and we have a couple of different button colors here we have two um we'll worry about that later too so let me pull in my button here the color we'll do the background color of our button will be this color and then we also have a border radius on those and let me see what that is border radius of 8 pixels great let me do a border radius of 8 pixels now by default uh the the browser brings in button styling so that's why we have these borders on here and in fact i'll um that is my dev tools if i zoom in on this if you couldn't see before you can see that i have all these borders on my buttons here so let me do first a border of none and then immediately in the line uh well radius doesn't overwrite that but i'm gonna do a border of none so we can get rid of all those borders excellent now let's deal with the space in between these some of you might immediately think to go to margin because that is the space um that's the space outside of a box however since we're using a grid here css grid they have an awesome attribute called gap and gaap to me is probably one of the most exciting parts of css grid because i wish we had this in flexbox whenever you add uh say well we don't have gap at all in flexbox but whenever you add a margin in something in flexbox um say you have three things all in a row the let me cut over here if you add a margin of flexbox say you have three things all in a row uh that margin is going to be added to the leftmost and the rightmost side because you're adding it to all of the items but so many times when you're laying things out you don't want any margin on the left and right sides because other components within the structure are taking care of that so let's um let's use the css grid gap which only does spacing in between grid items and not anywhere outside so in the grid here grid gap there we go and i happen to know that i put 10 pixels of gap between everything so without having to add a margin there we go we have a grid gap of 10 pixels i've really zoomed in on my browser here so you can see that but you can see that around the edges we're still butting right up against the edges here and that's actually a great thing right now because that means our gap's working it's only applying to inside the items we can worry about the overall placement of the grid somewhere else so let me zoom out a little bit here and in my interface i am showing here that we're kind of centering this on the page so let me see how we can do that i think what i'm going to do here is i want the calculator component to only worry about itself not its placement on the page so i will have the app component actually uh worry about that and that's going to allow me to do yet another thing is i'm going to be able to on the calculator component remove this flex 1 because i don't need it to stretch and take up all of the space so i just saved that and you can see nothing happened there um except that now this that's interesting ah okay so i put the background on the calculator component i really shouldn't have done that i should put that on the app component because this is more of our app background that's why that's why the background did that so i shouldn't need this flex one anymore i've saved all that so now this looks the same but i want everything in the center so i'm gonna put the app component which contains calculator in charge of centering everything on the screen here and we'll once again do that with flexbox so we will use align items and justify content to get all this centered so we are in a uh we are in a flex column right now so uh i'm probably going to get this wrong live but we're in a flex column right now so if i do a line items that should center it vertically let's see if i'm right nope okay well there we go i knew it was going to be both uh line items and justify uh now i'm seeing both items and content i think it's content but i'm second guessing myself okay it's content there we go so with the line i man i knew that i was gonna get that wrong with the line items and justified content we both horizontally and vertically centered this thing now um we're getting a step closer now that we have our buttons uh look actually real fast let me change this font size what font size is this this is font size 24 pixels let me change that in the button font size i just use pixels for font sizes i everyone has their own opinion there there we go bigger bigger font now let's worry about the placement of these buttons and this is really what i had to look up uh as far as css grid goes because grid has a lot of interesting ways to place things within a grid and one of the ways is literally hand picking which cells these buttons go into when i was thinking about doing this video part of me thought maybe i'll just put this in a table because it is a table of buttons but we can see by avoiding the table looking at our calculator component right here it's very clean not a lot of dom here i don't have to worry about a bunch of rows and columns and it'd be pretty easy to move things around if i wanted so we're going to use the capability of css grid in order to hand place each of these buttons and we will start let me let me add two more buttons real fast i'm gonna add the ac and oops and if you missed the beginning of this uh live stream i'll explain what oops is again later i probably didn't even explain it very much in the beginning so let me add two more buttons ac and oops okay great uh just so i can get the ones that are at the top of this and once again we're not going to make the display part of the grid so let me pull over my grid cheat sheet here i'm using the one on css tricks that just happened to be the one that came up first in google and one thing you can do here is one thing you can do here is assign a grid column start grid column end grid row start and grid row end and so using those you can hand using those on an item inside the grid you can hand place where an item goes on the grid and if we look in the illustration down here it looks like this is one indexed and not zero indexed so that's that's something to keep in mind and there are some interesting properties here so for most of our buttons in our design most of these are a one by one button meaning they're only taking up one cell wide and one cell high but we have several outliers so ac and oops uh are two cells wide the equal sign is two cells tall and the zero is three cells wide because i'm just not putting a decimal in this calculator you can do that on your own after this um so we have several exceptions here and one thing i don't want to do is in the calculator component start like hand coding or overriding css and so i think what i'm going to do is actually create another prop or attribute on this component pass it a very simple number and have the button component translate that for me into css in a format that i'd like so this might make more sense if i just start prototyping this out so let's say let's say that this ac button um let's say i put it at uh let's say i create a prop called position and i want the position of this to be 0 0 so i'm going to make the i'm going to make this prop accept an array and we will assume that this is the first position of zero zero i'm going to zero index it and then we'll uh we'll uh change that up for css grid so this will be zero zero this will be z uh zero one this will be zero two and this would be 0 3. very similar so this would be 1 0 1 1 1 2 and 1 3. so the first number will be the row with the origin being the top left and then the second number will be the column but this ac button is also um is also wider than it is tall so what if i made a width attribute here and i'm going to make the width a number which is the number of cells it is wide so let me copy this position over oops then we'll be in it we'll start in 0 2 so 0 1 2 and it's also going to be 2 wide so let's try this out um once again i have totally made up these props uh and then we're going to implement those here in the button component i'm going to eventually make this required so you have to say where the button goes in this component but starting out here i'm going to make it optional so that we can implement this gradually so let's do position as an optional prop and it's going to be an array and this is going to be more like a tuple array so meaning it's going to be a fixed size so we will have a x coordinate which is a number and a y coordinate which is a number and there we go so this is this is signifying really a tuple in typescript meaning this position uh prop should have two items in it and then we will also have an optional prop of whip which should be a number great all of my red went away so looks like all the types are checking out now let's implement those in our button component using these css grid properties so what we can do is um build up some styles here and i'm trying to think of how i might do that i think what i'll do is uh let me add in position and width so i can pull those in here i think what i'll do is create a styles object and especially since some of these are optional start appending to that if certain props are provided usually i don't mutate things as much as i can but this will be okay here so if position is provided then let's put a styles dot grid column start will be the positions first element so that's going to be the x no that's going to be row start so let's see hold on uh i think i got that wrong okay i am going to make this x y i might have got these flipped up here i'll have to check okay so grid row star i do want um no that's going to be column the column in row always flips me it flips me up in grid because so x is going to be which column we're starting in so that's going to be our first element of position and then the second element will be grid row start now and this will be the second element now typescript is complaining at me here um so i'm going to let's see you know what i'd like to do is style what is the style type react.css properties hey there we go react.css properties excellent so these are probably typed now too yes awesome okay so if we have a position we're going to specify the position there we haven't implemented width but let me let's just see if this works okay so i'll save all and we'll see what happens boom fail okay we didn't oh we didn't finish here so style is styles okay getting closer uh so ac didn't move it was the first already um oops button did move uh and that's okay because i got this wrong so x and i flipped my x's and y's earlier so i'm gonna make the first element x so we're gonna put that into and make it on the first row and since these are since these are one indexed i think i need to add a plus one here i'm wondering if it's not liking my zeros oh boy debugging grid okay it didn't like my zero so a couple of things there uh i am zero indexing these positions because that's what makes sense to me as a developer css one indexes the grid and rows uh columns so we are abstracting that out for the user of this button component and we have our first button ac in position zero zero and we have our oops button over here in position two zero now what you can see is what css grid does automatically is it automatically fills in everything else that didn't specify a position in any of the remaining space which is a pretty cool feature there so we're manually assigning the positions here and then the all the others fill in so let's handle this width property where we are making the width of 2. i noticed earlier looking at the documentation here there's an interesting thing in grid column end in grid row end called span so if you're used to html tables this is much like a call span or a row span where you can just say uh how many things it spans um so let's let's use that i haven't used that before and see if that works so if uh if width is specified we will set the styles.grid column span grid column no grid uh grid column end to and this is going to have to be a text value so span of whatever that width is so let's save that and see what happens there we go hey that's awesome so we have our buttons up here that are taking up a width of two for two cells two units and then all of our other buttons down below so let's go in here and let's finish plugging in the positions for our other buttons and then i'll go back and add in the minus plus and equals and we can see by manually plugging in these positions if it leaves us the empty space let me come in here i'm going to uh option drag down again and paste all at one time so x's and y's so this will be row no this will be uh column three row now i'm wanting to uh one index these uh column two row two this will be column one row two column zero row two same thing i'll plug in this pattern here we'll see if i get this right the first time this is actually going to be one two three there we go okay awesome so we can see that since we've manually specified the position on all of these it is leaving empty space because there aren't any elements in here that don't have a set position so let me add in one more thing one second let me add in my zero now so let's do a label of zero this will be in position column 0 row 4 and this width is going to be 3 so this will be our our first and only i think a button with the width of three and there we go great and now you can see that this is now perfectly vertically centered within our window earlier when we were centering this calculator component you may have realized you may not have looked vertically centered but because we had specified the size of the grid there was some empty space that was accounted for in the calculation of that vertical centering so let's add in our remaining buttons and since i have these other non-numerical buttons at the top i think that's where i'll add in the others since we're specifying the positions it doesn't really matter what order these go in so let me do a plus and say that's going to be in column three row one no width necessary to specify here the plus will be in oh that's the minus the plus will be in uh once again column three row two let's hit save excellent and then the equals sign will be in row three and then we'll need something new for that so looking better still uh but this time we need to make equals taller so what we might want here is a height attribute of 2 so we can do something similar to the width attribute so let's make a height attribute that is optional i typically keep things in alphabetical order um and we'll do something similar to width here so if a height is specified then let's do the grid row end of the span of whatever height is provided and i'll add that in here save everything and there we go now we have our calculator buttons fully laid out using css grid in the proper placement with the proper spans and this is all really nice and if we look at our calculator component here like i said we didn't have to worry about messing with a bunch of rows and columns or rows and column dom structures we're able to just specify the position here so let's uh let's um let's take a look at the coloring and because we have our yellow buttons um but then we also have these gray buttons as well so there are a couple different ways you could go about that i could maybe add a button type to where i'm giving it a type and then the button component handles um what color that should be or i could just directly set a color on it i think i will go for just the more direct method of setting a color right now and we can change that later and i think what i'm going to do is i'm actually going to make the default color this gray color and then make the orange color the alt the the alternative so this is going to be my default color here so let me pull that into the button and i'm going to cut this so i can use it later so this is now going to be the default color of the buttons oh you know what um now i'm changing my mind because with these buttons the text color also changes so i think what i will do here is add in a button type yes okay so let's do that so we're going to add another prop here that is button type and we'll do type here and this is going to be some sort of button type and we will make an enum type script option here that declares which type of button this is and we can uh so let's make an enum called button type and figure out our button types here so we have really our numerical button types and then we also have kind of our operation button types um if if i think ahead to maybe implementing a decimal uh i don't know if you would call a decimal numerical you might call these like value buttons because it's it's um changing the value so whether that's a number or a decimal or maybe even like a plus or a minus and then everything else is an operation type so let's call these uh let's make it simple let's call these number and operation so those are the two types so i'm going to pass that in here now i'd like to set a default value of these and let's say that the default value will be operation because that's also my default color so things are gray by default and we'll make them we'll make the number the exception to the rule here so we'll say that button type is an operation by default and i guess i'll make this optional okay so we have this um and let's specify our button types for the numbers so on all of the numbers let's say that the type is button oh let's export that enum so we can use it in this other component so let's say export enum so on all of these i want these to be button type it's not going to be nice to be right now button type is number let's see if it'll import now nope okay we'll import that ourselves button type okay now this won't change anything because we haven't made it change anything yet i haven't saved all so let's make the number change both the background and the font color in fact let's make the default font color and button white there we go okay now let's uh change this on the fly so if the type is the button type of numerical then we will change the color to be black and that should be an equal sign and we will make the um background uh the orange which i'm sure i long ago deleted off my clipboard here's our orange okay excellent so on the fly we're changing the style of this to make the button color black and the background orange if the button type is a number type so passing it a type to a component is a great way to adjust the functionality we're only adjusting the style here so this is um not the the best example of that but this is something you can do sometimes if you need to alter the behavior so this is looking pretty good for the ui so far the last thing we need before we actually get into the logic behind this calculator is adding in the display value up here and like i said we're not going to make that directly a part of the grid or at least this initial grid because it's a different height i don't want to mess with that or do i hmm we could do a different height just by simply adding in a row up here okay i think i'm going to change my mind on the fly here okay we are going to make this a part of the grid and we'll make this height 120 pixels okay we're going to make this part of the grid let's see if we can adjust this okay so we're going to add in a row at the top and it's going to be 120 pixels tall so we will add into the beginning of our grid template rows which is the definition for what is the size of each row we're going to make this 120 pixels now if i save this what should happen because i haven't added anything into the the grid here is these i think will become 120 pixels okay excellent that's expected so we're going to then shift everything down and add in a display value or display component up top excellent okay so let's do that so essentially in all of our positions here uh we need to add one to the y position which would be our second number here so i'm just going to go in uh i'm going to go in and add one to all of these two three four uh these let's see these are all two these are all three these are all four this is five okay so that shifted everything down now i have this big empty space for my display value i'm i may end up breaking out the display value into its own component but i don't think that's necessary right now so i'm just going to use my um snippet that i created to say display and we're going to make the background of this white so we can see that we're going to match the border radius from the other components which is 8 pixels and we'll do a text a line of right and let's see how big this text is 48 pixels so a font size of 48 pixels we'll use our display here um now we don't have the position and width and all those attributes on that because those only exist on buttons so this will default to being in that topmost position but i do need to specify that it needs to be four columns wide so i'm going to add in the um what's that called grid grid column end so grid column end will be a span of four columns wide so let's see if that works excellent okay let's add in a dummy value here we'll add in our 42 likes is in our design great um now let's uh center that up and add some padding looks like i have 23 pixels of padding you i'll i'll even that up so let's make that a padding of 24 pixels and then we need to vertically center this it's almost vertically centered already but i want to vertically center it otherwise i'm actually going to change this to where the padding is only applied to the left and the right so i will do a padding vertical padding of 0 and a left and right padding of 24 and then how might i center this text you ask i'm going to use flexbox but all in this same uh dom element all in the same component so let me do a display of flex that uh that will oh that's interesting okay so i'm gonna ignore text align right now and we'll align it with flexbox so now let's see if i can get this right this time align items uh should align it to the end horizontally no okay that's justify items okay justify items uh we'll align it to the uh justify items we'll align it to the right that doesn't sound right okay i'm totally blanking on what justify item it's like uh end what is it justify end i think it's end no so align items is going to be middle no it's going to be center okay we have this vertically centered and i'm totally blanking on is this not flex end i could have sworn that was it let's take a look all right so we have this it may be that let me throw in my texta line again it may be because i'm trying to do this all in the same element yeah i think it's because i'm trying to do it all in the same element i'm sure there's a way to do that so i have a couple of options here um i want to keep this as simple as possible so i think i will to keep this super simple i think i'm going to ditch the flexbox and just align this right and just add in the padding and call this a day in reality i think what i would do is add in an inner component that is the display value um this is what i'm going to do for now so we can continue alrighty so there we have a react and a little bit of typescript code for building out the ui for this calculator so that is all of the ui work we're going to do as far as the visuals the next step let me pull up my very detailed software development plan here so we have built the ui with react that's what we've done next up we're going to build out the calculator brain with typescript and that won't have anything to do with react whatsoever we're actually going to build this in just pure typescript um we're not even going to really uh comment you know accommodate for clicks and buttons and all that that will happen later when we hook this all together so now let's totally switch gears we're in dom world and css world totally switch gears to building out this calculator brain itself but before i do that let me explain one thing so part of the goal of this is i want to build out this calculator in a very functional programming sense um something where i can um type all these or push all these buttons and in the background build up essentially a log of every single button press and then in this functions like a calculator might function as you'd expect but i'm adding in this function of the oops button and the oops button is anytime you hit a button on the calculator and you think oops i didn't mean to do that so whether you hit equals when you meant to hit plus whether you typed in a wrong number anything you do i want the oops button to undo that previous button press so like i said whether it's entering in an additional digit value or whether it's adding in an operation so oops is kind of like undo but it's undoing one button push at a time and maybe we could even um make it to where you could oops oops oops uh as many times as you wanted so this the style of this there are several different ways that you could build this but i've been really into immutable logs lately immutable history of data so with every button push we're going to build up an array of every single button push and use that array of actions to generate a calculator state that we can use to display our values and do the actual calculations itself so again i have not designed this out so we're going to kind of sit here and think through this together and figure out how we can build up this calculator functionality so i'm going to close all of my react components here because we are exiting react world in fact i'll probably just move this over here to the side maybe we can have a visual reference here but primarily this is going to be all about the code and the other thing i'm going to do here is i i might try to um write some tests for this along the way uh i'm definitely not a tdd um heavy hitter but i think this is a practical place to add in some tests especially as we're defining the functionality here together and and figuring this out along the way so let's create some files for this i'm going to make a new file i'm going to put it within a modules folder and i'm just going to call this calc so i'll do calc.ts will be my main file here and then we'll do we're going to use jest for the tests so we'll do calc.test.ts and we'll put these up side by side and so i'm just going to make some comments and rough out what we're going to do here so the input to this brain this logic will be a list of button presses or a list of user inputs so um to our uh calculator cal accumulator brain here uh the input is going to be a list or the uh the input is going to be a list of inputs which is uh weird uh i'll say like user input here we probably won't call it user input in the types but we're going to have a list of user inputs and that is going to end up taking that input we're going to generate some sort of state object um which we can then pull off like the display value from and uh things like that so let me let me start roughing some of this out so that you can see kind of what i have in mind here i am first going to create um some placeholders for this so i'm going to do const calc i'm going to export this all as one object with functions off of that object so we'll do calc dot get state so we'll define a get state function up here that will accept things and then we will export default calc so this is this is all going to export as one object and then we'll have several functions on this object so let's start writing tests so i'll import calc from our calc file and we'll start writing our tests oops um create react app i'm pretty sure ships with just yes uh create react app ships with just and so if you want to use just uh you shouldn't have to do anything here to get started uh so we are going to um make a very simple test here to kind of derive our state and there are going to be several things that we're going to need to do this so i'll i'll flesh this more out as we go but i want to have a list of oops i want to have a list of inputs here that we can generate state from so what i mean by input is every single button press and we need to identify what those button presses are so these could be a number button press or any of our operations over here i am going to treat ac all clear and oops differently those are going to actually change what inputs we are pushing into this brain here so our two types of inputs as far as the logic here goes will be a numerical input and an operation input so i'm going to have a this list of inputs will be a list of objects and we'll have a type of uh you know what else i'm going to do is i'm going a lot of times i'll add a types file so that i can reference that do i want to do that i'll start in the same file actually for now so we will say export type input type actually this is going to be an enum so export enum input type and very similar to our styling one we may reuse that for styling is we're either going to have a numerical input or an operation input numerical will be 0 through 9 operation will be minus plus equals so this will be let's say we do 12 plus 3. so we want to test the input type numerical and then we need to give that a value so if we're doing 12 plus 3 here the value of this input isn't 12 it's actually 1 because each of those button pushes are an individual action so the value of this is going to be 1. so we'll do 12 so 1 2 we're just building up a list of button presses here and then later on uh one more down we'll do plus three so now we want uh in between those we're gonna have an operation input and uh let's make a property on here called operation and maybe then we'll have an operation type that sounds like a good idea okay export enum operation type and this will be could say plus or minus or add and subtract i think i'll say add add and subtract and equals which will be equals i guess i don't know there's probably a better word for that add subtract calculate add subtract uh finish we'll say equals for now add subtract equals um so operation operation type will be add so in this example this is my representation this is my data for what would this interface need to generate to push into our calculator brain here so each button press is going to be an item in this array we'll have a numerical input of 1 numerical input of 2 that would make 12. then operation input of add then a numerical input of 3 and then we need an equals so then an operation input of equals so those are all of our inputs um so let's go ahead and type these as well so let's export a type of calculator input and a calculator input could be one of two things it could be a numerical input or an operation input so first let's define our numerical input so this will be type input type numerical and that has a value which is a number or i use the pipe or the type will be an operation and it has an operation property which is an operation type so if i hit save there it'll reformat this to look nicer so these are our calc inputs it could be one of these two types so i can go ahead and i'll type this array to be an array of calc inputs and there we go so those those are typed so what do i now want to do with these inputs um i could do a couple of different things here i let's say that we have a function takes all these inputs and generates the current state of the world so let's say that uh the current state might include um well it will definitely include the display value so what whatever is currently displayed up on our calculator up here um so let's let's check that out so what if we had a function on our calc i'll call it a module called get state and we pass it our inputs and then our test would be that we expect the state dot we could call it display value to equal 15. why 15 12 plus 3 is 15. so 1 2 would be 12 plus 3 equals 15. so if i were to push these five buttons i want the calculator to display 15. so we have several errors here because uh we haven't typed all this out so let's say git state um accepts an array of calc inputs so let's add that to here and then it will return let's make another type called export type cal state and we will add in the state here let me check some things real fast okay we will add in um some values here so let's uh calc state let's say that the state will have a display value property and we will make that a number and get state will return account state okay so let me just say return display value and we'll make this zero for now so if i save everything here all of the types check out but this of course isn't going to work because we haven't implemented it yet now by default i have my just nvs code not running so let me go up here and start the just runner and so this should start running my tests and hopefully we'll get an x here meaning that this is not yet working then we can go in and implement this so if i hover over this we expected 15 but we got zero that's what we expected so we've defined our api here we've defined our types now we need to go and uh implement this function and that's where we'll really have to do some thinking here uh so let's figure out how to do that so let's talk about this get state function we have essentially single digit inputs or single operation inputs and we need to uh turn those into real numbers so we need to take one and two and turn that into 12. so what i'm probably going to do here is a couple of map and reduce operations and cycle through these inputs and turn them into something that is easier to calculate um yeah so if we have inputs of essentially so if we have coming if we have coming into us we have one two plus three equals these are all of our inputs that we have coming in um i probably want some sort of intermediary state before i before i return everything that helps me turn this into something i can actually process so i think what i want to turn this into is operations like uh so so now now i'm thinking through what kind of intermediary state do we want now i think i want to turn this into operations like um 1 2 and then plus 3 and then equals so i want to combine these numbers into 12 with some sort of operation so i may just want this to be like plus to start out with so essentially 0 plus 12 plus 3 equals 15. um yeah so we need to calculate this intermediary state and let's do that now so i have really come to love uh map and reduce operations um reduce can scare some people it definitely scared me when i first encountered it uh some time ago but it's it's really become a favorite of mine for dealing with this type of immutable data and generating state from some sort of log what we're doing here is building up a very simple log of of button presses so let's go in here and uh try to build up this state so i'm sitting here thinking through as we build up this state i probably want to iterate through and have like a current value and current operation uh so really what this is gonna be is it's gonna be some sort of like another array of objects and this is not any real syntax down here i'm making this up some some sort of array of object which is like operation and value operation value and then we'll finally have like an equals here so that's pretty good okay let's make that happen so what i want to do is i want to take these inputs and if you have trouble thinking about when to use map filter or reduce even though we're ending up with an array here i still want to use reduce because i'm reducing it down i'm reducing a five item array into a three item array and so we're going to build this up along the way so i'm going to within this get state function i might break this out later um uh within this reduced state function let me make something i'll just call it result from now uh we'll say inputs dot reduce and we'll start reducing so reduce is going to call our inner function here for every single element and we first get the previous value and the current value um i feel like i kind of need a name for these um so these are all called inputs um all of our calc inputs these might really be um we've already used operation uh these might really be uh i'm blanking on a name here i want to use operation but we've already used operation ooh maybe we'll call those operator yeah i think i want to rename those to operator okay and then i can use operation here that that makes more sense so i'm going to use uh f2 and vs code this is one of the great things about typescript f2 and rename these across the board so that's now operator type and i'm going to rename this to operator great so see we can all this was changed here and even changed over here very nice so those are operate operators i'm going to call these operations and in fact to make this even more clear i'm going to make a function that's just simply called get operations and what that will do is take a set of inputs which is an array of calc inputs and it will return an array of uh operations okay and then let's define what an operation is so an operation is a uh operator and a value so we can see we inputted one two plus and i want that to generate plus twelve and then we said uh three oh no actually [Music] yeah it'll default to plus so pl this plus is a default so one two will generate plus 12 and then plus three will generate plus three so we have on an operation we have an operator and this is going to be an operator type that works out nicely and then a value which will be a number and i put type twice here okay so here is where i'm going to reduce this down and i can probably just go ahead and return here so we're going to take our inputs reduce them down the first thing we get is the previous value the next thing we get is the current so previous and current we'll call our current uh operation and we're going to reduce this into like i said in a array no actually our current is an input and we're going to reduce this into an array of operations so let's go in here and let me check some things i'll look at the stream here real fast okay we're going to reduce this down into an array of operations and our operations are operator and value so the default is um the default is a we're gonna have an empty array here and we're going to reduce this down and we need to tell it what we're reducing down to we're reducing down to a type of uh array of operations okay this is where it gets a little tricky we're collecting each input and we are building up these operations so the very first input i'm going to receive here is this value up here we get a type of numerical and a value of 1. so what we can do is switch on switch on the type here so i'm going to add in a switch statement and we're going to switch on the input dot type because we have our input type up here and so we'll add two cases because we have two input types so we have a numerical input and then we have an operation input which i actually want to change to operator okay and i want to do different things and so one two if i hit one and 2 i need to build up a number there and so i need to get whatever the current so it let me name previous is going to be operations so operations is going to be the list of operations we have built up thus far and let me get the last operation and so that will be the last of the operations i think i'm going to pull in low dash here even though that's totally uh overkill um no i'm not gonna do that okay operations and then we'll do operations.length minus one that's that's the last um the last one now if length minus if the length is zero then we don't have an operation so far so if if there is a length at all then we'll pull in the last operation if there isn't uh and let me type this to operation if there isn't then we will make the type of this operation or the value of this operation a 0 and the operator a plus and i'll explain that in a second okay typescript is a little mad at me or very mad at me here let's figure out why um push okay let's figure out what's going on here i really made this mad i'm going to just go ahead and return a basic uh operation there okay so that that uh that makes sense so operations there we go okay looking looking less scary um i'll shift that down for now so we are going to switch on the input type here and we're going to build some things up so pretend that or let's not pretend the very first thing we enter here is going to be a value of 1. and and that's a numerical type so anytime if you think about the operation of a calculator if you think about how a calculator functions every time you press a number that appends the number to the right of the interface here so if i click 4 then i first get a four and then if i click two the two comes in on the right um so i just keep appending numbers so whatever the last operation is if i get a number in then i need to append that to the right of the number so we will take the last operation and we will change that up um let me think here okay so i'm building up i may want to change this up if i'm building up these operations i need to know when there is an operator okay so let's let's keep going here so last operation well how am i going to do that okay so i'm trying to what i'm thinking through is i need to decide am i going to edit the last operation in the sequence that sounds wrong or am i going to uh somehow build up like a temporary operation and then uh append it to a list of such and i think i'm going to do the ladder okay let me make one more intermediary state here and that is um that is like some sort of like operations builder oh gosh like java now um operations builder so let's say that we have a list of operations and then the one we're like currently working on so in this sequence as i uh and as i iterate through all of our inputs here i want to build up a working operation and then once that's done push it on to the operations list so let me let me illustrate that a bit further when i push one that is not a full what i'm calling operation yet an operation being an operator plus minus equals and then number when i press 2 that's still not a full operation i'm still working on what that operation is going to be when i hit plus or add now i have everything i need i have a number and i have an operator and in fact our working one will always be a partial operation so we will be omitting um the operator out of operation so working will only be a value that's what it will be this makes a lot more sense okay let me let me clear some things up here so we're still reducing down our inputs and but this time instead of reducing it into an array of operations we're going to reduce it down to an operations builder and we have and we're going to start out with operations of nothing and uh working of uh i'd like to make that a null okay this is looking better i think i'm going to erase this and we'll type out again what we need so we are we have a working value in our builder and we have the current input so now let's switch on the type of that input so that's this type up here whether we're getting numerical or operator and we'll build up a case for each so input type of numerical and we'll say if we get a numerical then we need to modify the current working value um so we will have like a new working value here so our previous value will if if um a builder dot dot uh type doesn't make sense okay that's what i was expecting this is going to be or no [Music] i want that yeah or or no um so in our current builder currentbuilder.working if that exists then we're going to get the value off of it and you can see that vs code very nicely for me put in this question mark which means it will only get the value if working exists if that does not exist we'll just make it a zero so uh i'm going to say or zero so this previous value will always be a number it'll either be whatever we had previously or zero and to append the number on to the right essentially what we need to do is multiply our previous value by 10 so we can shift it over and then add in our new value so our new value is our previous value times 10 plus our input dot value and if you remember inputs can have a couple of different things for numerical they either have value or for operator they have operator but because we're inside of a switch statement here and we're inside of the numerical case then we know for sure that this is going to have a value because that's required by the types so we have a new value here so let us return a new builder state so what we're going to do is i'm just going to say keep everything that was in the builder but as our new working value we're going to make the new value our new value so i hope that was i hope you could follow along with that um we'll keep going here so this only handles our numerical case let's also handle the case of where we get an operator and that'll be that'll be interesting so instead of punching in a number if the person punches in an operation and let's just handle uh well we're about to handle all of them interestingly this looks like it's gonna be a switch within a switch okay let's try it out so if they punch in an operator then uh we need to do a couple of different things so if it's uh we also have an equals in here let's handle add right now let's assume that let's assume that uh equals is really kind of a different type of operator um okay so let's do this one let's handle the add and subtract cases so what we want to do now is we want to take the working value that has our numbers that we've punched in so if we punched in one and then we've punched in two up here we have built up a working value of now 12 and we want to record an operation that is uh that is that number now actually the operation gets reque the number gets recorded on the previous operation so if i do like plus three the plus is actually what i type in first so on an operation we actually know the operator first that's interesting okay so i didn't quite think this through so uh it it's actually easier to think about the next one so if i i do want two plus three plus three is the actual operation i'm doing so we know about the operator beforehand so at the beginning of our calculation we could assume that we're adding plus 12 because we're we're starting from zero so we'll do plus 12 here uh so i'm going to change up my types a bit and this may be getting a little confusing so i'm going to draw out what the expected result uh will be over here for our um operations so based upon these inputs above this crazy operations reducer we're building what what do we expect those operations to be we want that to be first um an operator of uh plus or add operator type add and then the value to be 12 and then next we're going to do plus three then we need we need something to specify equals we may do that without a value so if we do 1 2 we want that to build up this operation if we do plus 3 we want that to build up this operation and then we when we hit equals we want that to build up this operation and actually equals equals maybe something entirely different uh that makes me okay i won't get too ahead of myself here uh i might even break that up a little further but i do want to get something working here in the tests so let's uh let's try to keep trucking here i think i think i know where i want to go next after that though why is why is this earring out and doesn't like add i don't know why okay let's see so let's build up our different operations here and i need to change that up so in this first one when we start out with one and two we're assuming that these are positive we're adding this to zero so in my initial state of my reducer instead of starting with null let's let's not start with null and in fact we we don't need to omit the operator because we'll always know the operator we're going to start out with an operator of add and then a value of zero so that is our default working state and what i'm what we need to figure out is when do we append to our running list of operations well it looks like we append whenever we actually encounter a new operator so if i hit 1 i'm editing my working value the working value i'm currently in working on in the builder if i push 2 i'm still editing that working value if i push plus or add i need to push my previous working state onto my list of operations and begin a new one that's that's what i want to do here so we're going to start out with a default state of plus and 0. if i get a number just like before i'm going to keep editing the number here but if i get an operator i need to push that previous working state onto our list of operations so let's uh let's say that we're going to return um we're going to return uh operations and that's going to be our previous builders operations and appending our current uh working operation okay and then we need to generate a brand new working state so we will do operator which is the one we just got off the input and we'll start with the value of zero so this might this might be a little hard to follow here but to reiterate in our example here we're collecting five inputs five button presses one two plus three equals we want to translate that or transform that into something that will be easier to calculate so we're going to transform one and two into 12. so our first reduction is taking these five and generating these three so we're going to take one and two and generate an operation with the value of three we're going to take plus three and generate an operation with plus three and then equals will still just be equals so we're reducing this down a step at a time transforming it into our raw button presses to numbers we can actually calculate and then eventually we'll get to what shows up on the display okay so um i have a few errors here let's see what's going on okay uh operator numerical operations [Music] working working has an operator value of zero i'm not quite sure what the problem would be here um and this is handling all cases let's see argument of type is not assignable parameter type these types they're not compatible type undefined it's not assignable to operator type i know for a fact that this is going to air out i'm not sure why this one is erroring out uh i'm gonna continue along okay and then finally from this um we're building up this builder but what we want to return is just simply a list of operations so we will return our builder dot operations i think that's what's going on it's reducing it down into the wrong type maybe i have typed my reducer wrong here let me erase this so our initial value is specifying that we're we're starting with an operations builder this is saying array to calculate reduce this is saying that we're generating a calculator input from our callback function and in fact we're passing in a calculator input that's that's not what we want um i'm not sure why that didn't like our operations builder before it still says okay i must i must be blanking on uh typing a reduce function let me look that up real fast that doesn't help what i'm trying to do is uh type accumulator and a typescript reduce function and i am i'm doing the same thing here okay let me look at this one more time comment if you uh see my error here but i'm i'm taking my list of calculator inputs i want to generate what i want to generate is an operations builder i'm passing in the default value the initial value i'm passing in as an operations builder let me let me uh this shouldn't matter but let me type this as a variable that really shouldn't matter i really don't know why this is not typing this um i could type the function here okay what's going on i've hit a roadblock here for some reason my reduce function is not recognizing that i'm trying to reduce into an operations builder type calc input is not assigned to operations builder type value ah oh no that's working no that's fine this is crazy okay yeah that's fine that's fine okay let me see here reduce callback function reduce view previous value u yeah i should be able to pass that in there okay ah okay we're getting closer here um i didn't specify an operator on this working value okay so what what happened in all of this is uh when i was calculating the working state when i was expecting a numerical input i neglected to carry through the operator that we had initialized so let me go ahead and just spread across our existing working so that will carry over the operator and bring in the new value um i'm not gonna break these out anymore i think that maybe that one error cascaded all the way through because uh for instance with this uh type here specifying the generic here i really shouldn't have to manually type any of these this should carry through and it does this is saying it expects a return function i am returning from all cases here i'm not sure uh why it's not liking that right now usually it recognizes that i've handled all cases i'm going to keep going since that's only a warning perhaps that will resolve okay so we should have a list of operations here and what i'm going to do is let's go ahead and test this because this this is a lot going on here so let's test this intermediate state and test the fact if we can get these inputs to generate these operations so i'm going to copy this test and we're going to make one for generates operations so based upon these inputs i don't need that ui based upon these inputs uh will i get these operations so let me just make this some real data uh these are operations so this is going to be an array of operation which is not inputted imported uh and we are going to say we're not going to export that we're going to add it on here so we expect count dot get operations we expect calc.getoperations to equal operations okay uh this type equals down here it doesn't like that because uh equals doesn't have a value i'm gonna just stick a value of zero on here for now so we've saved um it says git operations is not a function that's because i haven't saved all let me save all it should be it's running the test right now and let's see what we get back okay we have something to work with okay so we expected four and we received zero lovely um let's see deep equality object operator 0 value 3. i really want to see more than that so let me see if uh let me see if that shows up down here i thought usually my test showed up down here i'm sure there is a way to see more of the state but i don't know that off the top of my head so i'm going to run it in the console because i know i can see it there okay so if i just do a yarn test that will run my just tests let's take a look um looks like there are some react tests in here i really don't need those so let's go delete those out app.test we don't need that move to trash okay one thing i'm going to do here see where this says operator 0 and operator 2 those aren't very human recognizable one thing that that's coming from the enums because all these operators are enums so one thing i like to do in my typescript enums is set them equal to strings so i'm going to take all of these operators and we're going to set them equal to strings of the same name you could capitalize you could lowercase these if you wanted in fact let's go ahead and do that now so let's lower case these that'll be a nice javascripty output so let's run that again so here we can more clearly see what's going on uh we expected to receive an equals at the end uh we did not we received uh nothing so to make this test pass i could let's see go in here and um do because what's happening is an equals is not enough to push uh that final operation but that's okay because let's see do i want it to do that yeah let's go ahead and do that okay so if this is an equals i need to um push on to the stack or onto the operations list an additional equals operation and that's even in addition to the working operation so this sim we have a very it's really the same as this but in addition you know right here if it's equals we need to push on that final equals operation so let's let's try to work that out here what i'm going to do is i'm going to build up i'm gonna build up an object called a final operation and actually yeah i'm gonna build up an object called uh final operation and that will be of type operation and i want that operator to be equals and the value to be 0. and we only want to push this on if the current operator that we got was an equals i may go back and change this a bit later yeah okay let's let's make a case here so if if the operator if the input operator is equals then we're going to do something otherwise we're going to redo what we've been doing here so if it's an equals we're going to push on this final operation so that is the operator equals and just we're going to have the operator equals is always going to have a value of 0 on that operation and then on our working operation we're i'm actually going to make this a plus um to reset it to a default state so that may be a little bit weird but you should be able to see this hopefully in a passing test yes okay yes so we have our first passing test okay so we have our five button presses we're calling those inputs one two add three equals that is transforming into these three operations we have a plus 12 a plus three and an equals now that we have real numbers here we can take these numbers and we can perform math on them and that's what we're testing down below so we are testing um so we want to take these operations and uh now do the math where we can derive uh the current state of the world and the state that we need really the only thing that matters as far as our calculator goes is the display value there's no other output of the calculator the display is the only thing so if we think through what happens here when i press 1 i expect a 1 to show up here when i press 2 i expect 1 2 to show up when i hit plus 1 2 is still going to be there until i press something else when i press three a three will show up when i press equals then the final value will be displayed so it's that equals operator that signals to us that we're ready to perform the mathematical operation so let's test that out so let's get rid of this we have a test above okay we're deriving state we still want that to be 15 we're still getting zero okay our tests our tests are still written correctly now what we need to do is we're going to minimize this get operations craziness delete some of these working comments now we need to do is we need to get the state or we need to get uh the value of the state now i'm going to majorly cheat here um but actually what i'm doing is i'm going to follow tdd principles here and do the minimum possible to make this test pass and then we're going to create some failure cases to handle what should be appropriately handled here so if i take a look at these operations 12 and 3 and so these inputs will generate these same operations and i want this to equal 15. well a very easy way for this to equal 15 is for me to just add up all the values and make that the display value that would be the minimum work required so we'll do that make this test pass and then create another test so we can start handling the different cases of our calculator and we might start hooking that up before we finish everything so we can actually see this in action and see it play out so let me go into my state here we're still going to accept a list of inputs and we're still going to return a calculator state but in between i'm going to generate that immediate immediate intermediary state which is the operations so let me say const operations get operations based upon the input inputs so i took the inputs i used our get operations function up here and i now have a list of operations now i can take that list of operations and sum up all the values and make that my display value that will make my test pass down here so a quick and easy way to do that is um like i said we're just going to add up all the values even the equal sign even though the equal sign has a value it's a 0 so that won't make a difference so instead of just returning zero here i want to return all of the values added together well to sum things together in javascript i can do a math dot uh no i can't that's always the function i want that is always the function i want there is not a math dot sum that's always what i want okay there i must have worked in some language in the past where there was a math dot sum because that's what i always go to and i'm always surprised that it's not there okay so we will reduce once again so what we will do is we will take uh this sum so we're going to take all of our operations we're going to reduce them down and we're going to reduce them down to a number so we'll have our current sum and then our current value and we will just add those together and we'll start with zero um actually our current value is in operation so uh this is and this is an operation so sum plus our operation dot value and then our display value is just going to be i'm going to name this total our display value is going to be total so cross your fingers that should make our test pass down here excellent okay so we have our um first test which tests at how we are transforming our inputs into our operations then we have our second test which still takes all of our inputs but sees if we can get to that final result now even though we've made this test pass i know for a fact that there are several cases in here that we haven't handled yet so let's test a few more cases and in fact this sequence here would be a great sequence to keep testing but not its entirety so i'm going to copy let's see i'm going to say derives final state and let's say i want to test the same thing but what happens before i hit the equals button that display value um if i push one two plus three the display value is going to be it should be three so that is what i would expect out of this test so just derives display value um let's see upon new numerical input um it's not yeah so if i type one two plus three i'm expecting a three to be currently on the display in fact let me pull up calculator we're gonna make sure so one two plus three i'm expecting a three to show up great um but my test is failing that's because i can't just add up all the numbers and return that as my display value what i need to do is it seems like my display is depends on whatever that last operation is if the last operation is an equals i need to return the full calculation if it's not an equals then i probably need to return the most recent value so that's what i'll do so let's in our state here we're going to first get the operations that's nice but then we're going to switch on um the last operation so last operation and that is operations once again we'll get our last one here so that's uh operations.length um if there is this is why i like pulling in low dash if there is an operations dot length then get the last one otherwise return null so uh and then if there is no last operation then we're just going to return a display value of 0 which is a test case we should have also so before before i save my uh file on the left i want to say if i'm passing in no inputs then i want this to be zero in fact it is zero because the reduce function starts at zero um so i actually don't need to do that so if if the last operation and the last operation dot operator is an operator type of equals then i want to return what i did previously which is totaling everything up and returning that as the display value actually i want i do want to pull this out because i don't have to check that every time so if there's no last operation we're going to simply return a display value of 0 always so and then we're going to switch on this essentially so we're going to switch based upon the operator if the operator is an equals we'll return this otherwise in fact any other time um we're going to return the last value so uh return display value the value of the last operation so we'll return a last operation if there was one uh actually i don't need that if there was one we'll return its value otherwise we'll return zero see if that checks out okay still says one test failed here let's see what happens uh expected 3 received 12. okay that's interesting that makes a lot of sense so um in my get operations here i actually don't have that final operation yet because uh i have this inner working value um so that's interesting so this get operations i actually need more than the final list off of operations i actually need that builder um that's building up the state so rather than pulling out the operations at the end i want to return the builder as a whole so first thing is i'm going to do is i'm going to rename this function so get operations builder and we will return instead of an array of operation an array of operations i'm going to return an operations builder and instead of assigning this i'm just going to return this reduce function itself because that itself returns an operations builder okay this is going to break a few things here so i'm going to make this builder const operations is operation i'll pull those out um this should check out now uh these let's see get operations so this this now is get operations builder and i'm testing the operations here so i'm going to leave this test as is um just renaming things to where that will pass again let me save this uh this should fix at least that one and this one okay so we're back down to uh this case down here so we have our we have uh we're accounting for two cases as of now one is they hit the equal sign if they hit the equal sign we return to them the final calculation if they don't hit the equal sign instead of returning the last operations value we need to return the current working value and so that comes from the builder so builder dot working dot value i think that will work let's see okay why do we have like oh test title okay uh upon numerical c derives uh c has display value of zero with no inputs okay we have all passing tests that's awesome um so we're handling the case of no inputs we start with zero we handle the case of being in the middle of an operation where we're displaying the current working value and we handle the case of hitting equals to get that final value now i know there's one other case that we're not getting and that is here we're sit we're really deriving a final state with addition but what happens if we also have subtraction in there with addition and subtraction so what if we have 1 2 plus 3 equals so that's 15 and let's say we want to do minus five um so let's before we hit equals here let's do a minus a subtract five and if i subtract 5 from 15 that will be 10. so i'll add 10 here and we should get a failing test okay great we get a failing test that's because we are blindly adding up all numbers together we're not adhering to uh that subtraction operation so here where we're calculating our our last value we really need to determine is this a plus or is this a minus um so what we can do there i'm sure there are several different ways to do this um what i'm probably going to do here is always add but either add a positive or add a negative and we'll add a negative for subtraction or will i just make it more explicit i think i'll make it more explicit and i'll tell you why let's see so this is saying uh if we put an equals do this okay we're gonna make this man that would be another switch statement oh okay um let's do that let's create a function that is uh get total something like that and we're going to extract this functionality into that function i'm using lots of switch statements here because there's a great benefit in typescript of using switch statements if you use switch statements on enum um it will it will fail will cause a compiler error if i believe it's a compiler if you don't handle all of the types of that enum so let me give you an example here so up here in my operations builder i'm handling two input types numerical and operator everything's working great here what if there is a new input type like let's say we want to add in a memory function to this calculator so let me add in a memory type if i hit save um i would expect oh calc input doesn't have that okay uh so cal can put let's say that's a memory if i hit save here with no syntax errors i get a giant error and that's because uh there's going to be several errors in here but that's because i'm not handling all of the cases in that switch statement so that is one of the benefits of using switch statements is typescript will point out to you when you're not hand properly handling all cases so let's let's uh let's make it get total function and what i want from get total is i want to give it a list of operations so that will be an array of operation and the total will just simply be a number so we'll return that so i'm essentially we're going to start with this so that's our very simplified version let's say get total of operations okay there we go this is looking a little cleaner okay if we just save that everything should work as it did before um but we still have this failing test down here of where we we're using a subtract and it's adding instead of subtracting so let's go in here and we're going to switch this up so again we're always always adding here let's uh let's change that up so we're going to turn this into a function and once again we're going to pull out our old trusty switch statement and we're going to switch on the operation operator again a benefit of using the switch statement is we have to handle all of the operators here or typescript will give us an error so that's that's a nice benefit so let's handle all of the different operator types so we have add equals and subtract i'll do those in add subtract and equals order okay if it's an ad we're going to return sum plus the new value if it's a subtract we're going to subtract it if it's an equals equals is always going to have a zero so we can um just simply return the previous value and you see when i did that um the errors went away so if i hit save here i think this will make it pass and it did um that is awesome so we have a suite of passing tests that handle a variety of cases and we have our calculator brain which is about 100 lines of code including several types that handle all the calculations i should really point out that none of this has anything to do with the react none of the the calculator function here has anything to do with react so this could be pulled into uh some other different place where maybe you weren't working on a calculator ui maybe you were doing something server-side where you could reuse this functionality but now that we have this in place let's get to the fun part where we can actually hook this up with our interface and hopefully see this function so let's do that now alrighty let's pull in our ui again i will get rid of the apple calculator ui that's cheating okay we have our calculator ui and let's start implementing this with react i'm going to open up our calculator component and close our test file whoa lots of red i don't know why it doesn't like my jsx setting that is so weird to me i'm there must be some sort of mismatch of my typescript version i have installed and uh create react app or something i i really don't know but i'm gonna keep adjusting that value through this video and figure that out later okay so we still have all of our buttons we need to somehow use this state and build up our list of inputs so i'm in our calculator function i'm i'm going to switch sides here i'm going to put the calculator function on the left i'm in the calculator component and essentially what i want to happen is with each and every button press that needs to add to let's see actually our calculator tests are easier to read with each and every button press i need to create an input here and append that to a list so that means we're going to have state in this calculator component and so for that we can use use state so i'm going to make an inputs state and we'll have set inputs here and that will be a used state which should import from react um and that will start out as an array and i think this has a generic on it or i could say that this is an uh this is this state is an array of calculator inputs yes that works so what i want is i want to be able to click on a button and have that input and then the display value needs to be generated from the calculator state so we need to get our state which is calculated from our module from our get state function by passing it all of the inputs and then we should be able to display the state dot display value okay so if i save this i'm going to expect this hard-coded 42 on the right to change to zero yes okay that took a little while um that changed to zero great getting there um now i want to add click handlers to the numbers we'll just start out with the numbers so far um so let's do that let's do on click and we will make a function that makes a function that has a handler so let's say a handle uh numerical and then we'll pass it what number this is i'm gonna get super fancy i'm gonna move over i'm gonna copy this column of numbers move back over and paste yes okay so handle numerical okay great this on click is showing me red let me fix that before i implement our handle numerical handler so let's pull up our button the reason that's red is because we don't have on click in our props now i could just directly add on click to our props here but there's a more proper way to do it which is to utilize um utilize the div props built into react and i am blanking on that right now it's like it's uh so what i'm gonna do is i'm gonna say i give me the react dot uh it's uh element prop component props for div react component div props type type script shhh that's not it let's see components props what do you pass into that oh my gosh my d key is not working okay i'm not good at googling today okay let's look at the docs here that's probably a better idea okay component element type element type oh my gosh okay one second i'm totally blanking i'm gonna go look at this in another project i have one second i'm gonna put just me on here in case i bring something up okay looking this up component props let's see it is html ah okay that's it okay we can fix this this is okay what we need to do back on track here what we need to do is we have our props type that we're passing in for the button let me pull up this we have our props type that we're passing in for the button we're wanting to put and we're wanting to add to that the on click handler now the very simple way would be just to add another property called on click here and add a function however we don't want to keep doing that for anything that the user might want to do so what we want is we want to kind of inherit almost anything that react has built in that can go on this element which happens to be a button so in addition to props we're going to intersect that with react's html props and pass in which type of html element this is so this is an html button element let's see i don't need that anymore um so now we are allowing any uh button props to also be passed on to here type button number is not assignable to type undefined um oh button type oh that's weird that's weird let me take this out see if that works okay save that that's odd let's see button type number is not assignable to type undefined i think that probably has something to do with my default value down here if i didn't have that let's say i force the user to put that in oh type i'm going to pull these out oh my gosh what's going on it really doesn't like that this type anymore i wonder if that has something to do is this supposed to be component props do i have that wrong you're seeing it live okay it really doesn't like my type here one second let me take a look at this it okay yeah it's html i had that right first it's html props oops and html div element um but it just does not like this type okay so it may be this name type that it's not liking let me do button type perhaps it doesn't like that name button type is not assignable to never never huh one zero okay there we go it didn't like okay that's something interesting to look into it once i added in 3x default types it doesn't like the prop named type so that's weird let's keep going um where was i let me pull something back in because i don't want to have to specify these if i don't have to let me still have a default button type of operation let me still make this optional and let me still let me remove operation here that cleans this up a lot this is probably going to air out real fast because i didn't save all okay we're back to where we were only we just changed type to button type there we go um but now we are wanting to implement on click handlers uh now we're but now that we're accepting all of the props that a div can have in react uh or actually it's not a div it is a button that's our that's our type it's a button i need to spread this and do rest props and add that back onto our button down here and we can't have children so let me pull that out here let's see jsx tags children props except expects type never rest props should not have children if i'm pulling it out that's weird but only a single child that's very odd let me pull rest props out hmm i don't think i've ever had that happen okay i am going to just add in on click here i waved the white flag uh i do want to figure out how i can get a spread of all props in here but i'm not going to waste your time doing that now uh something's funky with children but i'm not sure what okay so we're just going to go ahead and put on click here and continue on that later so let's take all of our on click handlers that we made let's put those back on here and now finally let's create this handling function that handles numerical this is going to be a higher order function so we have handle numerical we're passing in a value and that's calling the function directly but we need that to return a handler function so we have const handle numerical which will receive the number value and that will itself return a handler so with that handler we need to add an input value to our calculator so what we will do is we will do a set inputs and we will take our previous and return everything that we already had inputs but add in another one so let's add in an input type of numerical and the value is value so this will append our inputs which will cause react to re-render which will cause calculation to get a new state which should cause us to get a new display value so this is the magical moment let's see if it works only numbers work so far okay let's punch in one let's refresh let's punch in one nah okay let's see what's going on let me let me look at my console i don't see anything here okay that's fine let me do um do the all console.log so i'm going to log every time this renders i'm going oops i'm going every time this renders i'm going to log inputs okay one one one one oh we actually have lots of ones okay so that to me says our display value is probably not working so let's add a test case for this um so we already have derives display value upon new numerical input but what we don't have is derives display value upon a first numerical input so we're just simply trying to add a one here and we want it to display a one so let's see if that test passes where i'm expecting not uh and it doesn't okay that that means it's probably not an issue in our ui it's actually an issue in our calculator brain here um so we have a case that we haven't accounted for let's take a look in our calculator logic so we have a list of inputs which is only one item long that should build up a builder with a working operation and uh if i'm looking at the display value let's see last operation okay interesting i see the problem here's the problem if our last operation is pressing the equals button we should display the total if our last operation is not an equals button we should display that value the problem is we we don't yet have an operation built up in our array we've hit we've pressed the number one and in an array of operations here it's still empty because we haven't hit a plus or anything to append that operation and what we have here is if there's no operation return a display value of zero that's not what we want what we actually want is return the builder's working value that's what we want so if i save this i'm i expect this test to pass it did and i actually saw that happen in the ui first so i'm going to refresh this ui we're going to start from scratch so if i click one one excellent if i click two excellent if i click plus nothing will happen because we haven't done that yet so we're getting farther along we're almost there let me go back to my calculator so we we're handling all of the numbers uh now let's handle the operations so let me do uh get fancy here okay on click we want to handle operation and operate hand it's really handle operator operator type and then it's going to be a this one's subtract this one's plus and this one's equals so now let's create a handler function for that and it's very similar to this however i'm going to create a little helper here i'm going to create an append uh input and that will accept uh one calculator input and we're not going to return anything that's going to handle this logic of appending an item to the previous inputs array so i'm going to cut that paste that up here i'm actually going to take that out so this append input will handle taking an input appending it and so now i'm going to use that here to append this input that will just make this helper function a little easier and it will also help us create our next one which is handle operator ignore everything on the right there handle operator and this is going to receive an operator type and it's going to create an oper operator input with the operator type that's passed in okay it should be compiling in the background um because of react 17's uh improved reloading it actually maintains the state interestingly enough even through our errors there so i'm going to hit plus and that switches to zero we need to talk about that in a moment now i'm gonna hit three that changes to three and now i'm gonna hit equals fifteen yes it only took three hours to develop this calculator um that's 15. now that is that's uh that is all of our basic very basic operations here um we have not covered clear all clear and oops yet we'll do that momentarily um but there is one thing i want to check i don't think that this functionality is quite uh the functionality that a calculator normally has so let's let's test this out uh i can't clear i have to refresh okay if i hit one i get a one if i hit one i get a one great if i hit two i get a two or twelve at two i get twelve if i hit plus notice that value doesn't change whereas if i hit plus over here uh my value reset to zero um so that's something we probably want to change in hours um so then 3 equals 15 3 equals 15. let's see if we can do that so let's test this let's add yet another case now i'm really glad i i made these tests derives display value upon operator operator input so we want to test if i enter 1 2 add instead of resetting the display to 0 i want to keep it at 12 like most calculators so let me hit save i'm expecting this test to fail it does expected 12 got zero okay i'm not entirely sure how i'm going to implement that okay so our get state is equals our default is our builder working value what i might say is if our builder working value is zero to display the last operation value that would be one way to go about it however what happens in the calculator if i do let's say one two plus what happens if i hit zero here okay if i hit zero it it does it it accepts that as an input and it resets the display so i can't just check if the last i can't just check if the working value is zero um what i might need to do is completely clear out the working value and really track if the working value is purposefully null meaning we we have not um we have not added anything so that's what i'm going to do okay so what we're going to do is we're going to change this let me show you what's happening whenever you hit plus that's an operator if it's not equals it comes down here and it resets the value to zero along with the new operator what i want to do is i want to say that this value is in fact null it's purposefully null it's empty um we know we haven't put in anything there yet now that's created a giant type situation for us but that's because our working value um it requires a number as its value um so what i'm going to say here is i'm going to say that this is um uh it's a uh we want to make one of the props not required and that is uh i'm for getting it off the top of my head once again okay so i'm gonna say operator operation operation type operator type and then the value is going to be a number or null could be null okay um so let's see here let's see what's going on now it's really mad at me again okay i really do need to figure this out let's see let me reset this back put this back to zero um now i'm wondering if i don't know that i want to go through and change operation to have a nullable value because um that is a lot of null checks um what i could do is only let's see let me take a look at this okay i may not have to change any of that so the display [Music] the display should only um display the last working value if the last input was not an operation okay this may actually just be a one line change here okay so the display value we're currently providing our working value um but let's do that only if our last input was a number so let me i'm building up last operation here let me also build up last input so i'm going to use my use my same format here to get the last one i should probably just really make a helper function or pull in low dash okay so this is going to be an input or null so if we have a last input and that last input's type is numerical then display the working value otherwise if it's not numerical that means right now it's an operator so we want to display the um actually i think the current total so let's extract out the total up here i think that's what we want okay all of our test pass let's see see if it passes the smell test okay one one two twelve plus it still says 12. that is the functionality we were looking for now if i hit 3 i'm wanting this to reset to 3. excellent if i hit equals it should be 15. yes this is where you make a commit and call it a day and go home um but we're not going to do that yet we have two more buttons to implement and then we'll be done we have the all clear button and the oops button uh so let's uh we're actually not going to do that in the calculator brain at all we're done with the calculator brain i think that's working out i think in fact let me actually let me do i haven't tested two equals yet if i do a minus 5 here do i get 10 that was a close one okay uh let us handle the all clear and oops i'm going to handle this in the calculator component itself i'm not going to make this a part of the logic you could do that um but i think it's i think that'll be easier to do this in that calculator component itself all clear what is what all clear essentially means is we want to erase all the inputs and reset everything back to zero so if i hit all clear right now it doesn't do anything so let me add an on click handler and we'll say handle all clear we'll create a handler function for that handle all clear doesn't need to accept anything and all it does is it sets the inputs to an empty array so let's save that i kind of needed an indicator if this reloaded all clear zero okay excellent let me refresh one five all clear there we go zero that was easy okay now oops oops may be just as easy let's see um let's do a similar thing here on click handle oops now if you're just joining or if you don't remember the oops button is going to undo one input at a time so no matter if you hit a number or if you hit plus minus or equals you can hit the oops button and undo what you just did so let's make a handle handler doesn't need to accept anything and what this needs to do is it needs to take the inputs but remove the last one and i want to do that in an immutable fashion so we are going to set the inputs to whatever the previous inputs were but we are going to slice or splice it i'm pretty sure it's sliced we're going to start at zero and i think we can do minus one to um go to the second to last it may be a minus two let me see um so let's do that so let me refresh one two three if i hit oops i want this to go back to one two excellent excellent excellent yes okay i think that's the calculator i hope you had fun that was three and a half hours but we've covered so many topics here uh we've covered up we've covered building this calculator ui using react using style components using css grid we've covered building up a calculator brain using really test driven development or test test adjacent development perhaps um as we wrote our tests and the brain at the same time we handled various cases uh and then when we finally pulled it into the new ui into the ui we did find a few bugs but for the most part we weren't dealing with logic all of the logic is really in the calculator brain aside from the clear and the oops here so i hope you enjoyed this tutorial i hope you got something out of it let me know in the comments if you found this helpful and if you'd like to see more other than that i hope you have a great day and i'll see you next time
Info
Channel: Kevin Wade
Views: 3,509
Rating: undefined out of 5
Keywords: react, typescript, javascript, coding
Id: o2yNF9tTogk
Channel Id: undefined
Length: 207min 24sec (12444 seconds)
Published: Mon Nov 30 2020
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.