Files
cypress/packages/launchpad/src/error/BaseError.vue
T
Tim Griesser 92eac2f67e feat: Error standardization (#20323)
* refactor: reworking client-side error shape

* feat: add the CodeFrame to baseerror

* consolidate baseError handling, type fixes

* Fix UNIFY-1164 w/ test cleanup to avoid intercepting

* fix types, cleanup based on review / Brian

* fix: imports / types / tests

* cleanup tests, fix TSError location, add reinitializeCypress mutation

* fix: show correct stack trace file name (#20410)

* Improve comments for regexes / TSError capture

* feat: Add codeframe to error, address PR comments

* update snapshot

* change codeframe impl, per Brian's request

* Attempt to fix test flake

Co-authored-by: ElevateBart <ledouxb@gmail.com>
Co-authored-by: Alejandro Estrada <estrada9166@hotmail.com>
2022-03-02 18:03:52 -05:00

137 lines
4.1 KiB
Vue

<template>
<div
v-if="baseError"
class="mx-auto space-y-32px text-center min-w-476px max-w-848px pt-16px children:text-center"
>
<div>
<h1
v-if="baseError.title"
class="font-medium leading-snug pb-24px text-32px text-gray-900"
data-testid="error-header"
>
<slot name="header">
{{ baseError.title }}
</slot>
</h1>
<!-- eslint-disable vue/multiline-html-element-content-newline -->
<slot name="message">
<Alert
:title="baseError.errorName"
status="error"
body-class="px-0px bg-red-50"
alert-class="bg-red-50"
header-class="bg-red-100 text-red-600 rounded-b-none"
:icon="ErrorOutlineIcon"
icon-classes="icon-dark-red-400"
max-height="none"
>
<div class="border-b-1 border-b-red-100 p-16px pt-0">
<div
ref="markdownTarget"
class="text-red-500"
data-testid="error-message"
v-html="markdown"
/>
<ErrorCodeFrame
v-if="baseError.codeFrame"
:gql="baseError.codeFrame"
/>
</div>
<div
class="m-16px mb-0 overflow-hidden"
>
<Collapsible
disable
max-height="none"
:initially-open="baseError.isUserCodeError"
>
<template #target="{open, toggle}">
<p
class="gap-8px inline-flex items-center justify-center"
:class="{'pb-8px': open}"
:data-cy="`stack-open-${open}`"
>
<i-cy-chevron-right-small_x16
class="min-w-8px min-h-8px transform duration-150 icon-dark-red-400"
:class="{'rotate-90': open}"
/>
<a
href="#"
class="cursor-pointer font-medium outline-none text-red-600 hocus:underline"
@click="toggle()"
@keypress.space.enter.self.prevent="toggle()"
>{{ t('launchpadErrors.generic.stackTraceLabel') }}</a>
</p>
</template>
<pre
data-testid="error-header"
class="bg-white rounded font-light border-1 border-red-200 p-16px overflow-auto"
v-html="baseError.errorStack"
/>
</Collapsible>
</div>
</Alert>
</slot>
<!-- eslint-enable vue/multiline-html-element-content-newline -->
<slot name="stack" />
</div>
<div class="w-full gap-16px inline-flex">
<slot name="footer">
<Button
v-if="props.retry"
size="lg"
variant="primary"
data-testid="error-retry-button"
:prefix-icon="RestartIcon"
prefix-icon-class="icon-dark-white"
@click="retry"
>
{{ t('launchpadErrors.generic.retryButton') }}
</Button>
</slot>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import { gql } from '@urql/vue'
import Button from '@cy/components/Button.vue'
import { useI18n } from '@cy/i18n'
import type { BaseErrorFragment } from '../generated/graphql'
import Alert from '@cy/components/Alert.vue'
import Collapsible from '@cy/components/Collapsible.vue'
import { useMarkdown } from '@packages/frontend-shared/src/composables/useMarkdown'
import RestartIcon from '~icons/cy/restart_x16.svg'
import ErrorOutlineIcon from '~icons/cy/status-errored-outline_x16.svg'
import ErrorCodeFrame from './ErrorCodeFrame.vue'
gql`
fragment BaseError on ErrorWrapper {
title
errorName
errorStack
errorType
errorMessage
isUserCodeError
codeFrame {
...ErrorCodeFrame
}
}
`
const { t } = useI18n()
const props = defineProps<{
gql: BaseErrorFragment
retry?: () => void
onReadDocs?: () => void
}>()
const markdownTarget = ref()
const baseError = computed(() => props.gql)
const { markdown } = useMarkdown(markdownTarget, computed(() => props.gql.errorMessage), { classes: { code: ['bg-error-200'] } })
</script>