Skip to content

Commit

Permalink
Updates error view with new design (#13197)
Browse files Browse the repository at this point in the history
* Update error occurred view with updated design
Add the ability to set CTA button text and click handler
Migrate to SCSS module

Signed-off-by: Edmundo Ruiz Ghanem <edmundo@airbyte.io>

* Move error view title string to en file

* Remove navigation stuff from BaseClearView because it's no longer used
Update error views to have go back cta

* Update errorView.title from three dots to ellipsis

Co-authored-by: Tim Roes <tim@airbyte.io>

* Remove alt text from ErrorOccurredView image

Co-authored-by: Tim Roes <tim@airbyte.io>

* Crop biting-nails img and update style

Co-authored-by: Tim Roes <tim@airbyte.io>
  • Loading branch information
edmundito and timroes authored Jun 2, 2022
1 parent b5b43d3 commit bf0a1c3
Show file tree
Hide file tree
Showing 12 changed files with 125 additions and 86 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
65 changes: 30 additions & 35 deletions airbyte-webapp/src/components/ApiErrorBoundary/ApiErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,15 @@
import React from "react";
import { FormattedMessage } from "react-intl";
import { useQueryErrorResetBoundary } from "react-query";
import { NavigateFunction, useNavigate } from "react-router-dom";
import { useLocation } from "react-use";
import { LocationSensorState } from "react-use/lib/useLocation";
import styled from "styled-components";

import { Button } from "components/base/Button";

import { isVersionError } from "core/request/VersionError";
import { ErrorOccurredView } from "views/common/ErrorOccurredView";
import { ResourceNotFoundErrorBoundary } from "views/common/ResorceNotFoundErrorBoundary";
import { StartOverErrorView } from "views/common/StartOverErrorView";

const RetryContainer = styled.div`
margin-top: 20px;
display: flex;
justify-content: center;
`;

interface ApiErrorBoundaryState {
errorId?: string;
message?: string;
Expand All @@ -30,17 +22,18 @@ enum ErrorId {
UnknownError = "unknown",
}

interface ApiErrorBoundaryProps {
hideHeader?: boolean;
}

interface ApiErrorBoundaryHookProps {
location: LocationSensorState;
onRetry?: () => void;
navigate: NavigateFunction;
}

interface ApiErrorBoundaryProps {
onError?: (errorId?: string) => void;
}

class ApiErrorBoundary extends React.Component<
ApiErrorBoundaryProps & ApiErrorBoundaryHookProps,
ApiErrorBoundaryHookProps & ApiErrorBoundaryProps,
ApiErrorBoundaryState
> {
state: ApiErrorBoundaryState = {};
Expand All @@ -66,6 +59,9 @@ class ApiErrorBoundary extends React.Component<

if (location !== prevProps.location) {
this.setState({ errorId: undefined, didRetry: false });
this.props.onError?.(undefined);
} else {
this.props.onError?.(this.state.errorId);
}
}

Expand All @@ -74,47 +70,46 @@ class ApiErrorBoundary extends React.Component<

render(): React.ReactNode {
const { errorId, didRetry, message } = this.state;
const { onRetry, hideHeader, children } = this.props;
const { onRetry, navigate, children } = this.props;

if (errorId === ErrorId.VersionMismatch) {
return <ErrorOccurredView message={message} />;
}

if (errorId === ErrorId.ServerUnavailable && !didRetry) {
return (
<ErrorOccurredView message={<FormattedMessage id="webapp.cannotReachServer" />} hideHeader={hideHeader}>
{onRetry && (
<RetryContainer>
<Button
onClick={() => {
this.setState({ didRetry: true, errorId: undefined });
onRetry?.();
}}
>
<FormattedMessage id="errorView.retry" />
</Button>
</RetryContainer>
)}
</ErrorOccurredView>
<ErrorOccurredView
message={<FormattedMessage id="webapp.cannotReachServer" />}
ctaButtonText={<FormattedMessage id="errorView.retry" />}
onCtaButtonClick={() => {
this.setState({ didRetry: true, errorId: undefined });
onRetry?.();
}}
/>
);
}

return !errorId ? (
<ResourceNotFoundErrorBoundary errorComponent={<StartOverErrorView hideHeader={hideHeader} />}>
{children}
</ResourceNotFoundErrorBoundary>
<ResourceNotFoundErrorBoundary errorComponent={<StartOverErrorView />}>{children}</ResourceNotFoundErrorBoundary>
) : (
<ErrorOccurredView message={<FormattedMessage id="errorView.unknownError" />} hideHeader={hideHeader} />
<ErrorOccurredView
message={<FormattedMessage id="errorView.unknownError" />}
ctaButtonText={<FormattedMessage id="ui.goBack" />}
onCtaButtonClick={() => {
navigate("..");
}}
/>
);
}
}

const ApiErrorBoundaryWithHooks: React.FC<ApiErrorBoundaryProps> = ({ children, hideHeader }) => {
const ApiErrorBoundaryWithHooks: React.FC<ApiErrorBoundaryProps> = ({ children, ...props }) => {
const { reset } = useQueryErrorResetBoundary();
const location = useLocation();
const navigate = useNavigate();

return (
<ApiErrorBoundary location={location} onRetry={reset} hideHeader={hideHeader}>
<ApiErrorBoundary {...props} location={location} navigate={navigate} onRetry={reset}>
{children}
</ApiErrorBoundary>
);
Expand Down
15 changes: 4 additions & 11 deletions airbyte-webapp/src/components/BaseClearView/BaseClearView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,14 @@ const MainInfo = styled.div`
flex-direction: column;
`;

interface BaseClearViewProps {
onBackClick?: React.MouseEventHandler;
hideHeader?: boolean;
}

const BaseClearView: React.FC<BaseClearViewProps> = ({ children, onBackClick, hideHeader }) => {
const BaseClearView: React.FC = ({ children }) => {
const { formatMessage } = useIntl();
return (
<Content>
<MainInfo>
{!hideHeader && (
<Link to=".." onClick={onBackClick}>
<LogoImg src="/logo.png" alt={formatMessage({ id: "ui.goBack" })} />
</Link>
)}
<Link to="..">
<LogoImg src="/logo.png" alt={formatMessage({ id: "ui.goBack" })} />
</Link>
{children}
</MainInfo>
<Version />
Expand Down
1 change: 1 addition & 0 deletions airbyte-webapp/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@
"docs.notFoundError": "We were not able to receive docs. Please click the link above to open docs on our website",
"errorView.notFound": "Resource not found.",
"errorView.notAuthorized": "You don’t have permission to access this page.",
"errorView.title": "Oops! Something went wrong…",
"errorView.retry": "Retry",
"errorView.unknown": "Unknown",
"errorView.unknownError": "Unknown error occurred",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ const DestinationItemPage: React.FC = () => {
/>

<Suspense fallback={<LoadingPage />}>
<ApiErrorBoundary hideHeader>
<ApiErrorBoundary>
<Routes>
<Route
path="/settings"
Expand Down
21 changes: 15 additions & 6 deletions airbyte-webapp/src/pages/OnboardingPage/OnboardingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const OnboardingPage: React.FC = () => {
const { hasConnections, hasDestinations, hasSources } = useCurrentWorkspaceState();

const [animateExit, setAnimateExit] = useState(false);
const [hasApiError, setHasApiError] = useState(false);

const afterUpdateStep = () => {
setAnimateExit(false);
Expand All @@ -93,11 +94,15 @@ const OnboardingPage: React.FC = () => {
return (
<ConnectorDocumentationWrapper>
<ScreenContent>
{currentStep === StepType.CREATE_SOURCE ? (
<LetterLine exit={animateExit} />
) : currentStep === StepType.CREATE_DESTINATION ? (
<LetterLine onRight exit={animateExit} />
) : null}
{!hasApiError && (
<>
{currentStep === StepType.CREATE_SOURCE ? (
<LetterLine exit={animateExit} />
) : currentStep === StepType.CREATE_DESTINATION ? (
<LetterLine onRight exit={animateExit} />
) : null}
</>
)}
<Content
big={currentStep === StepType.SET_UP_CONNECTION}
medium={currentStep === StepType.INSTRUCTION || currentStep === StepType.FINAL}
Expand All @@ -119,7 +124,11 @@ const OnboardingPage: React.FC = () => {
<FormattedMessage id={`onboarding.create${TITLE_BY_STEP[currentStep]}.text`} />
</TitlesBlock>
)}
<ApiErrorBoundary hideHeader>
<ApiErrorBoundary
onError={(error) => {
setHasApiError(!!error);
}}
>
{currentStep === StepType.INSTRUCTION && (
<WelcomeStep onNextStep={() => setCurrentStep(StepType.CREATE_SOURCE)} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const SourceItemPage: React.FC = () => {
/>

<Suspense fallback={<LoadingPage />}>
<ApiErrorBoundary hideHeader>
<ApiErrorBoundary>
<Routes>
<Route
path="/settings"
Expand Down
28 changes: 0 additions & 28 deletions airbyte-webapp/src/views/common/ErrorOccurredView.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@use "../../../scss/colors";

.errorOccurredView {
min-height: 400px;
height: calc(100% - 70px);
width: 100%;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
text-align: center;

.octavia {
width: 100px;
height: 128px;
margin-bottom: 24px;
}

background: radial-gradient(
35.57% 35.57% at 50% 50%,
colors.$whiteColor 0%,
colors.$whiteColor 55.87%,
colors.$backgroundColor 100%
);

.content {
max-width: 600px;
text-align: center;
}

.message {
margin-bottom: 30px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from "react";
import { FormattedMessage } from "react-intl";

import { Button, H2 } from "components";

import styles from "./ErrorOccurredView.module.scss";

interface ErrorOccurredViewProps {
message: React.ReactNode;
ctaButtonText?: React.ReactNode;
onCtaButtonClick?: React.MouseEventHandler;
}

export const ErrorOccurredView: React.FC<ErrorOccurredViewProps> = ({ message, onCtaButtonClick, ctaButtonText }) => {
return (
<div className={styles.errorOccurredView}>
<div className={styles.content}>
<img src="/images/octavia/biting-nails.png" alt="" className={styles.octavia} />
<H2 center>
<FormattedMessage id="errorView.title" />
</H2>
<p className={styles.message}>{message}</p>
{onCtaButtonClick && ctaButtonText && (
<Button size="xl" onClick={onCtaButtonClick}>
{ctaButtonText}
</Button>
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./ErrorOccurredView";
11 changes: 7 additions & 4 deletions airbyte-webapp/src/views/common/StartOverErrorView.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import React from "react";
import { FormattedMessage } from "react-intl";
import { useNavigate } from "react-router-dom";

import { ErrorOccurredView } from "views/common/ErrorOccurredView";

interface StartOverErrorViewProps {
message?: string;
onReset?: () => void;
hideHeader?: boolean;
}

export const StartOverErrorView: React.FC<StartOverErrorViewProps> = ({ message, onReset, hideHeader }) => {
export const StartOverErrorView: React.FC<StartOverErrorViewProps> = ({ message, onReset }) => {
const navigate = useNavigate();

return (
<ErrorOccurredView
message={message ?? <FormattedMessage id="errorView.notFound" />}
onBackClick={() => {
ctaButtonText={<FormattedMessage id="ui.goBack" />}
onCtaButtonClick={() => {
onReset?.();
navigate("..");
}}
hideHeader={hideHeader}
/>
);
};

0 comments on commit bf0a1c3

Please sign in to comment.