Merge branch 'main' into rd.create-page-wrappers

This commit is contained in:
RadoslavDimchev 2023-08-15 09:29:23 +03:00
commit 367845f7af
68 changed files with 1290 additions and 1032 deletions

1
.gitignore vendored
View File

@ -32,3 +32,4 @@ dist-ssr
!.yarn/sdks
!.yarn/versions
/.tamagui/
/storybook-static/

View File

@ -1,19 +1,20 @@
import type { StorybookConfig } from "@storybook/react-vite";
import type { StorybookConfig } from '@storybook/react-vite'
const config: StorybookConfig = {
stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
framework: '@storybook/react-vite',
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
'storybook-addon-designs',
'storybook-dark-mode',
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
autodocs: 'tag',
},
};
export default config;
}
export default config

View File

@ -0,0 +1,3 @@
<script>
window.global = window
</script>

View File

@ -1,15 +0,0 @@
import type { Preview } from "@storybook/react";
const preview: Preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;

25
.storybook/preview.tsx Normal file
View File

@ -0,0 +1,25 @@
import React from 'react'
import type { Preview } from '@storybook/react'
import { TamaguiProvider } from '@tamagui/web'
import { Provider as StatusProvider } from '@status-im/components'
import '../src/index.css'
import appConfig from '../tamagui.config'
const preview: Preview = {
parameters: {
// layout: 'centered',
},
decorators: [
Story => {
return (
<TamaguiProvider config={appConfig}>
<StatusProvider>
<Story />
</StatusProvider>
</TamaguiProvider>
)
},
],
}
export default preview

View File

@ -8,7 +8,23 @@
}
}
span {
/* span {
display: inline-block;
vertical-align: middle;
line-height: 1;
} */
.mb-1 {
margin-bottom: 1em;
}
.mt-1 {
margin-top: 1em;
}
.my-1 {
margin-top: 1em;
margin-bottom: 1em;
}
.py-05 {
padding-top: 0.5em;
padding-bottom: 0.5em;
}

View File

@ -1,16 +1,20 @@
import { TamaguiProvider } from 'tamagui'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import { Provider as StatusProvider } from '@status-im/components'
import './App.css'
import config from '../tamagui.config'
import LandingPage from './components/LayoutComponent/LandingPage'
import LandingPage from './components/LandingPage'
import DeviceHealthCheck from './pages/DeviceHealthCheck/DeviceHealthCheck'
const router = createBrowserRouter([
{
path: '/',
element: <LandingPage />,
},
{
path: '/device-health-check',
element: <DeviceHealthCheck />,
},
])
function App() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

3
src/assets/chevron.svg Normal file
View File

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M7.75006 14.5L12.2503 9.99994L7.75 5.50002" stroke="#09101C" stroke-width="1.2"/>
</svg>

After

Width:  |  Height:  |  Size: 201 B

View File

@ -0,0 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react'
import BreadcrumbBar from './BreadcrumbBar'
const meta = {
title: 'General/BreadcrumbBar',
component: BreadcrumbBar,
parameters: {
layout: 'top',
},
tags: ['autodocs'],
} satisfies Meta<typeof BreadcrumbBar>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
breadcrumbList: ['Nodes', 'Nimbus', 'Connect Device'],
},
}

View File

@ -0,0 +1,19 @@
import './breadcrumbbar.css'
type BreadcrumbBarProps = {
breadcrumbList: string[]
}
function BreadcrumbBar({ breadcrumbList }: BreadcrumbBarProps) {
return (
<nav className="breadcrumb-bar-nav">
<ul className="breadcrumb-bar-ul">
{breadcrumbList.map(item => (
<li className="breadcrumb-bar-li">{item}</li>
))}
</ul>
</nav>
)
}
export default BreadcrumbBar

View File

@ -0,0 +1,18 @@
function ConnectIcon() {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="20/connection">
<path
id="body"
fillRule="evenodd"
clipRule="evenodd"
d="M9.99978 1.8999C8.21669 1.8999 6.48343 2.48826 5.06881 3.57374C3.6542 4.65921 2.63728 6.18114 2.17578 7.90347L3.33489 8.21405C3.72802 6.74688 4.59428 5.45043 5.79933 4.52576C7.00437 3.6011 8.48086 3.0999 9.99978 3.0999C11.5187 3.0999 12.9952 3.6011 14.2002 4.52577C15.4053 5.45043 16.2715 6.74688 16.6647 8.21405L17.8238 7.90347C17.3623 6.18114 16.3454 4.65922 14.9307 3.57374C13.5161 2.48827 11.7829 1.8999 9.99978 1.8999ZM9.99988 4.9C8.87719 4.9 7.78588 5.27045 6.8952 5.9539C6.00451 6.63734 5.36423 7.59559 5.07366 8.68002L6.23277 8.9906C6.45497 8.16133 6.9446 7.42856 7.62571 6.90592C8.30682 6.38329 9.14136 6.1 9.99988 6.1C10.8584 6.1 11.6929 6.38329 12.374 6.90592C13.0552 7.42856 13.5448 8.16134 13.767 8.99061L14.9261 8.68002C14.6355 7.59559 13.9952 6.63735 13.1046 5.9539C12.2139 5.27045 11.1226 4.9 9.99988 4.9ZM9.99988 10.1C8.95054 10.1 8.09988 10.9507 8.09988 12C8.09988 13.0493 8.95054 13.9 9.99988 13.9C11.0492 13.9 11.8999 13.0493 11.8999 12C11.8999 10.9507 11.0492 10.1 9.99988 10.1ZM6.89988 12C6.89988 10.2879 8.2878 8.9 9.99988 8.9C11.712 8.9 13.0999 10.2879 13.0999 12C13.0999 13.5068 12.0248 14.7625 10.5999 15.042V17.5H9.39988V15.042C7.97494 14.7625 6.89988 13.5068 6.89988 12Z"
fill="#1B273D"
fillOpacity="0.7"
/>
</g>
</svg>
)
}
export default ConnectIcon

View File

@ -0,0 +1,117 @@
import { useState } from 'react'
import BreadcrumbBar from './BreadcrumbBar'
import { Button as StatusButton, Tag, Text, Avatar, Checkbox } from '@status-im/components'
import { Label, Separator, XStack, YStack } from 'tamagui'
import LayoutComponent from './LayoutComponent'
import NimbusLogo from './NimbusLogo'
import Titles from './Titles'
import NodeIcon from './NodeIcon'
import ConnectIcon from './ConnectIcon'
import PairIcon from './PairIcon'
import CreateIcon from './CreateIcon'
import LabelInputField from './LabelInputField'
function ContentPage() {
return (
<LayoutComponent
breadcrumbBar={<BreadcrumbBar breadcrumbList={['Nodes', 'Nimbus', 'Connect Device']} />}
content={<Content />}
/>
)
}
function Content() {
const [autoConnectChecked, setAutoConnectChecked] = useState(false)
const [portChecked, setPortChecked] = useState(false)
return (
<div className="container-inner connection-page">
<header>
<NimbusLogo />
<XStack space={'$2'} alignItems="center">
<Tag icon={ConnectIcon} label="Connect" size={32} selected />
<Tag icon={PairIcon} label="Pair" size={32} />
<Tag icon={CreateIcon} label="Create" size={32} />
</XStack>
</header>
<article className="content">
<section className="mb-1">
<Titles
title="Connect Device"
subtitle="Configure your device to connect to the Nimbus Node Manager"
/>
</section>
<section className="mb-1">
<XStack
width={'100%'}
alignItems="center"
justifyContent="space-between"
// media query
$lg={{
flexDirection: 'column',
flexWrap: 'nowrap',
}}
>
<XStack width={'40%'}>
<LabelInputField labelText="Beacon Address" placeholderText="something" />
</XStack>
<XStack width={'25%'}>
<LabelInputField labelText="Beacon Node Port" placeholderText="5052" />
</XStack>
<XStack width={'25%'}>
<LabelInputField labelText="Client Validator Port" placeholderText="5052" />
</XStack>
<YStack width={20}>
<Checkbox
id="port-checkbox"
variant="outline"
selected={portChecked}
onCheckedChange={v => setPortChecked(v)}
/>
</YStack>
</XStack>
<XStack width={'100%'} alignItems="center">
<LabelInputField labelText="API Token" placeholderText="****_*****_*****" />
</XStack>
</section>
<section className="mb-1">
<YStack>
<Text size={13} weight="regular" color={'#647084'}>
Device Avatar
</Text>
<XStack my={10}>
<Avatar type="user" size={80} name="Device Avatar" />
</XStack>
<XStack space>
<LabelInputField labelText="Device Name" placeholderText="Stake and chips" />
<LabelInputField labelText="Device Color" placeholderText="#011892" />
</XStack>
</YStack>
</section>
<Separator alignSelf="stretch" borderColor={'#F0F2F5'} />
<section className="my-1">
<YStack>
<Text size={19} weight="semibold">
Settings
</Text>
</YStack>
<XStack my={8} space={'$2'}>
<Checkbox
id="auto-connect"
selected={autoConnectChecked}
onCheckedChange={v => setAutoConnectChecked(v)}
variant="outline"
/>
<Label htmlFor="auto-connect">
<Text size={15} weight="regular">
Auto Connect Device
</Text>
</Label>
</XStack>
<Separator alignSelf="stretch" borderColor={'#F0F2F5'} />
</section>
<StatusButton icon={<NodeIcon />}>Connect Device</StatusButton>
</article>
</div>
)
}
export default ContentPage

View File

@ -0,0 +1,16 @@
function CreateIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<circle cx="10" cy="10" r="7.5" stroke="#1B273D" strokeOpacity="0.7" strokeWidth="1.2" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.6 9.4V6.5H9.4V9.4L6.5 9.4V10.6L9.4 10.6V13.5H10.6V10.6L13.5 10.6L13.5 9.4L10.6 9.4Z"
fill="#1B273D"
fillOpacity="0.7"
/>
</svg>
)
}
export default CreateIcon

View File

