console: add onboarding wizard

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/5372
GitOrigin-RevId: 088509185666500ab3d4a8d2db2aaeced4133901
This commit is contained in:
Abhijeet Khangarot 2022-08-19 23:27:44 +05:30 committed by hasura-bot
parent 4a9da49f8d
commit 522c9ea31a
21 changed files with 806 additions and 0 deletions

View File

@ -59,6 +59,7 @@ const Intro: React.FC<Props> = ({ session, startCreation, dispatch }) => {
startCreation();
}
}}
data-trackid="data-tab-heroku-db-button"
>
Create Database
</Button>

View File

@ -32,6 +32,7 @@ const DataSourceFormWrapper: React.FC<DataSourceFormWrapperProps> = ({
}}
disabled={loading}
data-test="connect-database-btn"
data-trackid="data-tab-connect-db-button"
>
{!isEditState ? 'Connect Database' : 'Update Connection'}
</Button>

View File

@ -284,6 +284,7 @@ class Schema extends Component {
size="sm"
className={styles.add_mar_left}
onClick={handleClick}
data-trackid="data-tab-create-table-button"
>
Create Table
</Button>
@ -358,6 +359,7 @@ class Schema extends Component {
color="white"
size="xs"
onClick={trackAllTables}
data-trackid="data-tab-track-all-button"
>
Track All
</Button>
@ -406,6 +408,7 @@ class Schema extends Component {
color="white"
size="xs"
onClick={handleTrackTable}
data-trackid="data-tab-track-table-button"
>
Track
</Button>

View File

@ -0,0 +1,10 @@
import React from 'react';
import { ComponentMeta, Story } from '@storybook/react';
import { Root } from './Root';
export default {
title: 'features/Onboarding Wizard/Root',
component: Root,
} as ComponentMeta<typeof Root>;
export const Base: Story = () => <Root />;

View File

@ -0,0 +1,21 @@
import React, { useReducer } from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { TopHeaderBar, ConnectDBScreen } from './components';
export function Root() {
// dialog cannot be reopened once closed
const [isWizardOpen, closeWizard] = useReducer(() => false, true);
// this dialog is being used to create a layover component over the whole app using react portal, and other handy functionalities radix dialog provides, which otherwise we'll have to implement manually
// note that we have a common radix dialog, but that component has very specific styling, doesn't include react portal, and it did not make sense to extend that to fit this particular one-off use case
return (
<Dialog.Root open={isWizardOpen} onOpenChange={closeWizard}>
<Dialog.Portal>
<Dialog.Content className="fixed top-0 w-full h-full focus:outline-none bg-gray-50 overflow-hidden z-[101]">
<TopHeaderBar />
<ConnectDBScreen closeWizard={closeWizard} />
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
}

View File

@ -0,0 +1,26 @@
import React from 'react';
import { ComponentMeta, Story } from '@storybook/react';
import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
import { ConnectDBScreen } from './ConnectDBScreen';
export default {
title: 'features/Onboarding Wizard/Connect DB screen',
component: ConnectDBScreen,
} as ComponentMeta<typeof ConnectDBScreen>;
export const Base: Story = () => <ConnectDBScreen closeWizard={() => {}} />;
Base.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
// Expect element renders successfully
expect(
await canvas.findByText('Welcome to your new Hasura project')
).toBeVisible();
expect(
await canvas.findByText(
"Let's get started by connecting your first database"
)
).toBeVisible();
};

View File

@ -0,0 +1,51 @@
import React from 'react';
import { useAppDispatch } from '@/store';
import { Button } from '@/new-components/Button';
import _push from '../../../../components/Services/Data/push';
import { OnboardingAnimation, OnboardingAnimationNavbar } from './components';
type ConnectDBScreenProps = {
closeWizard: () => void;
};
export function ConnectDBScreen(props: ConnectDBScreenProps) {
const { closeWizard } = props;
const dispatch = useAppDispatch();
const onClick = () => {
// we should change the route to `/data` first, and then close the wizard.
// this is because routing is slow on prod, but wizard closes instantaneously.
// if we close wizard first, it will show a flicker of `<Api />` tab before routing to `/data`.
dispatch(_push(`/data/manage/connect`));
closeWizard();
};
return (
<div className="max-w-5xl p-md ml-auto mr-auto mt-xl">
<h1 className="text-xl font-semibold text-cloud-darkest">
Welcome to your new Hasura project
</h1>
<p>Let&apos;s get started by connecting your first database</p>
<div className="mt-5">
<OnboardingAnimationNavbar />
<OnboardingAnimation />
</div>
<div className="flex items-center justify-between">
<div className="cursor-pointer text-secondary text-sm hover:text-secondary-dark">
<div data-trackid="onboarding-skip-button" onClick={closeWizard}>
Skip setup, continue to dashboard
</div>
</div>
<Button
data-trackid="onboarding-connect-db-button"
mode="primary"
onClick={onClick}
>
Connect Your Database
</Button>
</div>
</div>
);
}

View File

@ -0,0 +1,24 @@
import React from 'react';
type Props = {
className: string;
};
export function CustomRightChevron(props: Props) {
const { className } = props;
return (
<svg
className={className}
viewBox="0 0 22 80"
fill="none"
preserveAspectRatio="none"
>
<path
d="M0 -2L20 40L0 82"
vectorEffect="non-scaling-stroke"
stroke="currentcolor"
strokeLinejoin="round"
/>
</svg>
);
}

View File

@ -0,0 +1,169 @@
import React from 'react';
import {
FaDatabase,
FaPlug,
FaCogs,
FaLink,
FaBolt,
FaCaretRight,
FaMobileAlt,
FaServer,
FaShareAlt,
} from 'react-icons/fa';
import { HasuraLogoIcon } from '@/new-components/HasuraLogo';
const commonStyles = {
consumerList:
'relative bg-white rounded border border-amber-500 shadow shadow-amber-500/50 overflow-hidden p-sm w-48',
featuresList:
'opacity-0 group flex items-center border-t border-gray-200 bg-gray-100 text-gray-400 text-sm px-sm',
};
export function OnboardingAnimation() {
return (
<div className="flex justify-center overflow-auto bg-gray-200 border border-gray-300 rounded-b p-md mb-md">
<div className="flex items-center relative pr-6">
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_500ms_forwards] opacity-0 absolute top-0 left-0 text-sm font-semibold text-muted uppercase tracking-wider">
Sources
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_2500ms_forwards] opacity-0 absolute h-full right-0 border-r border-gray-400" />
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_2500ms_forwards] opacity-0 absolute w-4 border-t border-gray-400 top-0 right-0" />
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_2500ms_forwards] opacity-0 absolute w-4 border-b border-gray-400 bottom-0 right-0" />
<div className="space-y-md">
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_1500ms_forwards] opacity-0 flex items-center justify-end">
<div className="relative bg-transparent rounded border border-gray-400 text-gray-400 overflow-hidden p-sm w-48">
<div className="flex items-center">
<FaCogs className="fill-current w-4 h-4 mr-1.5" />
<div className="font-semibold">REST Endpoints</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_1000ms_forwards] opacity-0 flex items-center justify-end">
<div className="relative bg-white rounded shadow overflow-hidden border shadow p-sm w-48 hover:shadow-md">
<div className="flex items-center">
<FaDatabase className="fill-current w-4 h-4 mr-1.5" />
<div className="font-semibold">Databases</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_2000ms_forwards] opacity-0 flex items-center justify-end">
<div className="relative bg-transparent rounded border border-gray-400 text-gray-400 overflow-hidden p-sm w-48">
<div className="flex items-center">
<FaPlug className="fill-current w-4 h-4 mr-1.5" />
<div className="font-semibold">GraphQL Services</div>
</div>
</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_2500ms_forwards] opacity-0 flex items-center">
<div className="-mt-2.5 w-4 border-t border-gray-400" />
<div className="-mt-2.5 flex items-center px-xs text-sm text-muted">
<FaLink className="mr-1 w-3" />
Connected
</div>
<div className="-mt-2.5 w-4 border-t border-gray-400" />
<FaCaretRight className="-mt-2.5 w-2 mr-1 text-gray-400 h-full -ml-1.5" />
</div>
<div className="flex items-center">
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_3000ms_forwards] opacity-0 bg-white rounded shadow w-48 hover:shadow-md">
<div className="p-sm flex items-center">
<HasuraLogoIcon size="sm" />
<div className="font-semibold">Hasura</div>
</div>
<div className="relative">
<div
className={`animate-[onboardingWizardFadeIn_300ms_ease-out_3500ms_forwards] ${commonStyles.featuresList} py-1.5`}
>
<div>Authentication</div>
</div>
<div
className={`animate-[onboardingWizardFadeIn_300ms_ease-out_4000ms_forwards] ${commonStyles.featuresList} pt-1.5 pb-6`}
>
<div>Permissions</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_4500ms_forwards] opacity-0 group flex items-center bg-gray-100 text-gray-400 text-sm px-sm pb-1.5 pt-6 ">
<div>REST API</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_6500ms_forwards] -mt-[2rem] absolute top-1/2 -translate-y-1/2 font-semibold scale-105 w-48 opacity-0 group flex items-center text-sm px-sm rounded shadow border-gray-200 bg-white py-1.5">
<div className="absolute top-2.5 right-2.5 rounded-full w-3 h-3 bg-amber-500 animate-ping" />
<div className="absolute top-2.5 right-2.5 rounded-full w-3 h-3 bg-amber-500" />
<div>GraphQL API</div>
</div>
<div
className={`animate-[onboardingWizardFadeIn_300ms_ease-out_5000ms_forwards] ${commonStyles.featuresList} py-1.5`}
>
<div>Relationships</div>
</div>
<div
className={`animate-[onboardingWizardFadeIn_300ms_ease-out_5500ms_forwards] ${commonStyles.featuresList} py-1.5`}
>
<div>Caching</div>
</div>
<div
className={`animate-[onboardingWizardFadeIn_300ms_ease-out_6000ms_forwards] ${commonStyles.featuresList} py-1.5 rounded-b`}
>
<div>Observability</div>
</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_7000ms_forwards] opacity-0 flex items-center">
<div className="-mt-2.5 w-2 ml-1.5 border-t border-gray-400" />
<FaCaretRight className="-mt-2.5 text-gray-400 w-2 h-full" />
<div className="-mt-2.5 flex items-center px-xs text-sm text-muted">
<FaBolt className="text-muted mr-1 w-3" />
Powering
</div>
<div className="-mt-2.5 w-4 border-t border-gray-400" />
</div>
<div className="flex items-center relative pl-6">
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_7500ms_forwards] opacity-0 absolute top-0 right-0 text-sm font-semibold text-muted uppercase tracking-wider">
Consumers
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_7000ms_forwards] opacity-0 absolute h-full left-0 border-r border-gray-400" />
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_7000ms_forwards] opacity-0 absolute w-4 border-t border-gray-400 top-0 left-0" />
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_7000ms_forwards] opacity-0 absolute w-4 border-b border-gray-400 bottom-0 left-0" />
<div className="space-y-md">
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_8000ms_forwards] opacity-0 flex items-center justify-end">
<div className={commonStyles.consumerList}>
<div className="flex items-center">
<FaMobileAlt className="fill-current w-4 h-full mr-1.5" />
<div className="font-semibold">Apps</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_8500ms_forwards] opacity-0 flex items-center justify-end">
<div className={commonStyles.consumerList}>
<div className="flex items-center">
<FaServer className="fill-current w-4 h-full mr-1.5" />
<div className="font-semibold">Data Platforms</div>
</div>
</div>
</div>
<div className="animate-[onboardingWizardFadeIn_300ms_ease-out_9000ms_forwards] opacity-0 flex items-center justify-end">
<div className={commonStyles.consumerList}>
<div className="flex items-center">
<FaShareAlt className="fill-current w-4 h-full mr-1.5" />
<div className="font-semibold">Other Services</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1,66 @@
import React from 'react';
import { CustomRightChevron } from './CustomRightChevron';
const commmonListItemStyle =
'flex-shrink-0 w-10 h-10 flex items-center justify-center border-2 rounded-full';
export function OnboardingAnimationNavbar() {
return (
<nav>
<ol className="border-t border-l border-r border-gray-300 rounded-t divide-y mb-0 divide-gray-300 md:flex md:divide-y-0 bg-white">
<li className="relative flex-grow md:flex">
<div className="group flex items-center w-full">
<span className="px-md py-sm flex items-center">
<span
className={`${commmonListItemStyle} border-amber-500 bg-[#f9c548] font-semibold`}
>
<span className="text-sm text-gray-800">01</span>
</span>
<span className="ml-sm text-gray-900">Getting Started</span>
</span>
</div>
<div
className="hidden md:block absolute top-0 right-0 h-full w-5"
aria-hidden="true"
>
<CustomRightChevron className="h-full w-full text-gray-300" />
</div>
</li>
<li className="relative flex-grow md:flex">
<div className="px-6 py-4 flex items-center" aria-current="step">
<span
className={`${commmonListItemStyle} border-gray-300 group-hover:border-gray-400`}
>
<span className="text-sm font-semibold text-gray-500 group-hover:text-gray-900">
02
</span>
</span>
<span className="ml-sm text-gray-900">Connect Database</span>
</div>
<div
className="hidden md:block absolute top-0 right-0 h-full w-5"
aria-hidden="true"
>
<CustomRightChevron className="h-full w-full text-gray-300" />
</div>
</li>
<li className="relative flex-grow md:flex">
<div className="group flex items-center">
<span className="px-6 py-4 flex items-center">
<span
className={`${commmonListItemStyle} border-gray-300 group-hover:border-gray-400`}
>
<span className="text-sm font-semibold text-gray-500 group-hover:text-gray-900">
03
</span>
</span>
<span className="ml-sm text-gray-900">Make Your First Query</span>
</span>
</div>
</li>
</ol>
</nav>
);
}

