mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
console: add neon banner for onboarding wizard
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/6003 GitOrigin-RevId: f3556e3b6c9987b7dc139d6185fd909bb87d3fe8
This commit is contained in:
parent
969cb05bdf
commit
a81c028fe2
@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
import { ComponentMeta, Story } from '@storybook/react';
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
@ -64,6 +65,7 @@ export const Error: Story = () => (
|
||||
status={{
|
||||
status: 'error',
|
||||
buttonText: 'Try Again',
|
||||
buttonIcon: <MdRefresh />,
|
||||
errorTitle: 'Error creating database',
|
||||
errorDescription: 'You have exceeded the free project limit on Neon.',
|
||||
}}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { ReactElement } from 'react';
|
||||
import { Button } from '@/new-components/Button';
|
||||
import { IndicatorCard } from '@/new-components/IndicatorCard';
|
||||
|
||||
@ -12,6 +12,7 @@ export type Props = {
|
||||
| {
|
||||
status: 'error';
|
||||
buttonText: string;
|
||||
buttonIcon: ReactElement;
|
||||
errorTitle: string;
|
||||
errorDescription: string;
|
||||
}
|
||||
@ -54,10 +55,11 @@ export function NeonBanner(props: Props) {
|
||||
<Button
|
||||
data-trackid="neon-connect-db-button"
|
||||
data-testid="neon-connect-db-button"
|
||||
mode="primary"
|
||||
mode={status.status === 'loading' ? 'default' : 'primary'}
|
||||
isLoading={status.status === 'loading'}
|
||||
loadingText={status.buttonText}
|
||||
size="md"
|
||||
icon={status.status === 'error' ? status.buttonIcon : undefined}
|
||||
onClick={() => {
|
||||
if (!isButtonDisabled) {
|
||||
onClickConnect();
|
||||
|
@ -1,5 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
import { useNeonOAuth } from './useNeonOAuth';
|
||||
import { useNeonDatabase } from './useNeonDatabase';
|
||||
import { useCreateHasuraDatasource } from './useCreateHasuraDatasource';
|
||||
@ -116,6 +117,7 @@ export function Neon(props: {
|
||||
status: {
|
||||
status: 'error',
|
||||
buttonText: 'Try again',
|
||||
buttonIcon: <MdRefresh />,
|
||||
errorTitle: 'Creating Neon Database failed',
|
||||
errorDescription: `Error creating database: ${neonDBCreationStatus.error}`,
|
||||
},
|
||||
@ -169,6 +171,7 @@ export function Neon(props: {
|
||||
status: {
|
||||
status: 'error',
|
||||
buttonText: 'Try again',
|
||||
buttonIcon: <MdRefresh />,
|
||||
errorTitle: 'Error authenticating with Neon',
|
||||
errorDescription: neonOauthStatus.error.message,
|
||||
},
|
||||
|
@ -0,0 +1,85 @@
|
||||
import React from 'react';
|
||||
import { MdRefresh } from 'react-icons/md';
|
||||
import { ComponentMeta, Story } from '@storybook/react';
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { NeonBanner } from './NeonBanner';
|
||||
|
||||
export default {
|
||||
title: 'features/Onboarding Wizard/NeonConnectBanner',
|
||||
component: NeonBanner,
|
||||
} as ComponentMeta<typeof NeonBanner>;
|
||||
|
||||
export const Base: Story = () => (
|
||||
<NeonBanner
|
||||
onButtonClick={() => window.alert('clicked connect button')}
|
||||
status={{ status: 'default', buttonText: 'Create a Neon Database' }}
|
||||
/>
|
||||
);
|
||||
Base.play = async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// Expect element renders successfully
|
||||
expect(canvas.getByText('Create a Neon Database')).toBeVisible();
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).not.toBeDisabled();
|
||||
};
|
||||
|
||||
export const Creating: Story = () => (
|
||||
<NeonBanner
|
||||
onButtonClick={() => window.alert('clicked connect button')}
|
||||
status={{ status: 'loading', buttonText: 'Creating Neon Database' }}
|
||||
/>
|
||||
);
|
||||
Creating.play = async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// Expect element renders successfully
|
||||
expect(canvas.getByText('Creating Neon Database')).toBeVisible();
|
||||
// Expect button disabled state to be as expected
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).toBeDisabled();
|
||||
};
|
||||
|
||||
export const Error: Story = () => (
|
||||
<NeonBanner
|
||||
onButtonClick={() => window.alert('clicked connect button')}
|
||||
status={{
|
||||
status: 'error',
|
||||
buttonText: 'Try Again',
|
||||
buttonIcon: <MdRefresh />,
|
||||
errorTitle: 'Your Neon Database connection failed',
|
||||
errorDescription: 'You have exceeded the free project limit on Neon.',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
Error.play = async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// Expect element renders successfully
|
||||
expect(canvas.getByText('Try Again')).toBeVisible();
|
||||
expect(canvas.getByText('Try Again')).not.toBeDisabled();
|
||||
|
||||
// Expect element rend
|
||||
expect(
|
||||
canvas.getByText('Your Neon Database connection failed')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
canvas.getByText('You have exceeded the free project limit on Neon.')
|
||||
).toBeVisible();
|
||||
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).toBeVisible();
|
||||
expect(
|
||||
canvas.getByTestId('onboarding-wizard-neon-connect-db-button')
|
||||
).not.toBeDisabled();
|
||||
};
|
@ -0,0 +1,76 @@
|
||||
import React, { ReactElement } from 'react';
|
||||
import { Button } from '@/new-components/Button';
|
||||
import { IndicatorCard } from '@/new-components/IndicatorCard';
|
||||
import { NeonIcon } from './NeonIcon';
|
||||
|
||||
export type Props = {
|
||||
onButtonClick: VoidFunction;
|
||||
status:
|
||||
| {
|
||||
status: 'loading';
|
||||
buttonText: string;
|
||||
}
|
||||
| {
|
||||
status: 'error';
|
||||
buttonText: string;
|
||||
buttonIcon: ReactElement;
|
||||
errorTitle: string;
|
||||
errorDescription: string;
|
||||
}
|
||||
| {
|
||||
status: 'default';
|
||||
buttonText: string;
|
||||
};
|
||||
};
|
||||
|
||||
export function NeonBanner(props: Props) {
|
||||
const { status, onButtonClick } = props;
|
||||
const isButtonDisabled = status.status === 'loading';
|
||||
|
||||
return (
|
||||
<div className="border border-gray-300 border-l-4 border-l-[#297393] shadow-md rounded bg-white p-md">
|
||||
<div className="flex items-center">
|
||||
<div className="flex w-3/4 items-center">
|
||||
<div className="mr-sm">
|
||||
<NeonIcon />
|
||||
</div>
|
||||
<div className="text-lg text-gray-700 ml-sm">
|
||||
<b>Need a new database?</b> Hasura has partnered with Neon to help
|
||||
you seamlessly create your database with their serverless Postgres
|
||||
platform.
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-1/4 justify-end">
|
||||
<Button
|
||||
data-trackid="onboarding-wizard-neon-connect-db-button"
|
||||
data-testid="onboarding-wizard-neon-connect-db-button"
|
||||
mode={status.status === 'loading' ? 'default' : 'primary'}
|
||||
isLoading={status.status === 'loading'}
|
||||
loadingText={status.buttonText}
|
||||
size="md"
|
||||
icon={status.status === 'error' ? status.buttonIcon : undefined}
|
||||
onClick={() => {
|
||||
if (!isButtonDisabled) {
|
||||
onButtonClick();
|
||||
}
|
||||
}}
|
||||
disabled={isButtonDisabled}
|
||||
>
|
||||
{status.buttonText}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{status.status === 'error' && (
|
||||
<div className="mt-md">
|
||||
<IndicatorCard
|
||||
status="negative"
|
||||
headline={status.errorTitle}
|
||||
showIcon
|
||||
>
|
||||
{status.errorDescription}
|
||||
</IndicatorCard>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
import React from 'react';
|
||||
|
||||
type NeonIconProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function NeonIcon(props: NeonIconProps) {
|
||||
const { className } = props;
|
||||
return (
|
||||
<svg
|
||||
width="84"
|
||||
height="25"
|
||||
viewBox="0 0 84 25"
|
||||
fill="none"
|
||||
className={className}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_138_19342)">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0 4.79524C0 3.71972 0.429155 2.68826 1.19305 1.92775C1.95695 1.16725 2.99303 0.739998 4.07334 0.739998L19.5517 0.739998C20.632 0.739998 21.668 1.16725 22.4319 1.92775C23.1958 2.68826 23.625 3.71972 23.625 4.79524V17.9011C23.625 20.2178 20.6798 21.2233 19.2518 19.3946L14.786 13.6753V20.6105C14.786 21.5784 14.3998 22.5067 13.7123 23.1911C13.0248 23.8755 12.0924 24.26 11.1202 24.26H4.07334C2.99303 24.26 1.95695 23.8328 1.19305 23.0722C0.429155 22.3117 0 21.2803 0 20.2048L0 4.79524ZM4.07334 3.98445C3.62316 3.98445 3.25894 4.34705 3.25894 4.79458V20.2048C3.25894 20.6529 3.62316 21.0162 4.07269 21.0162H11.2422C11.4673 21.0162 11.527 20.8346 11.527 20.6105V11.3109C11.527 8.99356 14.4723 7.98808 15.9009 9.81741L20.3667 15.536V4.79524C20.3667 4.34705 20.4087 3.98445 19.9592 3.98445H4.07334Z"
|
||||
fill="#12FFF7"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0 4.79524C0 3.71972 0.429155 2.68826 1.19305 1.92775C1.95695 1.16725 2.99303 0.739998 4.07334 0.739998L19.5517 0.739998C20.632 0.739998 21.668 1.16725 22.4319 1.92775C23.1958 2.68826 23.625 3.71972 23.625 4.79524V17.9011C23.625 20.2178 20.6798 21.2233 19.2518 19.3946L14.786 13.6753V20.6105C14.786 21.5784 14.3998 22.5067 13.7123 23.1911C13.0248 23.8755 12.0924 24.26 11.1202 24.26H4.07334C2.99303 24.26 1.95695 23.8328 1.19305 23.0722C0.429155 22.3117 0 21.2803 0 20.2048L0 4.79524ZM4.07334 3.98445C3.62316 3.98445 3.25894 4.34705 3.25894 4.79458V20.2048C3.25894 20.6529 3.62316 21.0162 4.07269 21.0162H11.2422C11.4673 21.0162 11.527 20.8346 11.527 20.6105V11.3109C11.527 8.99356 14.4723 7.98808 15.9009 9.81741L20.3667 15.536V4.79524C20.3667 4.34705 20.4087 3.98445 19.9592 3.98445H4.07334Z"
|
||||
fill="url(#paint0_linear_138_19342)"
|
||||
/>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M0 4.79524C0 3.71972 0.429155 2.68826 1.19305 1.92775C1.95695 1.16725 2.99303 0.739998 4.07334 0.739998L19.5517 0.739998C20.632 0.739998 21.668 1.16725 22.4319 1.92775C23.1958 2.68826 23.625 3.71972 23.625 4.79524V17.9011C23.625 20.2178 20.6798 21.2233 19.2518 19.3946L14.786 13.6753V20.6105C14.786 21.5784 14.3998 22.5067 13.7123 23.1911C13.0248 23.8755 12.0924 24.26 11.1202 24.26H4.07334C2.99303 24.26 1.95695 23.8328 1.19305 23.0722C0.429155 22.3117 0 21.2803 0 20.2048L0 4.79524ZM4.07334 3.98445C3.62316 3.98445 3.25894 4.34705 3.25894 4.79458V20.2048C3.25894 20.6529 3.62316 21.0162 4.07269 21.0162H11.2422C11.4673 21.0162 11.527 20.8346 11.527 20.6105V11.3109C11.527 8.99356 14.4723 7.98808 15.9009 9.81741L20.3667 15.536V4.79524C20.3667 4.34705 20.4087 3.98445 19.9592 3.98445H4.07334Z"
|
||||
fill="url(#paint1_linear_138_19342)"
|
||||
/>
|
||||
<path
|
||||
d="M19.5516 0.739998C20.6319 0.739998 21.668 1.16725 22.4319 1.92775C23.1958 2.68826 23.625 3.71972 23.625 4.79524V17.9011C23.625 20.2178 20.6797 21.2233 19.2517 19.3946L14.7859 13.6753V20.6105C14.7859 21.5784 14.3997 22.5067 13.7122 23.1911C13.0248 23.8755 12.0924 24.26 11.1201 24.26C11.1735 24.26 11.2265 24.2495 11.2758 24.2292C11.3252 24.2088 11.37 24.179 11.4078 24.1414C11.4456 24.1037 11.4756 24.0591 11.496 24.0099C11.5165 23.9608 11.527 23.9081 11.527 23.8549V11.3109C11.527 8.99356 14.4722 7.98808 15.9009 9.81741L20.3667 15.536V1.55078C20.3667 1.10325 20.0018 0.739998 19.5516 0.739998Z"
|
||||
fill="#B9FFB3"
|
||||
/>
|
||||
<path
|
||||
d="M39.8251 7.66533V13.8171L33.8336 7.66533H30.7151V17.7267H33.5593V11.1149L40.085 17.7267H42.6693V7.66533H39.8251ZM47.9416 15.4844V13.7021H54.3086V11.5605H47.9416V9.90757H55.6657V7.66533H45.0397V17.7267H55.8245V15.4844H47.9416ZM63.3826 18.0573C67.3817 18.0573 69.9805 16.1025 69.9805 12.696C69.9805 9.28952 67.3817 7.33475 63.3826 7.33475C59.3834 7.33475 56.7991 9.28952 56.7991 12.696C56.7991 16.1025 59.3834 18.0573 63.3826 18.0573ZM63.3826 15.6713C61.1592 15.6713 59.7876 14.5933 59.7876 12.696C59.7876 10.7987 61.1736 9.72072 63.3826 9.72072C65.6059 9.72072 66.9775 10.7987 66.9775 12.696C66.9775 14.5933 65.6059 15.6713 63.3826 15.6713ZM80.8821 7.66533V13.8171L74.8906 7.66533H71.772V17.7267H74.6162V11.1149L81.142 17.7267H83.7263V7.66533H80.8821Z"
|
||||
fill="black"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="paint0_linear_138_19342"
|
||||
x1="23.625"
|
||||
y1="24.26"
|
||||
x2="2.95571"
|
||||
y2="0.648685"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#B9FFB3" />
|
||||
<stop offset="1" stopColor="#B9FFB3" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="paint1_linear_138_19342"
|
||||
x1="23.625"
|
||||
y1="24.26"
|
||||
x2="9.60889"
|
||||
y2="18.784"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stopColor="#1A1A1A" stopOpacity="0.9" />
|
||||
<stop offset="1" stopColor="#1A1A1A" stopOpacity="0" />
|
||||
</linearGradient>
|
||||
<clipPath id="clip0_138_19342">
|
||||
<rect
|
||||
width="84"
|
||||
height="23.52"
|
||||
fill="white"
|
||||
transform="translate(0 0.739998)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user