Skip to content

Commit

Permalink
feat: storybook intro, a lot of small changes in new components (#2766)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnthecat authored Dec 3, 2024
1 parent ad85f08 commit da6081b
Show file tree
Hide file tree
Showing 23 changed files with 274 additions and 120 deletions.
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"jsdocPreferCodeFences": true,
"jsdocSeparateTagGroups": true,
"jsdocPrintWidth": 80,
"proseWrap": "always",
"overrides": [
{
"files": ["./src/renderer/domains/**/*.ts", "./src/renderer/shared/pallet/**/*.ts", "./src/renderer/features/fellowship-*/**/*.{ts,tsx}"],
Expand Down
61 changes: 50 additions & 11 deletions .storybook/intro/Intro.mdx
Original file line number Diff line number Diff line change
@@ -1,16 +1,55 @@
import { Icon } from '@/shared/ui'
import { Box } from '@/shared/ui-kit'

# Spectr components library
# Spektr components library

We've got 4 sources of components:
Collection of ui primitives and utils for building <a href="https://novaspektr.io/" target="_blank">Nova Spektr</a> and related applications.

```
@/shared/ui-kit
@/shared/ui-entities
```
## How to import

And deprecated
Library is a combination of several sources:

* <Box direction='row' verticalAlign='center'>`Design System / kit` <Icon name="right" size={12} /> `@/shared/ui-kit`</Box>
* <Box direction='row' verticalAlign='center'>`Design System / entities` <Icon name="right" size={12} /> `@/shared/ui-entities`</Box>
* <Box direction='row' verticalAlign='center'>`v1 / kit` <Icon name="right" size={12} /> `@/shared/ui` <sup><small>deprecated</small></sup></Box>
* <Box direction='row' verticalAlign='center'>`v1 / entities` <Icon name="right" size={12} /> `@/entities/*` <sup><small>deprecated</small></sup></Box>

## Basic conceptions

### Separation

Components are segregated into two distinct branches, ensuring library modularity and preventing feature bloat.

#### kit: the basic building blocks

Components classified as `kit` represent foundational elements characterized by:

- Interaction with generic data types like text, numbers, dates, and lists;
- Generation of abstract user interactions outside of specific business context;
- Building of basic layout.

Illustrative examples:
- `Button` triggers a click without encoding specific workflow logic;
- `Select` picks value without knowing semantic meaning;
- `Input` edits some text;
- `Box` provides flexible layout scaffolding.

#### entities: domain-specific components

`entities` encapsulate components with specialized knowledge of the Polkadot ecosystem.

Illustrative examples:
- `Address` rendering hash with identicon;
- `Account` combining address with chain-specific explorer links;
- `AssetIcon` picking certain icon for given asset.

### Composition
Each component is engineered with a singular, focused responsibility.
Any variations in behavior and presentation are achieved through component composition rather than customization.

### Minimal API surface
Main goal is to make props of each component simple and memorable. It's achieved by some restrictions:
- No `className` prop;
- Almost no dom element props in component API (except for integration between components);
- Finite number of component states. In theory developer can write examples for all combinations of props.

```
@/shared/ui
@/entities/*
```
4 changes: 2 additions & 2 deletions .storybook/preview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const preview: Preview = {
options: {
storySort: {
method: 'alphabetical',
order: ['Design System', ['kit', 'entities'], 'v1', ['ui', 'entities'], '*'],
order: ['Intro', 'Design System', ['kit', 'entities'], 'v1', ['ui', 'entities'], '*'],
},
},
},
Expand All @@ -25,7 +25,7 @@ const preview: Preview = {
},
(Story) => {
return (
<ThemeProvider bodyAsPortalContainer>
<ThemeProvider bodyAsPortalContainer iconStyle='colored'>
<Story />
</ThemeProvider>
);
Expand Down
18 changes: 18 additions & 0 deletions src/renderer/app/document.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
html, body {
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
transition: max-height 0.5s ease-in-out;
}

body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

@media (max-aspect-ratio: 32 / 25) and (width < 1024px) {
html, body {
max-height: 800px;
}
}
28 changes: 8 additions & 20 deletions src/renderer/app/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,6 @@
@tailwind components;
@tailwind utilities;

html, body {
width: 100%;
max-height: 100%;
height: 100%;
overflow: hidden;
transition: max-height 0.5s ease-in-out;
}

body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

@media (max-aspect-ratio: 32 / 25) and (width < 1024px) {
html, body {
max-height: 800px;
}
}


/* Scrollbar for Firefox */
* {
scrollbar-width: thin;
Expand Down Expand Up @@ -57,6 +37,14 @@ body {
background-color: var(--scroll-background-default);
}

/* Search input reset */
[type="search"]::-webkit-search-cancel-button,
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
appearance: none;
}


*:focus-visible {
outline: 2px solid var(--focus-container-border);
}
Expand Down
1 change: 1 addition & 0 deletions src/renderer/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './index.css';
import './document.css';
import './styles/theme/default.css';

import { Suspense, useEffect, useMemo, useState } from 'react';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const SelectWalletPairing = () => {
<Dropdown.Content>
{getDropdownOptions(t).map(({ title, walletType }) => (
<Dropdown.Item key={title} onSelect={() => walletPairingModel.events.walletTypeSet(walletType)}>
<div className="flex items-center gap-x-1.5">
<div className="flex items-center gap-2">
<WalletIcon type={walletType} />
{title}
</div>
Expand Down
6 changes: 3 additions & 3 deletions src/renderer/pages/Staking/ui/Actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ export const Actions = ({ canInteract, stakes, isStakingLoading, onNavigate }: P
<div className="flex items-center justify-between">
<SmallTitleText>{t('staking.overview.actionsTitle')}</SmallTitleText>
<div className="min-w-[228px]">
<Dropdown open={isActionsOpen} onToggle={toggleIsActionsOpen}>
<Dropdown width="trigger" open={isActionsOpen} onToggle={toggleIsActionsOpen}>
<Dropdown.Trigger>
<Button
disabled={isStakingLoading || noStakes || wrongOverlaps}
Expand All @@ -149,7 +149,7 @@ export const Actions = ({ canInteract, stakes, isStakingLoading, onNavigate }: P
{getActionButtonText()}
</Button>
</Dropdown.Trigger>
<Dropdown.Content width="trigger">
<Dropdown.Content>
{Object.entries(operationsSummary).map(([key, value]) => {
if (stakes.length !== value) return null;

Expand All @@ -158,7 +158,7 @@ export const Actions = ({ canInteract, stakes, isStakingLoading, onNavigate }: P

return (
<Dropdown.Item key={key} onSelect={() => onClickAction(typedKey, option.path)}>
<div className="flex w-full items-center gap-x-1.5">
<div className="flex w-full items-center gap-2">
<Icon name={option.icon} size={20} className="shrink-0 text-icon-accent" />
{t(option.title)}
</div>
Expand Down
36 changes: 17 additions & 19 deletions src/renderer/pages/Staking/ui/NominatorItem.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
import { useUnit } from 'effector-react';
import { type ReactNode } from 'react';

import { type Account, type Address, type Asset, type Explorer } from '@/shared/core';
import { type Account, type Address, type Asset, type Chain } from '@/shared/core';
import { useI18n } from '@/shared/i18n';
import { cnTw } from '@/shared/lib/utils';
import { FootnoteText, Icon, IconButton, Plate, Shimmering } from '@/shared/ui';
import { cnTw, nonNullable } from '@/shared/lib/utils';
import { FootnoteText, Icon, Plate, Shimmering } from '@/shared/ui';
import { AccountExplorers } from '@/shared/ui-entities';
import { Checkbox } from '@/shared/ui-kit';
import { AssetBalance } from '@/entities/asset';
import { AssetFiatBalance } from '@/entities/price';
import { ExplorersPopover, walletModel, walletUtils } from '@/entities/wallet';
import { walletModel, walletUtils } from '@/entities/wallet';
import { type NominatorInfo } from '../lib/types';

type Props = {
nominatorsLength: number;
asset?: Asset;
explorers?: Explorer[];
chain: Chain;
isStakingLoading: boolean;
stake: NominatorInfo<Account>;
content: ReactNode;
addressPrefix?: number;
onToggleNominator: (nominator: Address, boolean: boolean) => void;
onCheckValidators: (stash?: Address) => void;
};

export const NominatorsItem = ({
nominatorsLength,
asset,
explorers = [],
chain,
stake,
content,
isStakingLoading,
addressPrefix,

onToggleNominator,
onCheckValidators,
}: Props) => {
Expand Down Expand Up @@ -79,31 +79,29 @@ export const NominatorsItem = ({
</>
)}
</div>
<ExplorersPopover
button={<IconButton name="details" />}
address={stake.address}
addressPrefix={addressPrefix}
explorers={explorers}
>
<ExplorersPopover.Group active={Boolean(stake.stash)}>
<AccountExplorers accountId={stake.account.accountId} chain={chain}>
{nonNullable(stake.stash) && (
<button
type="button"
className={cnTw(
'group flex h-full w-full items-center gap-x-2 rounded-md px-2 py-1 transition-colors',
'group -mx-2 flex select-none items-center gap-x-1.5 rounded-md px-1.5 py-[3px] transition-colors',
'hover:bg-action-background-hover focus:bg-action-background-hover',
)}
onClick={() => onCheckValidators(stake.stash)}
>
<Icon name="viewValidators" size={16} />
<FootnoteText
as="span"
className="text-text-secondary transition-colors group-hover:text-text-primary group-focus:text-text-primary"
className={cnTw(
'text-text-secondary transition-colors',
'group-hover:text-text-primary group-focus:text-text-primary',
)}
>
{t('staking.overview.viewValidatorsOption')}
</FootnoteText>
</button>
</ExplorersPopover.Group>
</ExplorersPopover>
)}
</AccountExplorers>
</Plate>
);
};
21 changes: 5 additions & 16 deletions src/renderer/pages/Staking/ui/NominatorsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@ import { type ApiPromise } from '@polkadot/api';
import { type ReactNode } from 'react';
import { Trans } from 'react-i18next';

import {
type Account,
type Address,
type Asset,
type BaseAccount,
type Explorer,
type ShardAccount,
} from '@/shared/core';
import { type Account, type Address, type Asset, type BaseAccount, type Chain, type ShardAccount } from '@/shared/core';
import { useI18n } from '@/shared/i18n';
import { cnTw } from '@/shared/lib/utils';
import { FootnoteText, HelpText, Icon, Tooltip } from '@/shared/ui';
Expand All @@ -27,8 +20,7 @@ type Props = {
api?: ApiPromise;
era?: number;
asset?: Asset;
explorers?: Explorer[];
addressPrefix?: number;
chain: Chain;
onCheckValidators: (stash?: Address) => void;
onToggleNominator: (nominator: Address, value?: boolean) => void;
};
Expand All @@ -38,9 +30,8 @@ export const NominatorsList = ({
era,
nominators,
asset,
explorers,
chain,
isStakingLoading,
addressPrefix,
onCheckValidators,
onToggleNominator,
}: Props) => {
Expand Down Expand Up @@ -107,8 +98,7 @@ export const NominatorsList = ({
shardsStake={stake}
era={era}
asset={asset}
explorers={explorers}
addressPrefix={addressPrefix}
chain={chain}
getContent={getContent}
onToggleNominator={onToggleNominator}
onCheckValidators={onCheckValidators}
Expand All @@ -125,8 +115,7 @@ export const NominatorsList = ({
stake={stake}
nominatorsLength={nominators.length}
asset={asset}
explorers={explorers}
addressPrefix={addressPrefix}
chain={chain}
onToggleNominator={onToggleNominator}
onCheckValidators={onCheckValidators}
/>
Expand Down
9 changes: 4 additions & 5 deletions src/renderer/pages/Staking/ui/ShardedList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type ReactNode } from 'react';
import { Trans } from 'react-i18next';

import { type Address, type Asset, type Explorer } from '@/shared/core';
import { type Address, type Asset, type Chain, type Explorer } from '@/shared/core';
import { type ShardAccount } from '@/shared/core/types/account';
import { useI18n } from '@/shared/i18n';
import { Accordion, FootnoteText, Plate, Shimmering, Tooltip } from '@/shared/ui';
Expand All @@ -18,6 +18,7 @@ type Props = {
isStakingLoading: boolean;
era?: number;
asset?: Asset;
chain: Chain;
explorers?: Explorer[];
addressPrefix?: number;
onCheckValidators: (stash?: Address) => void;
Expand All @@ -29,9 +30,8 @@ export const ShardedList = ({
shardsStake,
era,
asset,
explorers,
chain,
isStakingLoading,
addressPrefix,
onCheckValidators,
onToggleNominator,
getContent,
Expand Down Expand Up @@ -141,8 +141,7 @@ export const ShardedList = ({
stake={shard}
nominatorsLength={shardsStake.length}
asset={asset}
explorers={explorers}
addressPrefix={addressPrefix}
chain={chain}
onToggleNominator={onToggleNominator}
onCheckValidators={onCheckValidators}
/>
Expand Down
5 changes: 2 additions & 3 deletions src/renderer/pages/Staking/ui/Staking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ export const Staking = () => {
/>
</NetworkInfo>

{networkIsActive && accounts.length > 0 && (
{networkIsActive && accounts.length > 0 && activeChain && (
<>
<Actions
canInteract={!!activeWallet && permissionUtils.canStake(activeWallet)}
Expand All @@ -322,9 +322,8 @@ export const Staking = () => {
era={chainId ? chainEra[chainId] : undefined}
nominators={nominatorsInfo}
asset={relaychainAsset}
explorers={activeChain?.explorers}
chain={activeChain}
isStakingLoading={isStakingLoading}
addressPrefix={addressPrefix}
onCheckValidators={openSelectedValidators}
onToggleNominator={toggleSelectedNominators}
/>
Expand Down
Loading

0 comments on commit da6081b

Please sign in to comment.