View File

@ -0,0 +1,2 @@
export { OnboardingAnimationNavbar } from './OnboardingAnimationNavbar';
export { OnboardingAnimation } from './OnboardingAnimation';

View File

@ -0,0 +1,10 @@
import React from 'react';
import { ComponentMeta, Story } from '@storybook/react';
import { TopHeaderBar } from './TopHeaderBar';
export default {
title: 'features/Onboarding Wizard/Top Header Bar',
component: TopHeaderBar,
} as ComponentMeta<typeof TopHeaderBar>;
export const Base: Story = () => <TopHeaderBar />;

View File

@ -0,0 +1,12 @@
import React from 'react';
import { HasuraLogoFull } from '@/new-components/HasuraLogo';
export function TopHeaderBar() {
return (
<header className="flex items-center bg-gray-700 px-sm py-xs">
<div className="mr-auto ml-auto">
<HasuraLogoFull size="sm" mode="primary" />
</div>
</header>
);
}

View File

@ -0,0 +1,2 @@
export { TopHeaderBar } from './TopHeaderBar/TopHeaderBar';
export { ConnectDBScreen } from './ConnectDBScreen/ConnectDBScreen';

View File

@ -0,0 +1,3 @@
import { Root } from './Root';
export const OnboardingWizard = Root;