@ -0,0 +1,33 @@
import type { Meta, StoryObj } from '@storybook/react'
import DeviceCPULoad from './DeviceCPULoad'
const meta = {
title: 'Device Health/DeviceCPULoad',
component: DeviceCPULoad,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof DeviceCPULoad>
export default meta
type Story = StoryObj<typeof meta>
export const GoodStats: Story = {
args: {
load: [12, 123, 4,32,40],
},
}
export const BadStats: Story = {
args: {
load: [12, 34, 81, 90, 91],
},
}
export const EmptyStats: Story = {
args: {
load: [],
},
}

View File

@ -1,7 +1,7 @@
import StandartLineChart from './StandardLineChart'
import ShadowBox from './ShadowBox'
import IconText from './IconText'
import { Paragraph, Separator, XStack, YStack } from 'tamagui'
import { Shadow, Text } from '@status-im/components'
type DataPoint = {
x: number
@ -34,7 +34,16 @@ const DeviceCPULoad: React.FC<DeviceCPULoadProps> = ({ load }) => {
const message = currentLoad < 80 ? 'Good' : 'Poor'
return (
<ShadowBox boxStyle={{ width: '284px', height: '136px' }}>
<Shadow
variant="$2"
style={{
width: '284px',
height: '136px',
borderRadius: '16px',
border: message === 'Poor' ? '1px solid #D92344' : 'none',
backgroundColor: message === 'Poor' ? '#fefafa' : '#fff',
}}
>
<YStack>
<XStack
justifyContent="space-between"
@ -57,13 +66,20 @@ const DeviceCPULoad: React.FC<DeviceCPULoadProps> = ({ load }) => {
</XStack>
<Separator borderColor={'#e3e3e3'} />
<XStack space={'$4'} style={{ padding: '10px 16px 10px 16px' }}>
<IconText icon={message === 'Good' ? '/icons/check-circle.png' : '/icons/alert.png'}>
<IconText
icon={message === 'Good' ? '/icons/check-circle.png' : '/icons/alert.png'}
weight={'semibold'}
>
{message}
</IconText>
{/* <Text color={'#E95460'}>This is additional text</Text> */}
{message === 'Poor' && (
<Text size={13} color="#E95460">
{((currentLoad / 80) * 100).toFixed(0)}% Utilization
</Text>
)}
</XStack>
</YStack>
</ShadowBox>
</Shadow>
)
}

View File

@ -0,0 +1,35 @@
import type { Meta, StoryObj } from '@storybook/react'
import DeviceMemoryHealth from './DeviceMemoryHealth'
const meta = {
title: 'Device Health/DeviceMemoryHealth',
component: DeviceMemoryHealth,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof DeviceMemoryHealth>
export default meta
type Story = StoryObj<typeof meta>
export const GoodStats: Story = {
args: {
currentMemory: [25, 31, 5, 14, 20],
maxMemory: 64,
},
}
export const BadStats: Story = {
args: {
currentMemory: [25, 31, 5, 14, 80],
maxMemory: 64,
},
}
export const EmptyStats: Story = {
args: {
currentMemory: [],
maxMemory:0,
},
}

View File

@ -0,0 +1,87 @@
import StandartLineChart from './StandardLineChart'
import IconText from './IconText'
import { Paragraph, Separator, XStack, YStack } from 'tamagui'
import { Shadow as ShadowBox, Text } from '@status-im/components'
type DataPoint = {
x: number
y: number
}
type ChartData = {
id: string
color: string
data: DataPoint[]
maxValue?: number
}
type DeviceMemoryHealthProps = {
currentMemory: number[]
maxMemory: number
}
const DeviceMemoryHealth = ({ currentMemory, maxMemory }: DeviceMemoryHealthProps) => {
const chartData: ChartData[] = [
{
id: 'cpu',
color: '#8DC6BC',
data: currentMemory.map((yValue, index: number) => ({
x: index + 1,
y: yValue,
})),
maxValue: maxMemory,
},
]
const currentLoad =
chartData[0].data.length > 0 ? chartData[0].data[chartData[0].data.length - 1].y : 0
const message = currentLoad < maxMemory ? 'Good' : 'Poor'
return (
<ShadowBox
variant="$2"
style={{
width: '284px',
height: '136px',
borderRadius: '16px',
border: message === 'Poor' ? '1px solid #D92344' : 'none',
backgroundColor: message === 'Poor' ? '#fefafa' : '#fff',
}}
>
<YStack>
<XStack
justifyContent="space-between"
style={{
padding: '8px 16px',
position: 'relative',
}}
>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<StandartLineChart data={chartData} />
</div>
<YStack space={'$3'}>
<Paragraph color={'#09101C'} size={'$6'} fontWeight={'600'}>
Memory
</Paragraph>
<Paragraph color={'#09101C'} size={'$8'} fontWeight={'700'}>
{currentLoad} GB
</Paragraph>
</YStack>
</XStack>
<Separator borderColor={'#e3e3e3'} />
<XStack space={'$4'} style={{ padding: '10px 16px 10px 16px' }}>
<IconText icon={message === 'Good' ? '/icons/check-circle.png' : '/icons/alert.png'}>
{message}
</IconText>
{message === 'Poor' && (
<Text size={13} color="#E95460">
{((currentLoad / maxMemory || 0) * 100).toFixed(0)}% Utilization
</Text>
)}
</XStack>
</YStack>
</ShadowBox>
)
}
export default DeviceMemoryHealth

View File

@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react'
import DeviceNetworkHealth from './DeviceNetworkHealth'
const meta = {
title: 'Device Health/DeviceNetworkHealth',
component: DeviceNetworkHealth,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof DeviceNetworkHealth>
export default meta
type Story = StoryObj<typeof meta>
export const GoodStats: Story = {
args: {
uploadRate: [1, 4, 23, 61, 34],
downloadRate: [20, 3, 40, 56, 32],
},
}
export const BadStats: Story = {
args: {
uploadRate: [1, 4, 23, 55],
downloadRate: [20, 3, 40, 56, 80],
},
}
export const NoStats: Story = {
args: {
uploadRate: [],
downloadRate: [],
},
}

View File

@ -0,0 +1,92 @@
import StandartLineChart from './StandardLineChart'
import IconText from './IconText'
import { Paragraph, Separator, XStack, YStack } from 'tamagui'
import { Shadow as ShadowBox, Text } from '@status-im/components'
type DataPoint = {
x: number
y: number
}
type ChartData = {
id: string
color: string
data: DataPoint[]
}
type DeviceNetworkHealthProps = {
uploadRate: number[]
downloadRate: number[]
}
const DeviceNetworkHealth = ({ uploadRate, downloadRate }: DeviceNetworkHealthProps) => {
const chartData: ChartData[] = [
{
id: 'uploadRate',
color: '#8DC6BC',
data: uploadRate.map((yValue, index: number) => ({
x: index + 1,
y: yValue,
})),
},
{
id: 'downloadRate',
color: '#D92344',
data: downloadRate.map((yValue, index: number) => ({
x: index + 1,
y: yValue,
})),
},
]
const currentLoad =
chartData[0].data.length > 0 ? chartData[0].data[chartData[0].data.length - 1].y : 0
const message = currentLoad > 60 ? 'Good' : 'Poor'
return (
<ShadowBox
variant="$2"
style={{
width: '284px',
height: '136px',
borderRadius: '16px',
border: message === 'Poor' ? '1px solid #D92344' : 'none',
backgroundColor: message === 'Poor' ? '#fefafa' : '#fff',
}}
>
<YStack>
<XStack
justifyContent="space-between"
style={{
padding: '8px 16px',
position: 'relative', // Make XStack a positioning context
}}
>
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<StandartLineChart data={chartData} />
</div>
<YStack space={'$3'}>
<Paragraph color={'#09101C'} size={'$6'} fontWeight={'600'}>
Network
</Paragraph>
<Paragraph color={'#09101C'} size={'$8'} fontWeight={'700'}>
{currentLoad} GB
</Paragraph>
</YStack>
</XStack>
<Separator borderColor={'#e3e3e3'} />
<XStack space={'$4'} style={{ padding: '10px 16px 10px 16px' }}>
<IconText icon={message === 'Good' ? '/icons/check-circle.png' : '/icons/alert.png'}>
{message}
</IconText>
{message === 'Poor' && (
<Text size={13} color="#E95460">
{((currentLoad / 60) * 100).toFixed(0)}% Utilization
</Text>
)}
</XStack>
</YStack>
</ShadowBox>
)
}
export default DeviceNetworkHealth

View File

@ -0,0 +1,36 @@
import type { Meta, StoryObj } from '@storybook/react'
import DeviceStorageHealth from './DeviceStorageHealth'
const meta = {
title: 'Device Health/DeviceStorageHealth',
component: DeviceStorageHealth,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof DeviceStorageHealth>
export default meta
type Story = StoryObj<typeof meta>
export const GoodStats: Story = {
args: {
storage: 10,
maxStorage: 20,
},
}
export const BadStats: Story = {
args: {
storage: 20,
maxStorage: 20,
},
}
export const NoStats: Story = {
args: {
storage: 0,
maxStorage: 0,
},
}

View File

@ -0,0 +1,88 @@
import IconText from './IconText'
import { Paragraph, Separator, XStack, YStack } from 'tamagui'
import StandardGauge from './StandardGauge'
import { Shadow, Text } from '@status-im/components'
interface DeviceStorageHealthProps {
storage: number
maxStorage: number
}
const DeviceStorageHealth: React.FC<DeviceStorageHealthProps> = ({ storage, maxStorage }) => {
const message = storage < maxStorage ? 'Good' : 'Poor'
const free = maxStorage - storage
const utilization = (storage / (maxStorage || 1)) * 100
const data = (free: number) => {
return [
{
id: 'storage',
label: 'Used',
value: storage,
color: '#E95460',
},
{
id: 'storage',
label: 'Free',
value: free,
color: '#E7EAEE',
},
]
}
return (
<Shadow
variant="$2"
style={{
width: '284px',
height: '136px',
borderRadius: '16px',
border: message === 'Poor' ? '1px solid #D92344' : 'none',
backgroundColor: message === 'Poor' ? '#fefafa' : '#fff',
}}
>
<YStack>
<XStack
justifyContent="space-between"
style={{
padding: '8px 16px',
position: 'relative',
}}
>
<div
style={{
position: 'absolute',
right: '44px',
width: '75px',
height: '75px',
}}
>
<StandardGauge data={data(free)} />
</div>
<YStack space={'$3'}>
<Paragraph color={'#09101C'} size={'$6'} fontWeight={'600'}>
Storage
</Paragraph>
<Paragraph color={'#09101C'} size={'$8'} fontWeight={'700'}>
{storage} GB
</Paragraph>
</YStack>
</XStack>
<Separator borderColor={'#e3e3e3'} />
<XStack space={'$4'} style={{ padding: '10px 16px 10px 16px' }}>
<IconText
icon={message === 'Good' ? '/icons/check-circle.png' : '/icons/alert.png'}
weight={'semibold'}
>
{message}
</IconText>
{message === 'Poor' && (
<Text size={13} color="#E95460">
{utilization.toFixed(0)}% Utilization
</Text>
)}
</XStack>
</YStack>
</Shadow>
)
}
export default DeviceStorageHealth

View File

@ -1,35 +1,27 @@
import { Text } from 'tamagui'
import { Text } from '@status-im/components'
export type TextElement = {
text: string
bold?: boolean
italic?: boolean
weight?: 'regular' | 'medium' | 'semibold'
}
type FormattedTextProps = {
textElements: TextElement[]
color?: string
fontSize?: string
size: 27 | 19 | 15 | 13 | 11
}
const FormattedText = ({ textElements, color, fontSize }: FormattedTextProps) => {
const calculateStyle = (textElement: TextElement) => {
const isBold = textElement.bold ?? false
const isItalic = textElement.italic ?? false
return { fontWeight: isBold ? 'bold' : '', fontStyle: isItalic ? 'italic' : '' }
}
const FormattedText = ({ textElements, color, size }: FormattedTextProps) => {
return (
<Text color={color} fontSize={fontSize}>
{textElements.map((textElement, index) => {
return (
<span style={calculateStyle(textElement)} key={index}>
{textElement.text}
</span>
)
})}
</Text>
<>
{textElements.map((textElement, index) => (
<Text key={index} size={size} color={color} weight={textElement.weight}>
{textElement.text}
</Text>
))}
</>
)
}

View File

@ -0,0 +1,70 @@
import type { Meta, StoryObj } from '@storybook/react'
import HealthInfoSection from './HealthInfoSection'
const meta = {
title: 'Device Health/HealthInfoSection',
component: HealthInfoSection,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof HealthInfoSection>
export default meta
type Story = StoryObj<typeof meta>
export const AllGoodStats: Story = {
args: {
usedStorage: 79 * 1024 * 1024 * 1024,
maxStorage: 100 * 1024 * 1024 * 1024,
usedRamMemory: 32 * 1024 * 1024 * 1024,
maxRamMemory: 64 * 1024 * 1024 * 1024,
cpuClockRate: 2.4,
networkLatency: 50,
},
}
export const StorageBad: Story = {
args: {
usedStorage: 80 * 1024 * 1024 * 1024,
maxStorage: 100 * 1024 * 1024 * 1024,
usedRamMemory: 32 * 1024 * 1024 * 1024,
maxRamMemory: 64 * 1024 * 1024 * 1024,
cpuClockRate: 2.5,
networkLatency: 50,
},
}
export const CpuBad: Story = {
args: {
usedStorage: 79 * 1024 * 1024 * 1024,
maxStorage: 100 * 1024 * 1024 * 1024,
usedRamMemory: 32 * 1024 * 1024 * 1024,
maxRamMemory: 64 * 1024 * 1024 * 1024,
cpuClockRate: 2.3,
networkLatency: 50,
},
}
export const RamBad: Story = {
args: {
usedStorage: 79 * 1024 * 1024 * 1024,
maxStorage: 100 * 1024 * 1024 * 1024,
usedRamMemory: 56 * 1024 * 1024 * 1024,
maxRamMemory: 64 * 1024 * 1024 * 1024,
cpuClockRate: 2.4,
networkLatency: 50,
},
}
export const LatencyBad: Story = {
args: {
usedStorage: 79 * 1024 * 1024 * 1024,
maxStorage: 100 * 1024 * 1024 * 1024,
usedRamMemory: 32 * 1024 * 1024 * 1024,
maxRamMemory: 64 * 1024 * 1024 * 1024,
cpuClockRate: 2.4,
networkLatency: 101,
},
}

View File

@ -26,11 +26,11 @@ const HealthInfoSection = (props: HealthInfoSectionProps) => {
const usedStoragePercentage = (usedStorage / maxStorage) * 100
const usedRamMemoryPercentage = (usedRamMemory / maxRamMemory) * 100
const cpuClockRatePercentage = cpuClockRate > 2.4 ? 100 : 0
const cpuClockRatePercentage = cpuClockRate < 2.4 ? 100 : 0
const networkLatencyPercentage = networkLatency > 100 ? 100 : 0
return (
<YStack space={'$2'}>
<YStack space={'$3'}>
<StatusIconText
percentage={usedStoragePercentage}
threshold={80}

View File

@ -1,14 +1,13 @@
import { Image } from 'tamagui'
import { Image } from '@status-im/components'
export type IconProps = {
source: string
src: string
width?: number
height?: number
className?: string
}
const Icon = ({ source, width = 16, height = 16, ...props }: IconProps) => {
return <Image {...props} source={{ uri: source }} width={width} height={height} />
const Icon = ({ src, width = 16, height = 16 }: IconProps) => {
return <Image src={src} source={{ uri: src }} width={width} height={height} />
}
export default Icon

View File

@ -1,21 +0,0 @@
import Icon from './Icon'
import ReactButton from './ReactButton'
type IconButtonProps = {
children: string
icon: string
style?: unknown
size?: string
fontSize?: string
onClick?: () => void
}
const IconButton = ({ icon, children, ...props }: IconButtonProps) => {
return (
<ReactButton {...props} icon={<Icon source={icon} />}>
{children}
</ReactButton>
)
}
export default IconButton

View File

@ -1,12 +1,14 @@
import { Paragraph, XStack } from 'tamagui'
import { XStack } from 'tamagui'
import Icon from './Icon'
import { Text } from '@status-im/components'
type IconTextProps = {
icon: string
children: string
weight?: 'regular' | 'medium' | 'semibold'
}
const IconText = ({ icon, children, ...props }: IconTextProps) => {
const IconText = ({ icon, children, weight }: IconTextProps) => {
return (
<XStack
style={{
@ -14,10 +16,10 @@ const IconText = ({ icon, children, ...props }: IconTextProps) => {
}}
space={'$2'}
>
<Icon source={icon} />
<Paragraph {...props} color={'#000000'} fontWeight={'bold'}>
<Icon src={icon} />
<Text size={13} color={'#000000'} weight={weight}>
{children}
</Paragraph>
</Text>
</XStack>
)
}

View File

@ -1,29 +0,0 @@
import { XStack } from 'tamagui'
import Icon from './Icon'
import FormattedText, { TextElement } from './FormattedText'
type InformationBoxProps = {
icon: string
textElements: TextElement[]
}
const InformationBox = ({ icon, textElements }: InformationBoxProps) => {
return (
<XStack
style={{
border: '2px solid #E7EAEE',
borderRadius: '12px',
padding: '11px 16px',
maxWidth: '590px',
alignItems: 'start',
}}
space={'$2'}
>
<Icon source={icon} width={12} height={12} />
<FormattedText textElements={textElements} color={'#647084'} fontSize={'$3'} />
</XStack>
)
}
export default InformationBox

View File

@ -0,0 +1,20 @@
import { Input as StatusInput, Text } from '@status-im/components'
import { Label } from 'tamagui'
type LabelInputProps = {
labelText: string
placeholderText: string
}
function LabelInputField({ labelText, placeholderText }: LabelInputProps) {
return (
<Label flexDirection="column" alignItems="flex-start" my={10} width={'100%'}>
<Text size={13} weight="regular" color={'#647084'}>
{labelText}
</Text>
<StatusInput placeholder={placeholderText} width={'100%'} />
</Label>
)
}
export default LabelInputField

View File

@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react'
import LandingPage from './LandingPage'
const meta = {
title: 'Pages/LandingPage',
component: LandingPage,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
argTypes: {},
} satisfies Meta<typeof LandingPage>
export default meta
type Story = StoryObj<typeof meta>
export const Page: Story = {
args: {},
}

View File

@ -1,13 +1,14 @@
import LayoutComponent from './LayoutComponent'
import './LandingPage.css'
import QuickStartBar from '../QuickStartBar/QuickStartBar'
import DeviceCPULoad from '../DeviceCPULoad'
import NodesLogo from '../NodesLogo'
import QuickStartBar from './QuickStartBar'
function LandingPage() {
return (
<>
<LayoutComponent content={<Content />} />
<LayoutComponent
content={<Content />}
rightImageSrc="src/assets/bg-img/landing-page-bg.png"
/>
<QuickStartBar />
</>
)
@ -17,7 +18,14 @@ function Content() {
return (
<div className="container-inner landing-page">
<header>
<NodesLogo />
<div>
<div>
<img src="src/assets/nodes-app-icon.png" alt="" />
</div>
<p className="logo-title">
nodes<span className="beta">BETA</span>
</p>
</div>
</header>
<article className="content">
<div className="avatar">
@ -30,8 +38,8 @@ function Content() {
fill="none"
>
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M23 44C34.598 44 44 34.598 44 23V21C44 9.40202 34.598 2.24162e-07 23 5.00679e-07L21 5.48363e-07C9.40202 8.2488e-07 0 9.40171 0 20.9997C0 21.6637 0 22.3312 0 22.9999C0 34.5979 9.40202 44 21 44H23Z"
fill="url(#pattern0)"
/>
@ -67,13 +75,13 @@ function Content() {
viewBox="0 0 20 20"
fill="none"
>
<g clip-path="url(#clip0_760_1602)">
<g clipPath="url(#clip0_760_1602)">
<path
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M4.9999 2.60002C3.67442 2.60002 2.5999 3.67454 2.5999 5.00002C2.5999 6.32551 3.67442 7.40002 4.9999 7.40002C6.32539 7.40002 7.3999 6.32551 7.3999 5.00002C7.3999 3.67454 6.32539 2.60002 4.9999 2.60002ZM1.3999 5.00002C1.3999 3.0118 3.01168 1.40002 4.9999 1.40002C6.98813 1.40002 8.5999 3.0118 8.5999 5.00002C8.5999 5.77749 8.35345 6.4974 7.9344 7.08587L9.48712 8.63859C10.4478 7.86383 11.6697 7.4 13 7.4C16.0928 7.4 18.6 9.90721 18.6 13C18.6 16.0928 16.0928 18.6 13 18.6C9.9072 18.6 7.4 16.0928 7.4 13C7.4 11.6697 7.86383 10.4478 8.63859 9.48712L7.08589 7.93442C6.49739 8.35353 5.77743 8.60002 4.9999 8.60002C3.01168 8.60002 1.3999 6.98825 1.3999 5.00002ZM8.6 13C8.6 10.5699 10.5699 8.6 13 8.6C15.4301 8.6 17.4 10.5699 17.4 13C17.4 15.4301 15.4301 17.4 13 17.4C10.5699 17.4 8.6 15.4301 8.6 13Z"
fill="white"
fill-opacity="0.7"
fillOpacity="0.7"
/>
<path
d="M7.9344 7.08587L7.52711 6.79585C7.3855 6.99472 7.40821 7.26679 7.58085 7.43943L7.9344 7.08587ZM9.48712 8.63859L9.13357 8.99215C9.31425 9.17283 9.6021 9.18821 9.801 9.0278L9.48712 8.63859ZM8.63859 9.48712L9.0278 9.801C9.18821 9.6021 9.17283 9.31425 8.99215 9.13357L8.63859 9.48712ZM7.08589 7.93442L7.43945 7.58087C7.26681 7.40823 6.99472 7.38552 6.79585 7.52715L7.08589 7.93442ZM3.0999 5.00002C3.0999 3.95068 3.95056 3.10002 4.9999 3.10002V2.10002C3.39828 2.10002 2.0999 3.3984 2.0999 5.00002H3.0999ZM4.9999 6.90002C3.95056 6.90002 3.0999 6.04937 3.0999 5.00002H2.0999C2.0999 6.60165 3.39828 7.90002 4.9999 7.90002V6.90002ZM6.8999 5.00002C6.8999 6.04937 6.04924 6.90002 4.9999 6.90002V7.90002C6.60153 7.90002 7.8999 6.60165 7.8999 5.00002H6.8999ZM4.9999 3.10002C6.04924 3.10002 6.8999 3.95068 6.8999 5.00002H7.8999C7.8999 3.3984 6.60153 2.10002 4.9999 2.10002V3.10002ZM4.9999 0.900024C2.73553 0.900024 0.899902 2.73566 0.899902 5.00002H1.8999C1.8999 3.28794 3.28782 1.90002 4.9999 1.90002V0.900024ZM9.0999 5.00002C9.0999 2.73566 7.26427 0.900024 4.9999 0.900024V1.90002C6.71198 1.90002 8.0999 3.28794 8.0999 5.00002H9.0999ZM8.34169 7.3759C8.81905 6.70553 9.0999 5.88483 9.0999 5.00002H8.0999C8.0999 5.67015 7.88784 6.28926 7.52711 6.79585L8.34169 7.3759ZM9.84068 8.28504L8.28795 6.73232L7.58085 7.43943L9.13357 8.99215L9.84068 8.28504ZM9.801 9.0278C10.676 8.32213 11.788 7.9 13 7.9V6.9C11.5514 6.9 10.2196 7.40554 9.17324 8.24939L9.801 9.0278ZM13 7.9C15.8167 7.9 18.1 10.1833 18.1 13H19.1C19.1 9.63106 16.3689 6.9 13 6.9V7.9ZM18.1 13C18.1 15.8167 15.8167 18.1 13 18.1V19.1C16.3689 19.1 19.1 16.3689 19.1 13H18.1ZM13 18.1C10.1833 18.1 7.9 15.8167 7.9 13H6.9C6.9 16.3689 9.63106 19.1 13 19.1V18.1ZM7.9 13C7.9 11.788 8.32213 10.676 9.0278 9.801L8.24939 9.17324C7.40554 10.2196 6.9 11.5514 6.9 13H7.9ZM6.73234 8.28798L8.28504 9.84068L8.99215 9.13357L7.43945 7.58087L6.73234 8.28798ZM4.9999 9.10002C5.88478 9.10002 6.70554 8.81913 7.37594 8.3417L6.79585 7.52715C6.28924 7.88793 5.67008 8.10002 4.9999 8.10002V9.10002ZM0.899902 5.00002C0.899902 7.26439 2.73553 9.10002 4.9999 9.10002V8.10002C3.28782 8.10002 1.8999 6.71211 1.8999 5.00002H0.899902ZM13 8.1C10.2938 8.1 8.1 10.2938 8.1 13H9.1C9.1 10.8461 10.8461 9.1 13 9.1V8.1ZM17.9 13C17.9 10.2938 15.7062 8.1 13 8.1V9.1C15.1539 9.1 16.9 10.8461 16.9 13H17.9ZM13 17.9C15.7062 17.9 17.9 15.7062 17.9 13H16.9C16.9 15.1539 15.1539 16.9 13 16.9V17.9ZM8.1 13C8.1 15.7062 10.2938 17.9 13 17.9V16.9C10.8461 16.9 9.1 15.1539 9.1 13H8.1Z"
@ -90,8 +98,8 @@ function Content() {
Discover Nodes
</button>
</article>
<DeviceCPULoad load={[13,32,24,1,49,90,13,32,24,1,49,90]}/>
</div>
)
}
export default LandingPage

View File

@ -4,6 +4,7 @@ import './layout.css'
type LeftProps = {
breadcrumbBar?: ReactNode
content: ReactNode
rightImageSrc?: string
}
function LayoutComponent(props: LeftProps) {
return (
@ -12,18 +13,16 @@ function LayoutComponent(props: LeftProps) {
{props.breadcrumbBar}
<div className="container">{props.content}</div>
</section>
<LayoutRight />
<LayoutRight rightImageSrc={props.rightImageSrc} />
</div>
)
}
function LayoutRight() {
function LayoutRight({ rightImageSrc }: { rightImageSrc?: string }) {
return (
<section className="layout-right">
<div className="image-container">
<img src="src/assets/bg-img/landing-page-bg.png" alt="" />
{/* <img src="src/assets/bg-img/day-night-bg.png" alt="" /> */}
{/* <img src="src/assets/bg-img/key-lock-bg.png" alt="" /> */}
<img src={rightImageSrc} alt="background" />
</div>
</section>
)

View File

@ -0,0 +1,14 @@
import type { Meta, StoryObj } from '@storybook/react'
import NimbusLogo from './NimbusLogo'
const meta = {
title: 'General/NimbusLogo',
component: NimbusLogo,
tags: ['autodocs'],
} satisfies Meta<typeof NimbusLogo>
export default meta
type Story = StoryObj<typeof meta>
export const ExampleNimbusLogo: Story = {}

View File

@ -1,6 +1,7 @@
import { Text, XStack } from 'tamagui'
import { XStack } from 'tamagui'
import Icon from './Icon'
import Tag from './Tag'
import { Text } from '@status-im/components'
const NimbusLogo = () => {
return (
@ -11,8 +12,10 @@ const NimbusLogo = () => {
}}
space={'$2'}
>
<Icon source={'/icons/marks.png'} width={55} height={60} />
<Text style={{ fontWeight: '650', fontSize: '24px' }}>Nimbus</Text>
<Icon src={'/icons/marks.png'} width={55} height={60} />
<Text size={27} weight={'medium'}>
Nimbus
</Text>
<Tag text="BETA" />
</XStack>
)

View File

@ -0,0 +1,34 @@
function NodeIcon() {
return (
<span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<g clipPath="url(#clip0_760_1602)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.9999 2.60002C3.67442 2.60002 2.5999 3.67454 2.5999 5.00002C2.5999 6.32551 3.67442 7.40002 4.9999 7.40002C6.32539 7.40002 7.3999 6.32551 7.3999 5.00002C7.3999 3.67454 6.32539 2.60002 4.9999 2.60002ZM1.3999 5.00002C1.3999 3.0118 3.01168 1.40002 4.9999 1.40002C6.98813 1.40002 8.5999 3.0118 8.5999 5.00002C8.5999 5.77749 8.35345 6.4974 7.9344 7.08587L9.48712 8.63859C10.4478 7.86383 11.6697 7.4 13 7.4C16.0928 7.4 18.6 9.90721 18.6 13C18.6 16.0928 16.0928 18.6 13 18.6C9.9072 18.6 7.4 16.0928 7.4 13C7.4 11.6697 7.86383 10.4478 8.63859 9.48712L7.08589 7.93442C6.49739 8.35353 5.77743 8.60002 4.9999 8.60002C3.01168 8.60002 1.3999 6.98825 1.3999 5.00002ZM8.6 13C8.6 10.5699 10.5699 8.6 13 8.6C15.4301 8.6 17.4 10.5699 17.4 13C17.4 15.4301 15.4301 17.4 13 17.4C10.5699 17.4 8.6 15.4301 8.6 13Z"
fill="white"
fillOpacity="0.7"
/>
<path
d="M7.9344 7.08587L7.52711 6.79585C7.3855 6.99472 7.40821 7.26679 7.58085 7.43943L7.9344 7.08587ZM9.48712 8.63859L9.13357 8.99215C9.31425 9.17283 9.6021 9.18821 9.801 9.0278L9.48712 8.63859ZM8.63859 9.48712L9.0278 9.801C9.18821 9.6021 9.17283 9.31425 8.99215 9.13357L8.63859 9.48712ZM7.08589 7.93442L7.43945 7.58087C7.26681 7.40823 6.99472 7.38552 6.79585 7.52715L7.08589 7.93442ZM3.0999 5.00002C3.0999 3.95068 3.95056 3.10002 4.9999 3.10002V2.10002C3.39828 2.10002 2.0999 3.3984 2.0999 5.00002H3.0999ZM4.9999 6.90002C3.95056 6.90002 3.0999 6.04937 3.0999 5.00002H2.0999C2.0999 6.60165 3.39828 7.90002 4.9999 7.90002V6.90002ZM6.8999 5.00002C6.8999 6.04937 6.04924 6.90002 4.9999 6.90002V7.90002C6.60153 7.90002 7.8999 6.60165 7.8999 5.00002H6.8999ZM4.9999 3.10002C6.04924 3.10002 6.8999 3.95068 6.8999 5.00002H7.8999C7.8999 3.3984 6.60153 2.10002 4.9999 2.10002V3.10002ZM4.9999 0.900024C2.73553 0.900024 0.899902 2.73566 0.899902 5.00002H1.8999C1.8999 3.28794 3.28782 1.90002 4.9999 1.90002V0.900024ZM9.0999 5.00002C9.0999 2.73566 7.26427 0.900024 4.9999 0.900024V1.90002C6.71198 1.90002 8.0999 3.28794 8.0999 5.00002H9.0999ZM8.34169 7.3759C8.81905 6.70553 9.0999 5.88483 9.0999 5.00002H8.0999C8.0999 5.67015 7.88784 6.28926 7.52711 6.79585L8.34169 7.3759ZM9.84068 8.28504L8.28795 6.73232L7.58085 7.43943L9.13357 8.99215L9.84068 8.28504ZM9.801 9.0278C10.676 8.32213 11.788 7.9 13 7.9V6.9C11.5514 6.9 10.2196 7.40554 9.17324 8.24939L9.801 9.0278ZM13 7.9C15.8167 7.9 18.1 10.1833 18.1 13H19.1C19.1 9.63106 16.3689 6.9 13 6.9V7.9ZM18.1 13C18.1 15.8167 15.8167 18.1 13 18.1V19.1C16.3689 19.1 19.1 16.3689 19.1 13H18.1ZM13 18.1C10.1833 18.1 7.9 15.8167 7.9 13H6.9C6.9 16.3689 9.63106 19.1 13 19.1V18.1ZM7.9 13C7.9 11.788 8.32213 10.676 9.0278 9.801L8.24939 9.17324C7.40554 10.2196 6.9 11.5514 6.9 13H7.9ZM6.73234 8.28798L8.28504 9.84068L8.99215 9.13357L7.43945 7.58087L6.73234 8.28798ZM4.9999 9.10002C5.88478 9.10002 6.70554 8.81913 7.37594 8.3417L6.79585 7.52715C6.28924 7.88793 5.67008 8.10002 4.9999 8.10002V9.10002ZM0.899902 5.00002C0.899902 7.26439 2.73553 9.10002 4.9999 9.10002V8.10002C3.28782 8.10002 1.8999 6.71211 1.8999 5.00002H0.899902ZM13 8.1C10.2938 8.1 8.1 10.2938 8.1 13H9.1C9.1 10.8461 10.8461 9.1 13 9.1V8.1ZM17.9 13C17.9 10.2938 15.7062 8.1 13 8.1V9.1C15.1539 9.1 16.9 10.8461 16.9 13H17.9ZM13 17.9C15.7062 17.9 17.9 15.7062 17.9 13H16.9C16.9 15.1539 15.1539 16.9 13 16.9V17.9ZM8.1 13C8.1 15.7062 10.2938 17.9 13 17.9V16.9C10.8461 16.9 9.1 15.1539 9.1 13H8.1Z"
fill="white"
/>
</g>
<defs>
<clipPath id="clip0_760_1602">
<rect width="20" height="20" fill="white" />
</clipPath>
</defs>
</svg>
</span>
)
}
export default NodeIcon

View File

@ -1,6 +1,7 @@
import { Text, XStack } from 'tamagui'
import { XStack } from 'tamagui'
import Icon from './Icon'
import Tag from './Tag'
import { Text } from '@status-im/components'
const NodesLogo = () => {
return (
@ -11,8 +12,10 @@ const NodesLogo = () => {
}}
space={'$2'}
>
<Icon source={'src/assets/nodes-app-icon.png'} width={32} height={32} />
<Text style={{ fontWeight: '700', fontSize: '28px' }}>nodes</Text>
<Icon src={'src/assets/nodes-app-icon.png'} width={32} height={32} />
<Text size={27} weight={'semibold'}>
nodes
</Text>
<Tag text="BETA" />
</XStack>
)

View File

@ -0,0 +1,61 @@
function PairIcon() {
return (
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.4517 10.8951L16.952 6.89508L17.2978 6.49996L16.952 6.10484L13.4517 2.10489L12.5486 2.89514L15.7032 6.49997L12.5486 10.1049L13.4517 10.8951ZM6.5487 9.10488L3.0484 13.1049L2.70264 13.5001L3.0484 13.8952L6.5487 17.8951L7.45176 17.1049L4.29722 13.5L7.45176 9.89512L6.5487 9.10488Z"
fill="#1B273D"
fillOpacity="0.7"
/>
<rect
x="4"
y="7.5"
width="2"
height="12"
transform="rotate(-90 4 7.5)"
fill="url(#paint0_linear_760_2959)"
/>
<g mask="url(#mask0_760_2959)">
<path d="M4.5 6.50006L16 6.50006" stroke="#1B273D" strokeOpacity="0.7" strokeWidth="1.2" />
</g>
<rect
x="16"
y="12.5"
width="2"
height="12"
transform="rotate(90 16 12.5)"
fill="url(#paint1_linear_760_2959)"
/>
<g mask="url(#mask1_760_2959)">
<path d="M15.5 13.4999L4 13.4999" stroke="#1B273D" strokeOpacity="0.7" strokeWidth="1.2" />
</g>
<defs>
<linearGradient
id="paint0_linear_760_2959"
x1="5"
y1="8"
x2="5"
y2="13.5"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#09101C" stopOpacity="0" />
<stop offset="1" stopColor="#09101C" />
</linearGradient>
<linearGradient
id="paint1_linear_760_2959"
x1="17"
y1="13"
x2="17"
y2="18.5"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#09101C" stopOpacity="0" />
<stop offset="1" stopColor="#09101C" />
</linearGradient>
</defs>
</svg>
)
}
export default PairIcon

View File

@ -0,0 +1,19 @@
import type { Meta, StoryObj } from '@storybook/react'
import QuickStartBar from './QuickStartBar'
const meta = {
title: 'General/QuickStartBar',
component: QuickStartBar,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof QuickStartBar>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {},
}

View File

@ -0,0 +1,47 @@
import './QuickStartBar.css'
function QuickStartBar() {
return (
<nav className="quick-start-bar">
<span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
>
<g clipPath="url(#clip0_760_8063)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M8.00008 14.3334C11.4979 14.3334 14.3334 11.4978 14.3334 8.00002C14.3334 4.50222 11.4979 1.66669 8.00008 1.66669C4.50228 1.66669 1.66675 4.50222 1.66675 8.00002C1.66675 11.4978 4.50228 14.3334 8.00008 14.3334ZM8.00008 15.3334C12.0502 15.3334 15.3334 12.0501 15.3334 8.00002C15.3334 3.94993 12.0502 0.666687 8.00008 0.666687C3.94999 0.666687 0.666748 3.94993 0.666748 8.00002C0.666748 12.0501 3.94999 15.3334 8.00008 15.3334Z"
fill="#939BA1"
/>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.82684 10.7661C4.76188 11.0129 4.98716 11.2382 5.23403 11.1733L9.74792 9.98539C9.86412 9.95481 9.95487 9.86406 9.98545 9.74786L11.1733 5.23397C11.2383 4.9871 11.013 4.76182 10.7661 4.82678L6.25224 6.01465C6.13604 6.04523 6.04529 6.13598 6.01471 6.25218L4.82684 10.7661ZM6.17202 9.58376C6.13304 9.73189 6.26821 9.86706 6.41634 9.82808L8.9293 9.16677C9.0455 9.13619 9.13625 9.04544 9.16683 8.92924L9.82814 6.41628C9.86712 6.26815 9.73195 6.13298 9.58383 6.17196L7.07086 6.83327C6.95466 6.86385 6.86391 6.9546 6.83333 7.0708L6.17202 9.58376Z"
fill="#939BA1"
/>
</g>
<defs>
<clipPath id="clip0_760_8063">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
</span>
<div>
<ul>
<li>Learn More</li>
<li>Nodes Community</li>
<li>Documentation</li>
</ul>
<button className="inversed">Quick Start</button>
</div>
</nav>
)
}
export default QuickStartBar

View File

@ -1,31 +0,0 @@
import './QuickStartBar.css'
function QuickStartBar() {
return (
<nav className='quick-start-bar'>
<span>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
<g clip-path="url(#clip0_760_8063)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00008 14.3334C11.4979 14.3334 14.3334 11.4978 14.3334 8.00002C14.3334 4.50222 11.4979 1.66669 8.00008 1.66669C4.50228 1.66669 1.66675 4.50222 1.66675 8.00002C1.66675 11.4978 4.50228 14.3334 8.00008 14.3334ZM8.00008 15.3334C12.0502 15.3334 15.3334 12.0501 15.3334 8.00002C15.3334 3.94993 12.0502 0.666687 8.00008 0.666687C3.94999 0.666687 0.666748 3.94993 0.666748 8.00002C0.666748 12.0501 3.94999 15.3334 8.00008 15.3334Z" fill="#939BA1"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.82684 10.7661C4.76188 11.0129 4.98716 11.2382 5.23403 11.1733L9.74792 9.98539C9.86412 9.95481 9.95487 9.86406 9.98545 9.74786L11.1733 5.23397C11.2383 4.9871 11.013 4.76182 10.7661 4.82678L6.25224 6.01465C6.13604 6.04523 6.04529 6.13598 6.01471 6.25218L4.82684 10.7661ZM6.17202 9.58376C6.13304 9.73189 6.26821 9.86706 6.41634 9.82808L8.9293 9.16677C9.0455 9.13619 9.13625 9.04544 9.16683 8.92924L9.82814 6.41628C9.86712 6.26815 9.73195 6.13298 9.58383 6.17196L7.07086 6.83327C6.95466 6.86385 6.86391 6.9546 6.83333 7.0708L6.17202 9.58376Z" fill="#939BA1"/>
</g>
<defs>
<clipPath id="clip0_760_8063">
<rect width="16" height="16" fill="white"/>
</clipPath>
</defs>
</svg>
</span>
<div>
<ul>
<li>Learn More</li>
<li>Nodes Community</li>
<li>Documentation</li>
</ul>
<button className='inversed'>Quick Start</button>
</div>
</nav>
)
}
export default QuickStartBar

View File

@ -1,16 +0,0 @@
import { Button } from 'tamagui'
import { ReactNode } from 'react'
type ReactButtonProps = {
children: string
style?: unknown
icon?: ReactNode
size?: string
onClick?: () => void
}
const ReactButton = ({ children, ...props }: ReactButtonProps) => {
return <Button {...props}>{children}</Button>
}
export default ReactButton

View File

@ -1,24 +0,0 @@
import { ReactNode } from 'react'
import { Stack } from 'tamagui'
type ShadowBoxProps = {
boxStyle?: React.CSSProperties
children: ReactNode
}
const ShadowBox = ({boxStyle, children }: ShadowBoxProps) => {
return (
<Stack
style={{
boxSizing: 'border-box',
borderRadius: '16px',
boxShadow: '0px 4px 20px 0px rgba(9, 16, 28, 0.08)',
...boxStyle
}}
>
{children}
</Stack>
)
}
export default ShadowBox

View File

@ -0,0 +1,37 @@
import type { Meta, StoryObj } from '@storybook/react'
import StandardGauge from './StandardGauge'
const meta = {
title: 'General/StandardGauge',
component: StandardGauge,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
decorators: [
Story => (
<div style={{ height: '25vh' }}>
<Story />
</div>
),
],
} satisfies Meta<typeof StandardGauge>
export default meta
type Story = StoryObj<typeof meta>
export const WithDataPoints: Story = {
args: {
data: [
{ id: '1', color: 'red', label: 'Red', value: 42 },
{ id: '2', color: 'blue', label: 'Blue', value: 1337 },
],
},
}
export const WithoutDataPoints: Story = {
args: {
data: [],
},
}

View File

@ -1,19 +1,20 @@
import { ResponsivePie } from '@nivo/pie'
interface Data {
export interface GaugeDataPoint {
id: string
label: string
value: number
color: string
}
interface StandardGaugeProps {
data: Data[]
data: GaugeDataPoint[]
}
const StandardGauge = ({ data }: StandardGaugeProps) => (
<ResponsivePie
data={data}
margin={{ top: 40, right: 80, bottom: 80, left: 80 }}
margin={{ top: 0, right: 0, bottom: 0, left: 0 }}
innerRadius={0.65}
colors={datum => datum.data.color}
fit={false}
@ -22,6 +23,8 @@ const StandardGauge = ({ data }: StandardGaugeProps) => (
arcLinkLabelsColor={{ from: 'color' }}
enableArcLabels={false}
legends={[]}
motionConfig="gentle" // Define transition style
animate={false} // Enable animation
/>
)

View File

@ -6,13 +6,18 @@ interface DataPoint {
interface ChartData {
id: string
color: string
data: DataPoint[]
maxValue?: number
}
interface StandartLineChartProps {
data: ChartData[]
}
const StandartLineChart = ({ data }: StandartLineChartProps) => {
const maxMemory = data[0].maxValue || 'auto'
const colors = data.map(dataset => dataset.color)
return (
<ResponsiveLine
data={data}
@ -20,8 +25,8 @@ const StandartLineChart = ({ data }: StandartLineChartProps) => {
xScale={{ type: 'linear', min: 0, max: data[0].data.length }}
yScale={{
type: 'linear',
min: 'auto',
max: 'auto',
min: 0,
max: maxMemory,
stacked: true,
reverse: false,
}}
@ -32,14 +37,11 @@ const StandartLineChart = ({ data }: StandartLineChartProps) => {
enableGridX={false}
enableGridY={false}
enablePoints={false}
pointSize={1}
pointColor={{ theme: 'background' }}
pointBorderWidth={2}
pointBorderColor={{ from: 'serieColor' }}
pointLabelYOffset={-12}
useMesh={true}
legends={[]}
colors={['#8DC6BC']}
colors={colors}
/>
)
}

View File

@ -1,9 +1,16 @@
import { Paragraph, styled } from 'tamagui'
import { Text } from '@status-im/components'
const SubTitle = styled(Paragraph, {
name: 'SubTitle',
accessibilityRole: 'header',
size: '$3',
})
type SubTitleProps = {
color?: string
children: string
}
const SubTitle = ({ color, children }: SubTitleProps) => {
return (
<Text size={15} color={color}>
{children}
</Text>
)
}
export default SubTitle

View File

@ -1,9 +1,16 @@
import { Paragraph, styled } from 'tamagui'
import { Text } from '@status-im/components'
const Title = styled(Paragraph, {
name: 'Title',
accessibilityRole: 'header',
size: '$9',
})
type TitleProps = {
color?: string
children: string
}
const Title = ({ color, children }: TitleProps) => {
return (
<Text size={27} weight={'medium'} color={color}>
{children}
</Text>
)
}
export default Title

View File

@ -0,0 +1,29 @@
import type { Meta, StoryObj } from '@storybook/react'
import Titles from './Titles'
const meta = {
title: 'General/Titles',
component: Titles,
tags: ['autodocs'],
} satisfies Meta<typeof Titles>
export default meta
type Story = StoryObj<typeof meta>
export const WelcomeTitles: Story = {
args: {
title: 'Welcome, John. This is your complete access to a truly decentralized Web 3.0',
subtitle:
'Status Nodes allows you to finally take control and ownership of the services you wish to run in a completely trustless and decentralized manner.',
isAdvancedSettings: false,
},
}
export const DeviceHealthCheckTitles: Story = {
args: {
title: 'Device Health Check',
subtitle: 'Configure your device to start Staking on Nimbus',
isAdvancedSettings: true,
},
}

View File

@ -1,32 +1,28 @@
import { XStack, YStack } from 'tamagui'
import { Button, Text } from '@status-im/components'
import Icon from './Icon'
import Title from './Title'
import SubTitle from './SubTitle'
import IconButton from './IconButton'
type TitlesProps = {
title: string
subtitle: string
isAdvancedSettings?: boolean
}
const Titles = ({ title, subtitle }: TitlesProps) => {
const Titles = ({ title, subtitle, isAdvancedSettings }: TitlesProps) => {
return (
<YStack>
<XStack justifyContent="space-between">
<YStack style={{ width: '100%' }}>
<XStack style={{ justifyContent: 'space-between', alignItems: 'center' }}>
<Title color={'#09101C'}>{title}</Title>
<IconButton
style={{
backgroundColor: 'transparent',
border: '1px solid #DCE0E5',
color: '#09101C',
}}
size={'$3'}
icon={'/icons/reveal.png'}
fontSize={'$5'}
>
Advanced Settings
</IconButton>
{isAdvancedSettings && (
<Button size={32} variant="outline" icon={<Icon src={'/icons/reveal.png'} />}>
Advanced Settings
</Button>
)}
</XStack>
<SubTitle color={'#09101C'}>{subtitle}</SubTitle>
<Text size={15} weight="regular">
{subtitle}
</Text>
</YStack>
)
}

View File

@ -0,0 +1,35 @@
.breadcrumb-bar-nav {
width: 100%;
flex: 1 1 100%;
padding: 1rem 2rem;
margin: 0.5rem;
}
.breadcrumb-bar-ul {
list-style-type: none;
display: flex;
padding: 0;
margin: 0;
}
.breadcrumb-bar-li {
padding: 0 1em;
color: #647084;
font-size: 15px;
font-weight: 500;
position: relative;
}
.breadcrumb-bar-li::after {
display: inline-block;
content: url("../assets/chevron.svg");
color: #09101C;
position: absolute;
top: 2px;
left: 100%;
transform: translateX(-50%);
}
.breadcrumb-bar-li:last-child {
color: #09101C;
}
.breadcrumb-bar-li:last-child::after {
display: none;
}

View File

@ -9,10 +9,11 @@
.layout-left {
flex: 0 0 55%;
max-width: 55%;
z-index: 2;
}
.container {
display: flex;
flex-wrap: wrap;
flex-wrap: wrap;
justify-content: end;
height: 100%;
margin: 0 auto;
@ -21,13 +22,14 @@
max-width: 70%;
flex: 1 0 70%;
display: flex;
flex-wrap: wrap;
/* flex-wrap: wrap; */
flex-direction: column;
}
header {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem 0;
}
header > div {
display: flex;
@ -49,8 +51,12 @@ header > div {
padding: 4px 6px;
margin-left: 10px;
}
.content {
flex-grow: 1;
}
.content .title {
font-size: 27px;
font-weight: 600;
margin-bottom: 0.25em;
}
.content .subtitle {
@ -75,6 +81,7 @@ header > div {
.layout-right {
flex: 0 0 45%;
max-width: 45%;
z-index: 0;
}
.image-container {
@ -105,4 +112,4 @@ header > div {
/* height: 100%; */
height: 140%;
width: auto;
}
}

View File

@ -4,7 +4,7 @@
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
color: rgba(0, 0, 0, 0.87);
background-color: #242424;
font-synthesis: none;
@ -14,6 +14,10 @@
-webkit-text-size-adjust: 100%;
}
#storybook-root {
width: 100%;
}
body {
margin: 0;
display: flex;
@ -33,8 +37,8 @@ h1 {
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
cursor: pointer;
}
a:hover {
color: #535bf2;

View File

@ -6,5 +6,5 @@ import './index.css'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
</React.StrictMode>,
</React.StrictMode>
)

View File

@ -0,0 +1,16 @@
import type { Meta, StoryObj } from '@storybook/react'
import DeviceHealthCheck from './DeviceHealthCheck'
const meta = {
title: 'Pages/DeviceHealthCheck',
component: DeviceHealthCheck,
tags: ['autodocs'],
} satisfies Meta<typeof DeviceHealthCheck>
export default meta
type Story = StoryObj<typeof meta>
export const Page: Story = {
args: {},
}

View File

@ -0,0 +1,68 @@
import { Stack, XStack, YStack } from 'tamagui'
import LayoutComponent from '../../components/LayoutComponent'
import NimbusLogo from '../../components/NimbusLogo'
import Titles from '../../components/Titles'
import DeviceStorageHealth from '../../components/DeviceStorageHealth'
import DeviceCPULoad from '../../components/DeviceCPULoad'
import HealthInfoSection from '../../components/HealthInfoSection'
import { Button, InformationBox } from '@status-im/components'
import Icon from '../../components/Icon'
import DeviceMemory from '../../components/DeviceMemoryHealth'
import DeviceNetworkHealth from '../../components/DeviceNetworkHealth'
const DeviceHealthCheck = () => {
return (
<LayoutComponent
content={<DeviceHealthCheckContent />}
rightImageSrc="/background-images/eye-background.png"
/>
)
}
export default DeviceHealthCheck
const DeviceHealthCheckContent = () => {
return (
<div className="container-inner landing-page">
<YStack
space={'$4'}
style={{
justifyContent: 'end',
alignItems: 'start',
marginBottom: '2rem',
maxWidth: '100%',
}}
>
<NimbusLogo />
<Titles
title="Device Health Check"
subtitle="Configure your device to start Staking on Nimbus"
isAdvancedSettings={true}
/>
<XStack space={'$4'}>
<DeviceStorageHealth storage={44} maxStorage={30} />
<DeviceCPULoad load={[12, 123, 4, 90]} />
</XStack>
<XStack space={'$4'}>
<DeviceMemory currentMemory={[25, 31, 5, 14, 20, 81]} maxMemory={38} />
<DeviceNetworkHealth uploadRate={[1, 4, 23, 55]} downloadRate={[20, 3, 40, 56]} />
</XStack>
<HealthInfoSection
usedStorage={120}
maxStorage={160}
usedRamMemory={8}
maxRamMemory={16}
cpuClockRate={2.5}
networkLatency={75}
/>
<InformationBox
icon={<Icon src="/icons/close.png" width={11} height={11} />}
message="The information provided in the Nodes Health Check is meant to utilized as a guide to guage the readiness of your device, however please do your own due diligence prior to commiting any funds. Read our Health Check Disclosure for more information."
/>
<Stack style={{ marginTop: '1rem' }}>
<Button>Continue</Button>
</Stack>
</YStack>
</div>
)
}

View File

@ -1,50 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
const meta = {
title: 'Example/Button',
component: Button,
parameters: {
// Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/react/configure/story-layout
layout: 'centered',
},
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
tags: ['autodocs'],
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {
backgroundColor: { control: 'color' },
},
} satisfies Meta<typeof Button>;
export default meta;
type Story = StoryObj<typeof meta>;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Primary: Story = {
args: {
primary: true,
label: 'Button',
},
};
export const Secondary: Story = {
args: {
label: 'Button',
},
};
export const Large: Story = {
args: {
size: 'large',
label: 'Button',
},
};
export const Small: Story = {
args: {
size: 'small',
label: 'Button',
},
};

View File

@ -1,47 +0,0 @@
import './button.css'
interface ButtonProps {
/**
* Is this the principal call to action on the page?
*/
primary?: boolean
/**
* What background color to use
*/
backgroundColor?: string
/**
* How large should the button be?
*/
size?: 'small' | 'medium' | 'large'
/**
* Button contents
*/
label: string
/**
* Optional click handler
*/
onClick?: () => void
}
/**
* Primary UI component for user interaction
*/
export const Button = ({
primary = false,
size = 'medium',
backgroundColor,
label,
...props
}: ButtonProps) => {
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'
return (
<button
type="button"
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
style={{ backgroundColor }}
{...props}
>
{label}
</button>
)
}

View File

@ -1,364 +0,0 @@
import { Meta } from "@storybook/blocks";
import Github from "./assets/github.svg";
import Discord from "./assets/discord.svg";
import Youtube from "./assets/youtube.svg";
import Tutorials from "./assets/tutorials.svg";
import Styling from "./assets/styling.png";
import Context from "./assets/context.png";
import Assets from "./assets/assets.png";
import Docs from "./assets/docs.png";
import Share from "./assets/share.png";
import FigmaPlugin from "./assets/figma-plugin.png";
import Testing from "./assets/testing.png";
import Accessibility from "./assets/accessibility.png";
import Theming from "./assets/theming.png";
import AddonLibrary from "./assets/addon-library.png";
export const RightArrow = () => <svg
viewBox="0 0 14 14"
width="8px"
height="14px"
style={{
marginLeft: '4px',
display: 'inline-block',
shapeRendering: 'inherit',
verticalAlign: 'middle',
fill: 'currentColor',
'path fill': 'currentColor'
}}
>
<path d="m11.1 7.35-5.5 5.5a.5.5 0 0 1-.7-.7L10.04 7 4.9 1.85a.5.5 0 1 1 .7-.7l5.5 5.5c.2.2.2.5 0 .7Z" />
</svg>
<Meta title="Configure your project" />
<div className="sb-container">
<div className='sb-section-title'>
# Configure your project
Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community.
</div>
<div className="sb-section">
<div className="sb-section-item">
<img
src={Styling}
alt="A wall of logos representing different styling technologies"
/>
<h4 className="sb-section-item-heading">Add styling and CSS</h4>
<p className="sb-section-item-paragraph">Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.</p>
<a
href="https://storybook.js.org/docs/react/configure/styling-and-css"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-section-item">
<img
src={Context}
alt="An abstraction representing the composition of data for a component"
/>
<h4 className="sb-section-item-heading">Provide context and mocking</h4>
<p className="sb-section-item-paragraph">Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.</p>
<a
href="https://storybook.js.org/docs/react/writing-stories/decorators#context-for-mocking"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-section-item">
<img src={Assets} alt="A representation of typography and image assets" />
<div>
<h4 className="sb-section-item-heading">Load assets and resources</h4>
<p className="sb-section-item-paragraph">To link static files (like fonts) to your projects and stories, use the
`staticDirs` configuration option to specify folders to load when
starting Storybook.</p>
<a
href="https://storybook.js.org/docs/react/configure/images-and-assets"
target="_blank"
>Learn more<RightArrow /></a>
</div>
</div>
</div>
</div>
<div className="sb-container">
<div className='sb-section-title'>
# Do more with Storybook
Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs.
</div>
<div className="sb-section">
<div className="sb-features-grid">
<div className="sb-grid-item">
<img src={Docs} alt="A screenshot showing the autodocs tag being set, pointing a docs page being generated" />
<h4 className="sb-section-item-heading">Autodocs</h4>
<p className="sb-section-item-paragraph">Auto-generate living,
interactive reference documentation from your components and stories.</p>
<a
href="https://storybook.js.org/docs/react/writing-docs/autodocs"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-grid-item">
<img src={Share} alt="A browser window showing a Storybook being published to a chromatic.com URL" />
<h4 className="sb-section-item-heading">Publish to Chromatic</h4>
<p className="sb-section-item-paragraph">Publish your Storybook to review and collaborate with your entire team.</p>
<a
href="https://storybook.js.org/docs/react/sharing/publish-storybook#publish-storybook-with-chromatic"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-grid-item">
<img src={FigmaPlugin} alt="Windows showing the Storybook plugin in Figma" />
<h4 className="sb-section-item-heading">Figma Plugin</h4>
<p className="sb-section-item-paragraph">Embed your stories into Figma to cross-reference the design and live
implementation in one place.</p>
<a
href="https://storybook.js.org/docs/react/sharing/design-integrations#embed-storybook-in-figma-with-the-plugin"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-grid-item">
<img src={Testing} alt="Screenshot of tests passing and failing" />
<h4 className="sb-section-item-heading">Testing</h4>
<p className="sb-section-item-paragraph">Use stories to test a component in all its variations, no matter how
complex.</p>
<a
href="https://storybook.js.org/docs/react/writing-tests/introduction"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-grid-item">
<img src={Accessibility} alt="Screenshot of accessibility tests passing and failing" />
<h4 className="sb-section-item-heading">Accessibility</h4>
<p className="sb-section-item-paragraph">Automatically test your components for a11y issues as you develop.</p>
<a
href="https://storybook.js.org/docs/react/writing-tests/accessibility-testing"
target="_blank"
>Learn more<RightArrow /></a>
</div>
<div className="sb-grid-item">
<img src={Theming} alt="Screenshot of Storybook in light and dark mode" />
<h4 className="sb-section-item-heading">Theming</h4>
<p className="sb-section-item-paragraph">Theme Storybook's UI to personalize it to your project.</p>
<a
href="https://storybook.js.org/docs/react/configure/theming"
target="_blank"
>Learn more<RightArrow /></a>
</div>
</div>
</div>
</div>
<div className='sb-addon'>
<div className='sb-addon-text'>
<h4>Addons</h4>
<p className="sb-section-item-paragraph">Integrate your tools with Storybook to connect workflows.</p>
<a
href="https://storybook.js.org/integrations/"
target="_blank"
>Discover all addons<RightArrow /></a>
</div>
<div className='sb-addon-img'>
<img src={AddonLibrary} alt="Integrate your tools with Storybook to connect workflows." />
</div>
</div>
<div className="sb-section sb-socials">
<div className="sb-section-item">
<img src={Github} alt="Github logo" className="sb-explore-image"/>
Join our contributors building the future of UI development.
<a
href="https://github.com/storybookjs/storybook"
target="_blank"
>Star on GitHub<RightArrow /></a>
</div>
<div className="sb-section-item">
<img src={Discord} alt="Discord logo" className="sb-explore-image"/>
<div>
Get support and chat with frontend developers.
<a
href="https://discord.gg/storybook"
target="_blank"
>Join Discord server<RightArrow /></a>
</div>
</div>
<div className="sb-section-item">
<img src={Youtube} alt="Youtube logo" className="sb-explore-image"/>
<div>
Watch tutorials, feature previews and interviews.
<a
href="https://www.youtube.com/@chromaticui"
target="_blank"
>Watch on YouTube<RightArrow /></a>
</div>
</div>
<div className="sb-section-item">
<img src={Tutorials} alt="A book" className="sb-explore-image"/>
<p>Follow guided walkthroughs on for key workflows.</p>
<a
href="https://storybook.js.org/tutorials/"
target="_blank"
>Discover tutorials<RightArrow /></a>
</div>
</div>
<style>
{`
.sb-container {
margin-bottom: 48px;
}
.sb-section {
width: 100%;
display: flex;
flex-direction: row;
gap: 20px;
}
img {
object-fit: cover;
}
.sb-section-title {
margin-bottom: 32px;
}
.sb-section a:not(h1 a, h2 a, h3 a) {
font-size: 14px;
}
.sb-section-item, .sb-grid-item {
flex: 1;
display: flex;
flex-direction: column;
}
.sb-section-item-heading {
padding-top: 20px !important;
padding-bottom: 5px !important;
margin: 0 !important;
}
.sb-section-item-paragraph {
margin: 0;
padding-bottom: 10px;
}
.sb-chevron {
margin-left: 5px;
}
.sb-features-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-gap: 32px 20px;
}
.sb-socials {
display: grid;
grid-template-columns: repeat(4, 1fr);
}
.sb-socials p {
margin-bottom: 10px;
}
.sb-explore-image {
max-height: 32px;
align-self: flex-start;
}
.sb-addon {
width: 100%;
display: flex;
align-items: center;
position: relative;
background-color: #EEF3F8;
border-radius: 5px;
border: 1px solid rgba(0, 0, 0, 0.05);
background: #EEF3F8;
height: 180px;
margin-bottom: 48px;
overflow: hidden;
}
.sb-addon-text {
padding-left: 48px;
max-width: 240px;
}
.sb-addon-text h4 {
padding-top: 0px;
}
.sb-addon-img {
position: absolute;
left: 345px;
top: 0;
height: 100%;
width: 200%;
overflow: hidden;
}
.sb-addon-img img {
width: 650px;
transform: rotate(-15deg);
margin-left: 40px;
margin-top: -72px;
box-shadow: 0 0 1px rgba(255, 255, 255, 0);
backface-visibility: hidden;
}
@media screen and (max-width: 800px) {
.sb-addon-img {
left: 300px;
}
}
@media screen and (max-width: 600px) {
.sb-section {
flex-direction: column;
}
.sb-features-grid {
grid-template-columns: repeat(1, 1fr);
}
.sb-socials {
grid-template-columns: repeat(2, 1fr);
}
.sb-addon {
height: 280px;
align-items: flex-start;
padding-top: 32px;
overflow: hidden;
}
.sb-addon-text {
padding-left: 24px;
}
.sb-addon-img {
right: 0;
left: 0;
top: 130px;
bottom: 0;
overflow: hidden;
height: auto;
width: 124%;
}
.sb-addon-img img {
width: 1200px;
transform: rotate(-12deg);
margin-left: 0;
margin-top: 48px;
margin-bottom: -40px;
margin-left: -24px;
}
}
`}
</style>

View File

@ -1,27 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { Header } from './Header';
const meta = {
title: 'Example/Header',
component: Header,
// This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/react/writing-docs/autodocs
tags: ['autodocs'],
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: 'fullscreen',
},
} satisfies Meta<typeof Header>;
export default meta;
type Story = StoryObj<typeof meta>;
export const LoggedIn: Story = {
args: {
user: {
name: 'Jane Doe',
},
},
};
export const LoggedOut: Story = {};

View File

@ -1,54 +0,0 @@
import { Button } from './Button'
import './header.css'
type User = {
name: string
}
interface HeaderProps {
user?: User
onLogin: () => void
onLogout: () => void
onCreateAccount: () => void
}
export const Header = ({ user, onLogin, onLogout, onCreateAccount }: HeaderProps) => (
<header>
<div className="storybook-header">
<div>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M10 0h12a10 10 0 0110 10v12a10 10 0 01-10 10H10A10 10 0 010 22V10A10 10 0 0110 0z"
fill="#FFF"
/>
<path
d="M5.3 10.6l10.4 6v11.1l-10.4-6v-11zm11.4-6.2l9.7 5.5-9.7 5.6V4.4z"
fill="#555AB9"
/>
<path
d="M27.2 10.6v11.2l-10.5 6V16.5l10.5-6zM15.7 4.4v11L6 10l9.7-5.5z"
fill="#91BAF8"
/>
</g>
</svg>
<h1>Acme</h1>
</div>
<div>
{user ? (
<>
<span className="welcome">
Welcome, <b>{user.name}</b>!
</span>
<Button size="small" onClick={onLogout} label="Log out" />
</>
) : (
<>
<Button size="small" onClick={onLogin} label="Log in" />
<Button primary size="small" onClick={onCreateAccount} label="Sign up" />
</>
)}
</div>
</div>
</header>
)

View File

@ -1,29 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react';
import { within, userEvent } from '@storybook/testing-library';
import { Page } from './Page';
const meta = {
title: 'Example/Page',
component: Page,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: 'fullscreen',
},
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const LoggedOut: Story = {};
// More on interaction testing: https://storybook.js.org/docs/react/writing-tests/interaction-testing
export const LoggedIn: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const loginButton = await canvas.getByRole('button', {
name: /Log in/i,
});
await userEvent.click(loginButton);
},
};

View File

@ -1,73 +0,0 @@
import React from 'react';
import { Header } from './Header';
import './page.css';
type User = {
name: string;
};
export const Page: React.FC = () => {
const [user, setUser] = React.useState<User>();
return (
<article>
<Header
user={user}
onLogin={() => setUser({ name: 'Jane Doe' })}
onLogout={() => setUser(undefined)}
onCreateAccount={() => setUser({ name: 'Jane Doe' })}
/>
<section className="storybook-page">
<h2>Pages in Storybook</h2>
<p>
We recommend building UIs with a{' '}
<a href="https://componentdriven.org" target="_blank" rel="noopener noreferrer">
<strong>component-driven</strong>
</a>{' '}
process starting with atomic components and ending with pages.
</p>
<p>
Render pages with mock data. This makes it easy to build and review page states without
needing to navigate to them in your app. Here are some handy patterns for managing page
data in Storybook:
</p>
<ul>
<li>
Use a higher-level connected component. Storybook helps you compose such data from the
"args" of child component stories
</li>
<li>
Assemble data in the page component from your services. You can mock these services out
using Storybook.
</li>
</ul>
<p>
Get a guided tutorial on component-driven development at{' '}
<a href="https://storybook.js.org/tutorials/" target="_blank" rel="noopener noreferrer">
Storybook tutorials
</a>
. Read more in the{' '}
<a href="https://storybook.js.org/docs" target="_blank" rel="noopener noreferrer">
docs
</a>
.
</p>
<div className="tip-wrapper">
<span className="tip">Tip</span> Adjust the width of the canvas with the{' '}
<svg width="10" height="10" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fillRule="evenodd">
<path
d="M1.5 5.2h4.8c.3 0 .5.2.5.4v5.1c-.1.2-.3.3-.4.3H1.4a.5.5 0 01-.5-.4V5.7c0-.3.2-.5.5-.5zm0-2.1h6.9c.3 0 .5.2.5.4v7a.5.5 0 01-1 0V4H1.5a.5.5 0 010-1zm0-2.1h9c.3 0 .5.2.5.4v9.1a.5.5 0 01-1 0V2H1.5a.5.5 0 010-1zm4.3 5.2H2V10h3.8V6.2z"
id="a"
fill="#999"
/>
</g>
</svg>
Viewports addon in the toolbar
</div>
</section>
</article>
);
};

View File

@ -1,30 +0,0 @@
.storybook-button {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 700;
border: 0;
border-radius: 3em;
cursor: pointer;
display: inline-block;
line-height: 1;
}
.storybook-button--primary {
color: white;
background-color: #1ea7fd;
}
.storybook-button--secondary {
color: #333;
background-color: transparent;
box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}
.storybook-button--small {
font-size: 12px;
padding: 10px 16px;
}
.storybook-button--medium {
font-size: 14px;
padding: 11px 20px;
}
.storybook-button--large {
font-size: 16px;
padding: 12px 24px;
}

View File

@ -1,32 +0,0 @@
.storybook-header {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 15px 20px;
display: flex;
align-items: center;
justify-content: space-between;
}
.storybook-header svg {
display: inline-block;
vertical-align: top;
}
.storybook-header h1 {
font-weight: 700;
font-size: 20px;
line-height: 1;
margin: 6px 0 6px 10px;
display: inline-block;
vertical-align: top;
}
.storybook-header button + button {
margin-left: 10px;
}
.storybook-header .welcome {
color: #333;
font-size: 14px;
margin-right: 10px;
}

View File

@ -1,69 +0,0 @@
.storybook-page {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 24px;
padding: 48px 20px;
margin: 0 auto;
max-width: 600px;
color: #333;
}
.storybook-page h2 {
font-weight: 700;
font-size: 32px;
line-height: 1;
margin: 0 0 4px;
display: inline-block;
vertical-align: top;
}
.storybook-page p {
margin: 1em 0;
}
.storybook-page a {
text-decoration: none;
color: #1ea7fd;
}
.storybook-page ul {
padding-left: 30px;
margin: 1em 0;
}
.storybook-page li {
margin-bottom: 8px;
}
.storybook-page .tip {
display: inline-block;
border-radius: 1em;
font-size: 11px;
line-height: 12px;
font-weight: 700;
background: #e7fdd8;
color: #66bf3c;
padding: 4px 12px;
margin-right: 10px;
vertical-align: top;
}
.storybook-page .tip-wrapper {
font-size: 13px;
line-height: 20px;
margin-top: 40px;
margin-bottom: 40px;
}
.storybook-page .tip-wrapper svg {
display: inline-block;
height: 12px;
width: 12px;
margin-right: 4px;
vertical-align: top;
margin-top: 3px;
}
.storybook-page .tip-wrapper svg path {
fill: #1ea7fd;
}