Skip to content

Commit

Permalink
style(dev-overlay): refine the error message header styling (vercel#6…
Browse files Browse the repository at this point in the history
…3823)

### What

Polish the UX based on the feedbacks from @sambecker 

* Fix the font that still use mono
* Align the color, to use red for the warnings
* Give the title to build error
* Only highlight the nextjs doc url as link

### After vs Before

#### Runtime Error


<img width="335" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/64f2f692-1eae-41db-9287-046aff9ba112">
<img width="335" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/2d4bad15-745e-4083-ba57-8968c0f321c2">

#### Build Error

<img width="335" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/ecfc3883-4522-40c6-b042-6d85e7ea970e">
<img width="335" alt="image"
src="https://github.com/vercel/next.js/assets/4800338/bbf37ec5-b639-4b95-a87f-9911182e431c">



Closes NEXT-2958
  • Loading branch information
huozhi authored Apr 5, 2024
1 parent a1e5d0b commit 14c8900
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ const splitRegexp = new RegExp(`(${MAGIC_IDENTIFIER_REGEX.source}|\\s+)`)

export const HotlinkedText: React.FC<{
text: string
matcher?: (text: string) => boolean
}> = function HotlinkedText(props) {
const { text } = props
const { text, matcher } = props

const wordsAndWhitespaces = text.split(splitRegexp)

Expand All @@ -20,9 +21,14 @@ export const HotlinkedText: React.FC<{
{wordsAndWhitespaces.map((word, index) => {
if (linkRegex.test(word)) {
const link = linkRegex.exec(word)!
const href = link[0]
// If link matcher is present but the link doesn't match, don't turn it into a link
if (typeof matcher === 'function' && !matcher(href)) {
return word
}
return (
<React.Fragment key={`link-${index}`}>
<a href={link[0]} target="_blank" rel="noreferrer noopener">
<a href={href} target="_blank" rel="noreferrer noopener">
{word}
</a>
</React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,22 @@ export const BuildError: React.FC<BuildErrorProps> = function BuildError({
<Overlay fixed>
<Dialog
type="error"
aria-labelledby="nextjs__container_build_error_label"
aria-describedby="nextjs__container_build_error_desc"
aria-labelledby="nextjs__container_error_label"
aria-describedby="nextjs__container_error_desc"
onClose={noop}
>
<DialogContent>
<DialogHeader className="nextjs-container-build-error-header">
<h4 id="nextjs__container_build_error_label">Failed to compile</h4>
<DialogHeader className="nextjs-container-errors-header">
<h1 id="nextjs__container_errors_label">{'Build Error'}</h1>
<p
id="nextjs__container_errors_desc"
className="nextjs__container_errors_desc"
>
Failed to compile
</p>
{versionInfo ? <VersionStalenessInfo {...versionInfo} /> : null}
</DialogHeader>
<DialogBody className="nextjs-container-build-error-body">
<DialogBody className="nextjs-container-errors-body">
<Terminal content={message} />
<footer>
<p id="nextjs__container_build_error_desc">
Expand All @@ -49,24 +55,25 @@ export const BuildError: React.FC<BuildErrorProps> = function BuildError({
}

export const styles = css`
.nextjs-container-build-error-header {
display: flex;
align-items: center;
.nextjs-container-errors-header > h1 {
font-size: var(--size-font-big);
line-height: var(--size-font-bigger);
font-weight: bold;
margin: var(--size-gap-double) 0;
}
.nextjs-container-build-error-header > h4 {
line-height: 1.5;
margin: 0;
padding: 0;
.nextjs-container-errors-header p {
font-size: var(--size-font-small);
line-height: var(--size-font-big);
white-space: pre-wrap;
}
.nextjs-container-build-error-body footer {
.nextjs-container-errors-body footer {
margin-top: var(--size-gap);
}
.nextjs-container-build-error-body footer p {
.nextjs-container-errors-body footer p {
margin: 0;
}
.nextjs-container-build-error-body small {
.nextjs-container-errors-body small {
color: var(--color-font);
}
`
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ type ReadyErrorEvent = ReadyRuntimeError

type DisplayState = 'minimized' | 'fullscreen' | 'hidden'

function isNextjsLink(text: string): boolean {
return text.startsWith('https://nextjs.org')
}

function getErrorSignature(ev: SupportedErrorEvent): string {
const { event } = ev
switch (event.type) {
Expand Down Expand Up @@ -268,19 +272,20 @@ export function Errors({
id="nextjs__container_errors_desc"
className="nextjs__container_errors_desc"
>
{error.name}: <HotlinkedText text={error.message} />
{error.name}:{' '}
<HotlinkedText text={error.message} matcher={isNextjsLink} />
</p>
{hydrationWarning && (
<>
<p
id="nextjs__container_errors__extra"
className="nextjs__container_errors__extra"
id="nextjs__container_errors__notes"
className="nextjs__container_errors__notes"
>
{hydrationWarning}
</p>
{activeError.componentStackFrames?.length ? (
<PseudoHtmlDiff
className="nextjs__container_errors__extra_code"
className="nextjs__container_errors__component-stack"
hydrationMismatchType={hydrationErrorType}
componentStackFrames={activeError.componentStackFrames}
firstContent={serverContent}
Expand Down Expand Up @@ -312,52 +317,54 @@ export const styles = css`
font-size: var(--size-font-big);
line-height: var(--size-font-bigger);
font-weight: bold;
margin: var(--size-gap-double) 0;
margin: calc(var(--size-gap-double) * 1.5) 0;
color: var(--color-title-h1);
}
.nextjs-container-errors-header small {
font-size: var(--size-font-small);
color: var(--color-accents-1);
margin-left: var(--size-gap-double);
}
.nextjs-container-errors-header small > span {
font-family: var(--font-stack-sans);
font-family: var(--font-stack-monospace);
}
.nextjs-container-errors-header p {
font-family: var(--font-stack-sans);
font-size: var(--size-font-small);
line-height: var(--size-font-big);
margin: 0;
margin-top: var(--size-gap);
white-space: pre-wrap;
}
.nextjs__container_errors_desc {
padding-left: var(--size-gap);
border-left: 4px solid var(--color-accents-1);
font-family: var(--font-stack-monospace);
padding: var(--size-gap) var(--size-gap-double);
border-left: 2px solid var(--color-text-color-red-1);
margin-top: var(--size-gap);
font-weight: 500;
color: var(--color-stack-subline);
font-weight: bold;
color: var(--color-text-color-red-1);
background-color: var(--color-text-background-red-1);
}
.nextjs__container_errors__extra {
margin: var(--size-gap-half) 0;
color: var(--color-stack-headline);
font-weight: 500;
p.nextjs__container_errors__notes {
margin: var(--size-gap-double) auto;
color: var(--color-stack-notes);
font-weight: 600;
font-size: 15px;
}
.nextjs-container-errors-header > div > small {
margin: 0;
margin-top: var(--size-gap-half);
}
.nextjs-container-errors-header > p > a {
color: inherit;
font-weight: bold;
}
.nextjs-container-errors-body > h2:not(:first-child) {
margin-top: calc(var(--size-gap-double) + var(--size-gap));
}
.nextjs-container-errors-body > h2 {
color: var(--color-title-color);
margin-bottom: var(--size-gap);
font-size: var(--size-font-big);
}
.nextjs__container_errors__extra_code {
margin-top: var(--size-gap);
.nextjs__container_errors__component-stack {
padding: 12px 32px;
color: var(--color-ansi-fg);
background: var(--color-ansi-bg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@ export function Base() {
--color-font: #757575;
--color-backdrop: rgba(17, 17, 17, 0.2);
--color-title-color: #1f1f1f;
--color-stack-h6: #222;
--color-stack-headline: #666;
--color-stack-subline: #999;
--color-stack-notes: #777;
--color-accents-1: #808080;
--color-accents-2: #222222;
--color-accents-3: #404040;
--color-text-color-red-1: #ff5555;
--color-text-background-red-1: #fff9f9;
--font-stack-monospace: 'SFMono-Regular', Consolas, 'Liberation Mono',
Menlo, Courier, monospace;
--font-stack-sans: -apple-system, BlinkMacSystemFont, sans-serif;
--font-stack-sans: -apple-system, 'Source Sans Pro', sans-serif;
--color-ansi-selection: rgba(95, 126, 151, 0.48);
--color-ansi-bg: #111111;
Expand Down Expand Up @@ -61,11 +66,15 @@ export function Base() {
--color-font: white;
--color-backdrop: rgb(44, 44, 46);
--color-title-color: #fafafa;
--color-stack-h6: rgb(200, 200, 204);
--color-stack-headline: rgb(99, 99, 102);
--color-stack-notes: #a9a9a9;
--color-stack-subline: rgb(121, 121, 121);
--color-accents-3: rgb(118, 118, 118);
--color-text-background-red-1: #2a1e1e;
}
}
Expand Down
15 changes: 8 additions & 7 deletions test/development/acceptance-app/ReactRefreshLogBox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
export default function Index() {
const boom = useCallback(() => {
throw new Error('end http://nextjs.org')
throw new Error('end https://nextjs.org')
}, [])
return (
<main>
Expand Down Expand Up @@ -435,7 +435,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
export default function Index() {
const boom = useCallback(() => {
throw new Error('http://nextjs.org start')
throw new Error('https://nextjs.org start')
}, [])
return (
<main>
Expand Down Expand Up @@ -480,7 +480,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
export default function Index() {
const boom = useCallback(() => {
throw new Error('middle http://nextjs.org end')
throw new Error('middle https://nextjs.org end')
}, [])
return (
<main>
Expand Down Expand Up @@ -525,7 +525,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
export default function Index() {
const boom = useCallback(() => {
throw new Error('multiple http://nextjs.org links http://example.com')
throw new Error('multiple https://nextjs.org links http://example.com')
}, [])
return (
<main>
Expand All @@ -541,8 +541,9 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {

const header4 = await session.getRedboxDescription()
expect(header4).toMatchInlineSnapshot(
`"Error: multiple http://nextjs.org links http://example.com"`
`"Error: multiple https://nextjs.org links http://example.com"`
)
// Do not highlight example.com but do highlight nextjs.org
expect(
await session.evaluate(
() =>
Expand All @@ -551,7 +552,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
.shadowRoot.querySelectorAll('#nextjs__container_errors_desc a')
.length
)
).toBe(2)
).toBe(1)
expect(
await session.evaluate(
() =>
Expand All @@ -575,7 +576,7 @@ describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => {
) as any
).href
)
).toMatchSnapshot()
).toBe(null)

await cleanup()
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,21 +60,19 @@ Syntax error: Selector "button" is not pure (pure selectors must contain at leas
| ^"
`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 1`] = `"Error: end https://nextjs.org"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 2`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 3`] = `"Error: https://nextjs.org start"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 4`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 5`] = `"Error: middle https://nextjs.org end"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 6`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 9`] = `"http://example.com/"`;
exports[`ReactRefreshLogBox app default logbox: anchors links in error messages 8`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app default module init error not shown 1`] = `
"index.js (3:7) @ eval
Expand Down Expand Up @@ -154,21 +152,19 @@ Parsing css source code failed
Selector is not pure (pure selectors must contain at least one local class or id), (lightningcss, Selector(button, specificity = 0x1))"
`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 1`] = `"Error: end http://nextjs.org"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 2`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 1`] = `"Error: end https://nextjs.org"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 3`] = `"Error: http://nextjs.org start"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 2`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 4`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 3`] = `"Error: https://nextjs.org start"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 5`] = `"Error: middle http://nextjs.org end"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 4`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 6`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 5`] = `"Error: middle https://nextjs.org end"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 8`] = `"http://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 6`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 9`] = `"http://example.com/"`;
exports[`ReactRefreshLogBox app turbo logbox: anchors links in error messages 8`] = `"https://nextjs.org/"`;
exports[`ReactRefreshLogBox app turbo module init error not shown 1`] = `
"index.js (3:7) @ <unknown>
Expand Down
Loading

0 comments on commit 14c8900

Please sign in to comment.