Skip to content

Commit

Permalink
fix(ui): #1744: ValueViewComponent update (#1755)
Browse files Browse the repository at this point in the history
* fix(ui): #1744: add `size` prop to `AssetIcon`

* fix(ui): #1743: update padding in the `Popover` component

* fix(ui): #1744: simplify the `size` of `AssetIcon`

* fix(ui): #1744: align the `ValueViewComponent` with the latest designs

* chore: changeset
  • Loading branch information
VanishMax authored Sep 3, 2024
1 parent 516caf3 commit 5100518
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 44 deletions.
5 changes: 5 additions & 0 deletions .changeset/witty-bananas-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@penumbra-zone/ui': patch
---

Update UI components: `ValueViewComponent`, `AssetIcon`, and `Popover`
2 changes: 0 additions & 2 deletions packages/ui/src/AssetIcon/DelegationTokenIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ const Svg = styled.svg.attrs({
})`
display: block;
border-radius: ${props => props.theme.borderRadius.full};
width: 24px;
height: 24px;
`;

const getFirstEightCharactersOfValidatorId = (displayDenom = ''): [string, string] => {
Expand Down
2 changes: 0 additions & 2 deletions packages/ui/src/AssetIcon/UnbondingTokenIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ const Svg = styled.svg.attrs({
})`
display: block;
border-radius: ${props => props.theme.borderRadius.full};
width: 24px;
height: 24px;
`;

const getFirstEightCharactersOfValidatorId = (displayDenom = ''): [string, string] => {
Expand Down
37 changes: 37 additions & 0 deletions packages/ui/src/AssetIcon/index.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { Meta, StoryObj } from '@storybook/react';

import { AssetIcon } from '.';
import {
PENUMBRA_METADATA,
DELEGATION_TOKEN_METADATA,
UNBONDING_TOKEN_METADATA,
UNKNOWN_TOKEN_METADATA,
PIZZA_METADATA,
} from '../utils/bufs';

const meta: Meta<typeof AssetIcon> = {
component: AssetIcon,
tags: ['autodocs', '!dev'],
argTypes: {
metadata: {
options: ['Penumbra', 'Pizza', 'Delegation token', 'Unbonding token', 'Unknown asset'],
mapping: {
Penumbra: PENUMBRA_METADATA,
Pizza: PIZZA_METADATA,
'Delegation token': DELEGATION_TOKEN_METADATA,
'Unbonding token': UNBONDING_TOKEN_METADATA,
'Unknown asset': UNKNOWN_TOKEN_METADATA,
},
},
},
};
export default meta;

type Story = StoryObj<typeof AssetIcon>;

export const Basic: Story = {
args: {
size: 'md',
metadata: PENUMBRA_METADATA,
},
};
68 changes: 39 additions & 29 deletions packages/ui/src/AssetIcon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,52 +1,62 @@
import { ReactNode } from 'react';
import styled from 'styled-components';
import { Metadata } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb';
import { Identicon } from '../Identicon';
import { DelegationTokenIcon } from './DelegationTokenIcon';
import { getDisplay } from '@penumbra-zone/getters/metadata';
import { assetPatterns } from '@penumbra-zone/types/assets';
import { Identicon } from '../Identicon';
import { DelegationTokenIcon } from './DelegationTokenIcon';
import { UnbondingTokenIcon } from './UnbondingTokenIcon';
import styled from 'styled-components';

const BorderWrapper = styled.div`
type Size = 'lg' | 'md' | 'sm';

const sizeMap: Record<Size, number> = {
lg: 32,
md: 24,
sm: 16,
};

const BorderWrapper = styled.div<{ $size: Size }>`
width: ${props => sizeMap[props.$size]}px;
height: ${props => sizeMap[props.$size]}px;
border-radius: ${props => props.theme.borderRadius.full};
border: 1px solid ${props => props.theme.color.other.tonalStroke};
overflow: hidden;
& > * {
width: 100%;
height: 100%;
}
`;

const IconImg = styled.img`
display: block;
width: 24px;
height: 24px;
`;

export interface AssetIcon {
export interface AssetIconProps {
size?: Size;
metadata?: Metadata;
}

export const AssetIcon = ({ metadata }: AssetIcon) => {
export const AssetIcon = ({ metadata, size = 'md' }: AssetIconProps) => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- possibly empty string
const icon = metadata?.images[0]?.png || metadata?.images[0]?.svg;
const display = getDisplay.optional()(metadata);
const isDelegationToken = display ? assetPatterns.delegationToken.matches(display) : false;
const isUnbondingToken = display ? assetPatterns.unbondingToken.matches(display) : false;

return (
<BorderWrapper>
{/* eslint-disable-next-line no-nested-ternary -- readable ternary */}
{icon ? (
<IconImg src={icon} alt='Asset icon' />
) : // eslint-disable-next-line no-nested-ternary -- readable ternary
isDelegationToken ? (
<DelegationTokenIcon displayDenom={display} />
) : isUnbondingToken ? (
/**
* @todo: Render a custom unbonding token for validators that have a
* logo -- e.g., with the validator ID superimposed over the validator
* logo.
*/
<UnbondingTokenIcon displayDenom={display} />
) : (
<Identicon uniqueIdentifier={metadata?.symbol ?? '?'} size={24} type='solid' />
)}
</BorderWrapper>
);
let assetIcon: ReactNode;
if (icon) {
assetIcon = <IconImg src={icon} alt='Asset icon' />;
} else if (isDelegationToken) {
assetIcon = <DelegationTokenIcon displayDenom={display} />;
} else if (isUnbondingToken) {
/**
* @todo: Render a custom unbonding token for validators that have a
* logo -- e.g., with the validator ID superimposed over the validator logo.
*/
assetIcon = <UnbondingTokenIcon displayDenom={display} />;
} else {
assetIcon = <Identicon uniqueIdentifier={metadata?.symbol ?? '?'} type='solid' />;
}

return <BorderWrapper $size={size}>{assetIcon}</BorderWrapper>;
};
Empty file.
30 changes: 20 additions & 10 deletions packages/ui/src/ValueViewComponent/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ReactNode } from 'react';
import styled from 'styled-components';
import { ValueView } from '@penumbra-zone/protobuf/penumbra/core/asset/v1/asset_pb';
import { getMetadata } from '@penumbra-zone/getters/value-view';
import { getFormattedAmtFromValueView } from '@penumbra-zone/types/value-view';
import { ConditionalWrap } from '../ConditionalWrap';
import { Pill } from '../Pill';
import { Pill, PillProps } from '../Pill';
import { Text } from '../Text';
import styled from 'styled-components';
import { AssetIcon } from '../AssetIcon';
import { getMetadata } from '@penumbra-zone/getters/value-view';
import { getFormattedAmtFromValueView } from '@penumbra-zone/types/value-view';
import { Density } from '../types/Density';
import { useDensity } from '../hooks/useDensity';

Expand All @@ -25,8 +26,9 @@ const AssetIconWrapper = styled.div`
`;

const PillMarginOffsets = styled.div<{ $density: Density }>`
margin-left: ${props => props.theme.spacing(props.$density === 'sparse' ? -2 : -1)};
margin-right: ${props => props.theme.spacing(props.$density === 'sparse' ? -1 : 0)};
margin-left: ${props => props.theme.spacing(-2)};
margin-top: ${props => props.theme.spacing(props.$density === 'sparse' ? 0 : -1)};
margin-bottom: ${props => props.theme.spacing(props.$density === 'sparse' ? 0 : -1)};
`;

const Content = styled.div<{ $context: Context; $priority: 'primary' | 'secondary' }>`
Expand Down Expand Up @@ -54,6 +56,14 @@ const SymbolWrapper = styled.div`
white-space: nowrap;
`;

const ValueText = ({ children, density }: { children: ReactNode; density: Density }) => {
if (density === 'sparse') {
return <Text body>{children}</Text>;
}

return <Text detail>{children}</Text>;
};

export interface ValueViewComponentProps<SelectedContext extends Context> {
valueView?: ValueView;
/**
Expand All @@ -67,7 +77,7 @@ export interface ValueViewComponentProps<SelectedContext extends Context> {
* represents a secondary value, such as when it's an equivalent value of a
* numeraire.
*/
priority?: 'primary' | 'secondary';
priority?: PillProps['priority'];
}

/**
Expand Down Expand Up @@ -104,13 +114,13 @@ export const ValueViewComponent = <SelectedContext extends Context = 'default'>(
>
<Row>
<AssetIconWrapper>
<AssetIcon metadata={metadata} />
<AssetIcon size={density === 'sparse' ? 'lg' : 'md'} metadata={metadata} />
</AssetIconWrapper>

<Content $context={context ?? 'default'} $priority={priority}>
<Text>{formattedAmount} </Text>
<ValueText density={density}>{formattedAmount} </ValueText>
<SymbolWrapper title={symbol}>
<Text technical>{symbol}</Text>
<ValueText density={density}>{symbol}</ValueText>
</SymbolWrapper>
</Content>
</Row>
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/utils/bufs/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,7 @@ export const PIZZA_METADATA = new Metadata({
display: 'pizza',
denomUnits: [{ denom: 'upizza' }, { denom: 'pizza', exponent: 6 }],
});

export const UNKNOWN_TOKEN_METADATA = new Metadata({
penumbraAssetId: { inner: new Uint8Array([]) },
});
2 changes: 1 addition & 1 deletion packages/ui/src/utils/popover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const PopoverContent = styled.div`
width: 240px;
max-width: 320px;
padding: ${props => props.theme.spacing(3)} ${props => props.theme.spacing(2)};
padding: ${props => props.theme.spacing(3)};
background: ${props => props.theme.color.other.dialogBackground};
border: 1px solid ${props => props.theme.color.other.tonalStroke};
Expand Down

0 comments on commit 5100518

Please sign in to comment.