What's our blog missing? Comments. Let's add a simple comment engine so people can leave their completely rational, well-reasoned comments on our blog posts. It's the internet, what could go wrong?
There are two main features we need to build:
- Comment form and creation
- Comment retrieval and display
Which order we build them in is up to us. To ease into things, let's start with the fetching and displaying comments first and then we'll move on to more complex work of adding a form and service to create a new comment. Of course, this is Redwood, so even forms and services aren't that complex!
Let's create a component for the display of a single comment. First up, the generator:
Storybook should refresh and our "Generated" Comment story will be ready to go:
Let's think about what we want to ask users for and then display in a comment. How about just their name and the content of the comment itself? And we'll throw in the date/time it was created. Let's update the Comment component to accept a
comment object with those two properties:
Once you save that file and Storybook reloads you'll see it blow up:
We need to update the story to include that comment object and pass it as a prop:
Note that Datetimes will come from GraphQL in ISO8601 format so we need to return one in that format here.
Storybook will reload and be much happier:
Let's add a little bit of styling and date conversion to get this Comment component looking like a nice, completed design element:
It's tough to see our rounded corners, but rather than adding margin or padding to the component itself (which would add them everywhere we use the component) let's add a margin in the story so it only shows in Storybook:
A best practice to keep in mind when designing in HTML and CSS is to keep a visual element responsible for its own display only, and not assume what it will be contained within. In this case a Comment doesn't and shouldn't know where it will be displayed, so it shouldn't add any design influence outside of its container (like forcing a margin around itself).
Now we can see our roundedness quite easily in Storybook:
If you haven't used TailwindCSS before just know that the
min the className is short for "margin" and the
4refers to four "units" of margin. By default one unit is 0.25rem. So "m-4" is equivalent to
We don't want Santa to skip our house for being naughty developers so let's test our Comment component. We could test that the author's name and the body of the comment appear, as well as the date it was posted.
The default test that comes with a generated component just makes sure that no errors are thrown, which is the least we could ask of our components!
Let's add a sample comment to the test and check that the various parts are being rendered:
Here we're testing for both elements of the output
createdAt timestamp: the actual text that's output (similar to how we tested for a blog post's truncated body) but also that the element that wraps that text is a
<time> tag and that it contains a
datetime attribute with the raw value of
comment.createdAt. This might seem like overkill but the point of the
datetime attribute is to provide a machine-readable timestamp that the browser could (theoretically) hook into and do stuff with. This makes sure that we preserve that ability!
What happens if we change the formatted output of the timestamp? Wouldn't we have to change the test?
Yes, just like we'd have to change the truncation text if we changed the length of the truncation. One alternative approach to testing for the formatted output could be to move the date formatting formula into a function that you can export from the Comment component. Then you can import that in your test and use it to check the formatted output. Now if you change the formula the test keeps passing because it's sharing the function with Comment.