From 23eb267499dafee6b0b42c7571bb6c7a2a74f22e Mon Sep 17 00:00:00 2001 From: Darsh Patel Date: Tue, 28 Jul 2020 16:52:28 +0530 Subject: [PATCH] Make Links rendered in the error overlay clickable [ 14017 ] (#14055) Co-authored-by: TodorTotev <51530311+TodorTotev@users.noreply.github.com> --- .../src/internal/container/Errors.tsx | 23 ++++++++++++- .../error-is-clickable/pages/first.js | 3 ++ .../error-is-clickable/test/index.test.js | 34 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 test/integration/error-is-clickable/pages/first.js create mode 100644 test/integration/error-is-clickable/test/index.test.js diff --git a/packages/react-dev-overlay/src/internal/container/Errors.tsx b/packages/react-dev-overlay/src/internal/container/Errors.tsx index cf40378b48..c05f1ca6ff 100644 --- a/packages/react-dev-overlay/src/internal/container/Errors.tsx +++ b/packages/react-dev-overlay/src/internal/container/Errors.tsx @@ -53,6 +53,23 @@ function getErrorSignature(ev: SupportedErrorEvent): string { return '' } +function makeClickable(text: string): JSX.Element[] | string { + // Regex Checks for http:// or https:// + const linkRegex = /https?:\/\/[^\s/$.?#].[^\s]*/gi + if (!linkRegex.test(text)) return text + return text.split(' ').map((word, index, array) => { + if (linkRegex.test(word)) { + return ( + <> + {word} + {index === array.length - 1 ? ' ' : ''} + + ) + } + return index === array.length - 1 ? <>{word} : <>{word} + }) +} + async function getErrorByType( ev: SupportedErrorEvent ): Promise { @@ -242,7 +259,8 @@ export const Errors: React.FC = function Errors({ errors }) { {isServerError ? 'Server Error' : 'Unhandled Runtime Error'}

- {activeError.error.name}: {activeError.error.message} + {activeError.error.name}:{' '} + {makeClickable(activeError.error.message)}

{isServerError ? (
@@ -292,6 +310,9 @@ export const styles = css` margin: 0; margin-top: var(--size-gap-half); } + .nextjs-container-errors-header > p > a { + color: var(--color-ansi-red); + } .nextjs-container-errors-body > h5:not(:first-child) { margin-top: calc(var(--size-gap-double) + var(--size-gap)); diff --git a/test/integration/error-is-clickable/pages/first.js b/test/integration/error-is-clickable/pages/first.js new file mode 100644 index 0000000000..ec1762701e --- /dev/null +++ b/test/integration/error-is-clickable/pages/first.js @@ -0,0 +1,3 @@ +export default function Index() { + throw new Error('This error should be clickable. https://nextjs.org') +} diff --git a/test/integration/error-is-clickable/test/index.test.js b/test/integration/error-is-clickable/test/index.test.js new file mode 100644 index 0000000000..067600e0bc --- /dev/null +++ b/test/integration/error-is-clickable/test/index.test.js @@ -0,0 +1,34 @@ +/* eslint-env jest */ + +import { findPort, killApp, launchApp } from 'next-test-utils' +import webdriver from 'next-webdriver' +import { join } from 'path' + +jest.setTimeout(1000 * 60 * 5) +let app +let appPort +const appDir = join(__dirname, '..') + +describe('Clickable error link', () => { + beforeAll(async () => { + appPort = await findPort() + app = await launchApp(appDir, appPort, { + env: { __NEXT_TEST_WITH_DEVTOOL: 1 }, + }) + }) + afterAll(() => killApp(app)) + + it('Shows error which is clickable and redirects', async () => { + const browser = await webdriver(appPort, '/first') + + await browser.eval(`(function () { + document.querySelector("nextjs-portal") + .shadowRoot + .querySelector("#nextjs__container_errors_desc > a").click() + })() + `) + const url = await browser.url() + + expect(url).toBe('https://nextjs.org/') + }) +})