View File

@ -0,0 +1,127 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { TemplateStoriesFactory } from '@/utils/StoryUtils';
import { HasuraLogoFull } from '@/new-components/HasuraLogo';
<Meta
title="components/Hasura Logo/Hasura Logo Full ⚛️"
component={HasuraLogoFull}
parameters={{
docs: { source: { type: 'code' } },
chromatic: { disableSnapshot: true },
}}
decorators={[Story => <div className={'p-4'}>{Story()}</div>]}
argTypes={{
content: {
table: {
disable: true,
},
},
}}
/>
# Hasura Logo Full ⚛️
- [🧰 Overview](#-overview)
- [🔁 States](#-states)
- [🎭 Variants](#-variants)
- [⚙️ API](#-api)
- [🧪 Testing](#-testing)
- [🐙 Code on Github](https://github.com/hasura/graphql-engine-mono/tree/main/console/src/new-components/HasuraLogo/HasuraLogoIcon.tsx)
## 🧰 Overview
A component which provides customizable hasura logo svg.
### Basic usage
```ts
import { HasuraLogoFull } from '@/new-components/HasuraLogo';
```
```tsx
<HasuraLogoFull />
```
<Canvas>
<Story name="Overview">
<HasuraLogoFull />
</Story>
</Canvas>
export const Template = args => <HasuraLogoFull {...args} />;
export const stories = {
'Variant - Mode default': {
mode: 'default',
},
'Variant - Mode primary': {
mode: 'primary',
},
'Variant - Size sm': {
size: 'sm',
},
'Variant - Size md': {
size: 'md',
},
'Variant - Size lg': {
size: 'lg',
},
};
## 🎭 Variants
### 🎭 Mode
<Canvas>
<Story
name="Variant - Mode"
args={{
'Variant - Mode default': stories['Variant - Mode default'],
'Variant - Mode primary': stories['Variant - Mode primary'],
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
#### 🚦 Usage
- The default logo should be used in the vast majority of circumstances
- The primary logo style could be used for dark backgrounds
### 🎭 Size
<Canvas>
<Story
name="Variant - Size"
args={{
'Variant - Size sm': stories['Variant - Size sm'],
'Variant - Size md': stories['Variant - Size md'],
'Variant - Size lg': stories['Variant - Size lg'],
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
#### 🚦 Usage
- Small is typically used for icons within other svg, or titles.
- Large and medium could be used for navbar / toolbars
## 🧪 Testing
### 🧪 Snapshot 📸
<Canvas>
<Story
name="Testing - Snapshot"
args={stories}
parameters={{
chromatic: { disableSnapshot: false },
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>

View File

@ -0,0 +1,78 @@
import React from 'react';
type LogoModes = 'default' | 'primary';
type LogoSize = 'sm' | 'md' | 'lg';
type Props = {
/**
* The logo size
*/
size?: LogoSize;
/**
* The logo mode
*/
mode?: LogoModes;
};
const logoSizing: Record<LogoSize, string> = {
sm: 'h-8 mx-1.5',
md: 'h-14 mx-2',
lg: 'h-20 mx-2',
};
const logoModesStyles: Record<LogoModes, string> = {
default: 'black',
primary: 'white',
};
export function HasuraLogoFull(props: Props) {
const { size = 'md', mode = 'default' } = props;
return (
<svg
className={logoSizing[size]}
viewBox="0 0 285 84"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_5273_22707)">
<path
d="M81.771 28.7984C84.2262 21.3329 82.7493 6.43933 77.9917 0.940859C77.3682 0.219597 76.208 0.322635 75.6899 1.11883L69.8299 10.1018C68.3814 11.9003 65.7727 12.3125 63.8061 11.0573C57.4471 6.99198 49.8511 4.63147 41.6889 4.63147C33.5267 4.63147 25.9304 6.99198 19.5714 11.0573C17.6148 12.3125 15.006 11.8909 13.5481 10.1018L7.68787 1.11883C7.16996 0.322635 6.00938 0.228964 5.38597 0.940859C0.628701 6.44866 -0.848352 21.3422 1.60701 28.7984C2.42227 31.2807 2.64287 33.9035 2.16331 36.4513C1.68374 38.9804 1.20418 42.0341 1.20418 44.1603C1.20418 65.9951 19.3316 83.689 41.6793 83.689C64.0365 83.689 82.1542 65.9858 82.1542 44.1603C82.1542 42.0341 81.6749 38.9804 81.1955 36.4513C80.7254 33.9035 80.9553 31.2807 81.771 28.7984ZM41.6793 74.8658C24.3959 74.8658 10.3447 61.1334 10.3447 44.2541C10.3447 43.7014 10.3638 43.1581 10.3926 42.6148C11.016 31.1121 18.1711 21.2861 28.2899 16.6026C32.347 14.7104 36.8933 13.6613 41.6889 13.6613C46.4845 13.6613 51.0212 14.7104 55.088 16.6119C65.2066 21.2954 72.3616 31.1308 72.9851 42.6242C73.014 43.1675 73.0331 43.7201 73.0331 44.2634C73.0233 61.1334 58.9628 74.8658 41.6793 74.8658Z"
fill={logoModesStyles[mode]}
/>
<path
d="M55.2591 56.0378L47.2505 42.4741L40.3832 31.1681C40.2201 30.8965 39.9228 30.7372 39.6063 30.7372H33.0459C32.7197 30.7372 32.4224 30.9058 32.2594 31.1869C32.0963 31.4585 32.1059 31.7957 32.269 32.0673L38.839 42.8488L30.0246 55.9816C29.8424 56.2533 29.8328 56.5996 29.9863 56.8806C30.1397 57.1615 30.4466 57.3398 30.7823 57.3398H37.3907C37.6976 57.3398 37.9854 57.1898 38.1484 56.9367L42.9153 49.668L47.1929 56.9089C47.356 57.1806 47.6533 57.3491 47.9699 57.3491H54.4823C54.8082 57.3491 55.1057 57.1806 55.2591 56.9089C55.4224 56.6466 55.4224 56.3095 55.2591 56.0378Z"
fill={logoModesStyles[mode]}
/>
<path
d="M119.484 21.5306H127.838V66.2673H119.484V47.2056H110.046V66.2771H101.692V21.5306H110.046V40.9578H119.484V21.5306Z"
fill={logoModesStyles[mode]}
/>
<path
d="M153.61 66.276L151.864 56.9842H141.842L140.24 66.276H131.886L141.103 21.5391H152.353L162.012 66.276H153.61ZM142.935 50.811H150.704L146.714 29.398L142.935 50.811Z"
fill={logoModesStyles[mode]}
/>
<path
d="M180.619 58.1171V48.5442C180.619 47.7855 180.476 47.2797 180.188 47.0174C179.9 46.7551 179.363 46.624 178.586 46.624H172.707C167.719 46.624 165.226 44.2635 165.226 39.5331V28.555C165.226 23.8714 167.834 21.5391 173.061 21.5391H181.051C186.278 21.5391 188.887 23.8808 188.887 28.555V34.7934H180.466V29.6884C180.466 28.9296 180.322 28.4238 180.035 28.1615C179.747 27.8993 179.21 27.7682 178.433 27.7682H175.671C174.846 27.7682 174.289 27.8993 174.002 28.1615C173.714 28.4238 173.57 28.9296 173.57 29.6884V38.6901C173.57 39.4488 173.714 39.9546 174.002 40.2169C174.289 40.4792 174.846 40.6103 175.671 40.6103H181.406C186.489 40.6103 189.031 42.924 189.031 47.5607V59.2603C189.031 63.9436 186.394 66.276 181.118 66.276H173.273C167.998 66.276 165.36 63.9343 165.36 59.2603V53.0872H173.704V58.1171C173.704 58.8758 173.848 59.382 174.136 59.6443C174.424 59.9062 174.98 60.0376 175.805 60.0376H178.567C179.344 60.0376 179.871 59.9062 180.169 59.6443C180.466 59.382 180.619 58.8758 180.619 58.1171Z"
fill={logoModesStyles[mode]}
/>
<path
d="M211.32 21.5306H219.664V59.2516C219.664 63.9349 217.027 66.2673 211.752 66.2673H202.899C197.624 66.2673 194.986 63.9256 194.986 59.2516V21.5306H203.34V58.1182C203.34 58.8769 203.484 59.3826 203.772 59.6449C204.059 59.9073 204.597 60.0382 205.374 60.0382H209.219C210.044 60.0382 210.601 59.9073 210.888 59.6449C211.176 59.3826 211.32 58.8769 211.32 58.1182V21.5306Z"
fill={logoModesStyles[mode]}
/>
<path
d="M234.914 48.8355V66.2771H226.569V21.5306H243.412C248.687 21.5306 251.324 23.8723 251.324 28.5465V41.8102C251.324 45.6882 249.56 47.955 246.021 48.62L253.646 66.2771H244.64L237.667 48.8355H234.914ZM234.914 27.769V42.8031H240.937C241.714 42.8031 242.242 42.672 242.539 42.4097C242.827 42.1474 242.97 41.6416 242.97 40.8829V29.6893C242.97 28.9305 242.827 28.4247 242.539 28.1624C242.251 27.9001 241.714 27.769 240.937 27.769H234.914Z"
fill={logoModesStyles[mode]}
/>
<path
d="M276.589 66.276L274.843 56.9842H264.82L263.218 66.276H254.874L264.091 21.5391H275.342L285 66.276H276.589ZM265.923 50.811H273.692L269.702 29.398L265.923 50.811Z"
fill={logoModesStyles[mode]}
/>
</g>
<defs>
<clipPath id="clip0_5273_22707">
<rect width="285" height="84" fill="white" />
</clipPath>
</defs>
</svg>
);
}

View File

@ -0,0 +1,127 @@
import { Canvas, Meta, Story, ArgsTable } from '@storybook/addon-docs';
import { TemplateStoriesFactory } from '@/utils/StoryUtils';
import { HasuraLogoIcon } from '@/new-components/HasuraLogo';
<Meta
title="components/Hasura Logo/Hasura Logo Icon ⚛️"
component={HasuraLogoIcon}
parameters={{
docs: { source: { type: 'code' } },
chromatic: { disableSnapshot: true },
}}
decorators={[Story => <div className={'p-4'}>{Story()}</div>]}
argTypes={{
content: {
table: {
disable: true,
},
},
}}
/>
# Hasura Logo Icon ⚛️
- [🧰 Overview](#-overview)
- [🔁 States](#-states)
- [🎭 Variants](#-variants)
- [⚙️ API](#-api)
- [🧪 Testing](#-testing)
- [🐙 Code on Github](https://github.com/hasura/graphql-engine-mono/tree/main/console/src/new-components/HasuraLogo/HasuraLogoIcon.tsx)
## 🧰 Overview
A component which provides customizable hasura logo svg.
### Basic usage
```ts
import { HasuraLogoIcon } from '@/new-components/HasuraLogo';
```
```tsx
<HasuraLogoIcon />
```
<Canvas>
<Story name="Overview">
<HasuraLogoIcon />
</Story>
</Canvas>
export const Template = args => <HasuraLogoIcon {...args} />;
export const stories = {
'Variant - Mode default': {
mode: 'default',
},
'Variant - Mode primary': {
mode: 'primary',
},
'Variant - Size sm': {
size: 'sm',
},
'Variant - Size md': {
size: 'md',
},
'Variant - Size lg': {
size: 'lg',
},
};
## 🎭 Variants
### 🎭 Mode
<Canvas>
<Story
name="Variant - Mode"
args={{
'Variant - Mode default': stories['Variant - Mode default'],
'Variant - Mode primary': stories['Variant - Mode primary'],
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
#### 🚦 Usage
- The default logo should be used in the vast majority of circumstances
- The primary logo style could be used for dark backgrounds
### 🎭 Size
<Canvas>
<Story
name="Variant - Size"
args={{
'Variant - Size sm': stories['Variant - Size sm'],
'Variant - Size md': stories['Variant - Size md'],
'Variant - Size lg': stories['Variant - Size lg'],
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>
#### 🚦 Usage
- Small is typically used for icons within other svg, or titles.
- Large and medium could be used for navbar / toolbars
## 🧪 Testing
### 🧪 Snapshot 📸
<Canvas>
<Story
name="Testing - Snapshot"
args={stories}
parameters={{
chromatic: { disableSnapshot: false },
}}
>
{TemplateStoriesFactory(Template).bind({})}
</Story>
</Canvas>

View File

@ -0,0 +1,55 @@
import React from 'react';
type LogoModes = 'default' | 'primary';
type LogoSize = 'sm' | 'md' | 'lg';
type Props = {
/**
* The logo size
*/
size?: LogoSize;
/**
* The logo mode
*/
mode?: LogoModes;
};
const logoSizing: Record<LogoSize, string> = {
sm: 'w-4 h-4 mr-1.5',
md: 'w-10 h-10 mr-2',
lg: 'w-14 h-14 mr-2',
};
const logoModesStyles: Record<LogoModes, string> = {
default: 'black',
primary: 'white',
};
export function HasuraLogoIcon(props: Props) {
const { size = 'md', mode = 'default' } = props;
return (
<svg
className={logoSizing[size]}
viewBox="0 0 81 84"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_5273_22557)">
<path
d="M79.7186 28.6019C82.1218 21.0731 80.6778 6.03602 76.0158 0.487869C75.4073 -0.238057 74.2624 -0.134353 73.757 0.664165L68.0121 9.72787C66.5887 11.5427 64.0308 11.9575 62.1124 10.6923C55.8827 6.59602 48.4359 4.21083 40.4322 4.21083C32.4285 4.21083 24.9817 6.59602 18.752 10.6923C16.8336 11.9575 14.2757 11.5323 12.8523 9.72787L7.10738 0.664165C6.60199 -0.134353 5.45712 -0.238057 4.84859 0.487869C0.186621 6.03602 -1.25735 21.0731 1.14583 28.6019C1.94002 31.1012 2.16693 33.7456 1.69248 36.3279C1.22834 38.879 0.753897 41.9693 0.753897 44.1056C0.753897 66.1323 18.5251 84.0005 40.4322 84.0005C62.3497 84.0005 80.1105 66.1427 80.1105 44.1056C80.1105 41.959 79.6464 38.879 79.1719 36.3279C78.6975 33.7456 78.9244 31.1012 79.7186 28.6019ZM40.4322 75.0819C23.4965 75.0819 9.71684 61.2271 9.71684 44.199C9.71684 43.639 9.73747 43.0893 9.7581 42.5397C10.3769 30.9353 17.3802 21.0108 27.3024 16.2819C31.2836 14.3738 35.7393 13.316 40.4322 13.316C45.1251 13.316 49.5808 14.3842 53.5724 16.2923C63.4945 21.0212 70.4978 30.9456 71.1166 42.5397C71.1476 43.0893 71.1579 43.639 71.1579 44.199C71.1476 61.2271 57.3679 75.0819 40.4322 75.0819Z"
fill={logoModesStyles[mode]}
/>
<path
d="M53.7371 56.083L45.8881 42.4045L39.153 30.997C38.9983 30.7274 38.7095 30.5615 38.3898 30.5615H31.9538C31.634 30.5615 31.3452 30.7378 31.1905 31.0074C31.0358 31.2874 31.0358 31.6296 31.2008 31.8993L37.6368 42.7882L28.9936 56.0415C28.8183 56.3111 28.7977 56.6637 28.9524 56.9541C29.1071 57.2445 29.4062 57.4208 29.7259 57.4208H36.2032C36.5023 57.4208 36.7808 57.2652 36.9458 57.0163L41.6181 49.6741L45.8056 56.9748C45.9603 57.2548 46.2594 57.4208 46.5688 57.4208H52.9533C53.273 57.4208 53.5618 57.2548 53.7165 56.9748C53.9022 56.6948 53.9022 56.363 53.7371 56.083Z"
fill={logoModesStyles[mode]}
/>
</g>
<defs>
<clipPath id="clip0_5273_22557">
<rect width="81" height="84" fill="white" />
</clipPath>
</defs>
</svg>
);
}

View File

@ -0,0 +1,2 @@
export { HasuraLogoIcon } from './HasuraLogoIcon';
export { HasuraLogoFull } from './HasuraLogoFull';

View File

@ -64,6 +64,22 @@ module.exports = {
width: {
inherit: 'inherit',
},
keyframes: {
onboardingWizardFadeIn: {
'0%': {
opacity: '0',
scale: '.9',
},
'50%': {
opacity: '.5',
scale: '.95',
},
'100%': {
opacity: '1',
scale: '1',
},
},
},
},
},
plugins: [require('@tailwindcss/typography'), require('@tailwindcss/forms')],