So far, our game has been contained within this single wordleboard component.
It's been working great, but since our application started growing,
this component grew with it.
This component is currently responsible for two things,
rendering the feedback and gathering user input.
This can be a good time for us
to extract one of these concerns into a dedicated component.
Notice that we did not start this project
by already predetermining
which vue components our application is going to need.
Instead, we have opted to wait for these components
to naturally appear as the application evolves.
Okay, let's move everything related to gathering user input
into a dedicated guess input component.
Since most of this logic over here
is going to be related to gathering the guess input,
I think we should be able to duplicate this wordleboard
into a guess input component
and then delete anything that is related to just rendering guess.
So this part over here does not belong to the guess input.
We also don't need to receive the word of the day.
And we don't need to keep track of the guess submitted.
What we are going to do is communicate with the parent
whenever the user truly submits a guess.
The most common way a child component communicates with a parent
is via emitting events.
For us to emit an event in this component, we can make use of defineEmit.
And we're going to define that it emits an event called guess-submitted
that contains a string as the payload
that is going to represent the guess the user is submitting.
We can do so, like this.
Through this, we're determining that our component
emits a guess-submitted event
that has a payload containing only a single element of guess
that is of type string.
Pretty nice, right?
By defining emits like this,
we're leveraging TypeScript to give us pretty nice intellisense.
So check this out.
If I try to emit an event that hasn't been defined, for example, foobar,
you can see that I'm getting a type error, because foobar,
is not an event that I have determined.
And if I provide the correct event with an invalid payload,
I also get an error because the payload must be of type string.
This is a very simple but effective way to make our code base more robust.
Okay, it looks like we're good on the guessInput component.
Now let's make use of it in the worldeboard.
We no longer need this onSubmit method.
We no longer need the formattedGuessInProgress,
nor the guessInProgress,
and we can eliminate unused imports.
All right, let's run our tests.
And we're passing.
Great, I think we're a good point to make a commit.
Extract GuessInput into dedicated component.
Now this could be a good time
to put a little bit of styling into our component.
Feel free to do this in whatever way you prefer.
I'll do it over here and then I'll show you what I've got.
This is what I've got so far.
Since styling isn't the focus of the lesson,
I'll briefly explain what I have done.
But if you want to discuss more about this,
please don't hesitate to reach out to me on Twitter.
For styling, I decided that I wanted to hide the native input type text element
and instead render a formatted guess as a list of letters.
But before I started styling anything,
I wanted to make sure that our input type text was always focused.
This test over here is how I've done it.
And it drives a couple of advanced concepts of vue test utils,
but I'll give you a brief explanation.
One way you can evaluate to see if an element is in focus
is by checking for the active element of the document.
However, your tests won't work for the active element
if you don't explicitly define an attachTo.
Let me demonstrate.
If I run just this test over here, you can see that it's passing,
but if I remove the attachTo from the mount options,
the test now is failing.
And the reason for the failure is that the active element is the body,
not the input.
That's why the attachTo is needed.
And as you can see over here, I'm trying to blur away from the input,
but the input should remain being the active element.
I also want the input to be auto-focused at the start.
To get this test to pass,
I've assigned the attribute of auto-focus to the input,
and I also defined an event handler for blur
that focus back on the input, so the user cannot lose focus of it.
To give a bit of flavor,
I also created this property called data letter,
which keeps track of the letter
that the letter square is currently rendering.
The reason why I wanted to keep track of the letter
is just that I wanted a pop animation to happen every time a letter
would go from empty to something else.
So this is what's happening here.
I'm looking for all the list items that don't have a data letter
of an empty space.
And if that happens, then the pop animation triggers.
Lastly, a key takeaway from this styling over here
is this scoped keyword.
I like to use this by default in my projects
because it forces all the styles defined in this component
to only apply to this component.
Well, these are the main key points I wanted to cover regarding styling.
But if you want to look deeper into this,
the link to the repository is in the description.
And again, don't hesitate to reach out.
The worldeboard itself also had a little bit of revamping looks.
I added a little animation for the feedback passage
to appear on the screen,
and I also centralized the elements of the worldeboard.
And here is a really nice thing about all of this.
I did not have to alter any of our existing tests,
but look at this.
They are all passing.
We did a complete revamp in the looks for the application,
but the core functionality remains unchanged.
Since we wrote our tests
by focusing on behavior instead of implementation,
our tests are all still passing.
Pretty nice, right?
I also want to emphasize that I did my restyling of the app in little chunks.
I would do something, then run the tests,
see they were all passing, and then make a commit.
And sometimes if the commit was still related to styling,
I would just amend with the previous commit for that component.
Make life easier for us.
Commit often. Test often.
Great! Together, we have seen firsthand the benefits of writing flexible tests.
Always strive for writing tests
that are not tied to implementation details,
but rather to behavior and functionality.
Writing tests that are tightly coupled to implementation
will result in a fragile test switch,
which has a higher chance of providing false negatives,
that is, tests failing when they shouldn't.
If you see your tests driving too much into internal properties
and attributes of a component,
it could be a sign that your test should be refactored.
Focusing on behavior, leads to a test switch that is robust,
and not fragile,
which will embrace and facilitate future changes
and refactors to your codebase, as opposed to creating attrition.