-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add Error component #484
add Error component #484
Conversation
This pull request is being automatically deployed with Vercel (learn more). 🔍 Inspect: https://vercel.com/c0d3/c0d3-app/27rl0luxw |
Codecov Report
@@ Coverage Diff @@
## master #484 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 94 95 +1
Lines 1284 1287 +3
Branches 268 268
=========================================
+ Hits 1284 1287 +3
|
components/Error.tsx
Outdated
src: string | ||
message?: string | ||
} | ||
const Error: React.FC<ErrorProps> = ({ title, message, src }) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the images
bound to specific messages and or titles? For example, every time message equals No data
, do we want to display the 500 error image with a Internal server error
title?
Similarly, when the message equals 404 error
, do we want to always
display the 404
error image with a Page not found
title? If we have images that are only
used for specific error messages or titles, then I don't think we should pass in src
as a prop. Rather there can be a single type
prop that determines the title
, image src
, and message
to use.
If it is the case that the image src
is not bound to any error message or title, then I think it would be better to have src
have only a limited number of options(not found, 404
) as a valid input. And instead of src
it should be renamed something like type
. A table can be made to match the corresponding image src
depending on value of type
passed in. This would make the component easier for others to use in the future, since they won't have to determine the correct paths to use as a value for src
.
Something like this:
enum ImageType {
NOT_FOUND = '500.png',
404 = '404.png'
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Titles and background images are tied together. Page not found
is shown with a confused man, Internal server error
results in generic server image, so it makes sense to merge them in a new type somehow. I am not sure if enum is the best way to do so though, why not a simple union?
type ErrorProps = {
type: '404' | '500'
message?: string
}
const Error: React.FC<ErrorProps> = ({ type, message }) => {
...
<Text component="div" size="xl" bold={true}>
{type === '404' ? 'Page not found' : 'Internal server error'}
</Text
....
This way you don't need to export enum every time you need to use Error component.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, unions look like they work too! I only used enums
as an example, which is why I said something like this
. I apologize for the misunderstanding, I should have been more clear in my language and emphasize to make the component easier for others to use in the future
as the main goal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I changed it to union type. If we need more error pages we could extend this union and change ternary operator for a switch.
components/Error.tsx
Outdated
</div> | ||
<div className="col-sm-9"> | ||
<img | ||
src={type === '404' ? '/404.png' : '/500.png'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the choices for type
, the image src paths
shouldn't have to be hardcoded.
src={`/${type}.png`}
components/Error.tsx
Outdated
<div className="d-flex col-sm-3 align-items-center justify-content-center"> | ||
<div className="text-center mt-3"> | ||
<Text component="div" size="xl" bold={true}> | ||
{type === '404' ? 'Page not found' : 'Internal server error'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see a high potential for someone else updating this component in the future, to add another type of error(for example, admin pages can have an unauthorized error for non-admins
).
Therefore, I think this piece of logic can be refactored so that it is easier for others to add another error type in the future.
Right now, the next person to add onto this file may experience these difficulties:
-
Having to visually filter through
html
code to determine where to edit in the fileLayout -> div -> div -> div -> div -> Text
: Have to dig all the way here to find the logic
-
Figuring out that
500
is the error number associated withInternal server error
- With a ternary operator, it's not explicitly clear that
Internal server error
is the intended title for error500
. A person would have to analyze the entire component as a whole, and then make the connection ofOh, there's only one other error type, and it is error 500. So I guess Internal server error is the title for 500
.
- With a ternary operator, it's not explicitly clear that
-
Figuring out the best way to add a third option
- Continuing to use a ternary operator to support a third error type makes the code unnecessarily complex and more unreadable. The next person to add another type to this file would have to go through the process of deciding how to best support a third error type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your initial enum suggestion would be the best approach here in my opinion. With a enum we can directly map an error code to it's associated image, not even needing a ternary operator or switch. And it's also easy to modify in the future by adding new error status codes.
For example:
// status code enum
enum StatusCode {
NOT_FOUND = '404',
INTERNAL_SERVER_ERROR = '500'
}
// default messages map
const errorMessages: ReadonlyMap<StatusCode, string> = new Map([
[StatusCode.NOT_FOUND, 'Page not found'],
[StatusCode.INTERNAL_SERVER_ERROR, 'No data']
])
// passing as a prop to the component
<Error type={StatusCode.NOT_FOUND} ... />
// using on the img src
<img src={`/${type}.png`} />
// get the default message
<Text>
{errorMessages.get(type)}
</Text>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I changed it to enum. It's easier to add new pages this way, on the other hand, using it would be easier with unions IMO since you don't need to export (and figure out extra type). But it's such a minor thing so I'm fine with either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think using the map
object makes things more complex than they have to be. Map
is meant to be used when we have to set memory addresses(such as objects
) as key values, but all the status codes values are just strings.
To be more simple, we can just use a regular object:
const errorTitles: Readonly<{ [key in StatusCode]: string }> = {
[StatusCode.NOT_FOUND]: 'Page not found',
[StatusCode.INTERNAL_SERVER_ERROR]: 'Internal server error'
}
Also, since there is already a message
prop that is passed into the Error
component, I find it confusing that there is another errorMessages
table. I think a more appropriate variable name is errorTitles
, or something that doesn't have message
in it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, I thought that map was faster than object for accessing values (turns out it is faster but for inserting/removing key-values pairs). It's an object now.
components/Error.tsx
Outdated
message?: string | ||
} | ||
const Error: React.FC<ErrorProps> = ({ type, message }) => { | ||
const Error: React.FC<ErrorProps> = ({ code, message }) => { | ||
const errorMessages: ReadonlyMap<StatusCode, string> = new Map([ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this hash table should be on the global scope. The table has constant values that do not depend on the value of props passed into the component. As such, there is no need to generate another errorMessages
table every time this component is used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapping from status codes is out of component scope and it's renamed to errorTitle.
stories/components/Error.stories.tsx
Outdated
} | ||
|
||
export const Internal: React.FC = () => { | ||
const mocks = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this file can be refactored so that there is less duplicated code. The mocks
are exactly the same value in each storybook entry(Internal, NotFound
).
components/Error.tsx
Outdated
import NavLink from './NavLink' | ||
import { Text } from './theme/Text' | ||
export enum StatusCode { | ||
NOT_FOUND = '404', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are the status code numbers
strings? I think it would make more sense to use numbers as numbers, rather than as strings.
Additionally, this removes the need to have a toStatusCode
function that you have in your PR#485
components/Error.tsx
Outdated
</NavLink> | ||
</div> | ||
</div> | ||
<div className="col-sm-9"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This div
is unneeded, you can just add the classname(col-sm-9
) to the image's classes
The two images should be
|
Resaving bitmap as svg won't help much. I don't have original source so it will look look as bad as upscaled png. Should I change it just for the sake of consistency? It would be misleading IMO, it's still a bitmap just in different format. I also added next/image component which should help with image optimization. |
You're right that just converting the png won't help. But even the Next image component won't help with scaling issues. Could you search for some another illustrations then? You can find some free resources here. |
OK, I changed images to svg, see new storybook. I liked the old one better but I couldn't find its source. |
Will you be writing unit tests for this component as well? |
But what is there to test? Error component has no internal logic, storybook snapshots should cover everything. |
@@ -0,0 +1,553 @@ | |||
<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't put all the error
images into the root of the public folder. To be more organized, they should be placed into a folder of their own
This PR adds new Error component. Part of #467.