Compare commits

...
9 changed files with 180 additions and 17 deletions
@@ -1,5 +1,4 @@
import { render, screen } from '@testing-library/react';
import { formatLabel } from '@vegaprotocol/react-helpers';
import {
OrderRejectionReason,
OrderStatus,
@@ -7,6 +6,7 @@ import {
Side,
} from '@vegaprotocol/types';
import { VegaTxStatus } from '@vegaprotocol/wallet';
import startCase from 'lodash/startCase';
import { generateOrder } from '../mocks/generate-orders';
import type { OrderFeedbackProps } from './order-feedback';
import { OrderFeedback } from './order-feedback';
@@ -45,7 +45,7 @@ describe('OrderFeedback', () => {
const order = generateOrder(orderFields);
render(<OrderFeedback {...props} order={order} />);
expect(screen.getByTestId('error-reason')).toHaveTextContent(
`Reason: ${formatLabel(orderFields.rejectionReason)}`
`Reason: ${startCase(orderFields.rejectionReason)}`
);
});
@@ -1,12 +1,9 @@
import { useEnvironment } from '@vegaprotocol/environment';
import type { OrderEvent_busEvents_event_Order } from '../../order-hooks/__generated__';
import {
addDecimalsFormatNumber,
formatLabel,
t,
} from '@vegaprotocol/react-helpers';
import { addDecimalsFormatNumber, t } from '@vegaprotocol/react-helpers';
import { OrderStatus, OrderType, Side } from '@vegaprotocol/types';
import type { VegaTxState } from '@vegaprotocol/wallet';
import startCase from 'lodash/startCase';
export interface OrderFeedbackProps {
transaction: VegaTxState;
@@ -23,7 +20,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
return (
<p data-testid="error-reason">
{order.rejectionReason &&
t(`Reason: ${formatLabel(order.rejectionReason)}`)}
t(`Reason: ${startCase(order.rejectionReason)}`)}
</p>
);
}
@@ -44,7 +41,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
<div>
<p className={labelClass}>{t('Transaction')}</p>
<a
className="underline break-words"
className="underline break-all"
data-testid="tx-block-explorer"
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
target="_blank"
@@ -102,7 +99,7 @@ export const OrderFeedback = ({ transaction, order }: OrderFeedbackProps) => {
<div>
<p className={labelClass}>{t('Transaction')}</p>
<a
className="underline break-words"
className="underline break-all"
data-testid="tx-block-explorer"
href={`${VEGA_EXPLORER_URL}/txs/0x${transaction.txHash}`}
target="_blank"
+2 -1
View File
@@ -23,13 +23,14 @@ export * from './price-change';
export * from './radio-group';
export * from './resizable-panel';
export * from './select';
export * from './slider';
export * from './sparkline';
export * from './splash';
export * from './syntax-highlighter';
export * from './tabs';
export * from './text-area';
export * from './theme-switcher';
export * from './toast';
export * from './toggle';
export * from './tooltip';
export * from './vega-logo';
export * from './slider';
@@ -0,0 +1 @@
export * from './toast';
@@ -0,0 +1,62 @@
import React, { useState } from 'react';
import type { ComponentStory, ComponentMeta } from '@storybook/react';
import { Toast } from './toast';
import { Button } from '../button';
import { Intent } from '../../utils/intent';
export default {
title: 'Toast',
component: Toast,
} as ComponentMeta<typeof Toast>;
const Template: ComponentStory<typeof Toast> = (args) => {
const [open, setOpen] = useState(args.open);
return (
<div>
<Button onClick={() => setOpen(true)}>Open Toast</Button>
<Toast {...args} open={open} onOpenChange={setOpen}>
{args.children}
</Toast>
</div>
);
};
export const Default = Template.bind({});
Default.args = {
open: false,
title: 'No intent supplied',
children: <p>Some content</p>,
};
export const Primary = Template.bind({});
Primary.args = {
open: false,
title: 'Intent: Primary',
children: <p>Some content</p>,
intent: Intent.Primary,
};
export const Danger = Template.bind({});
Danger.args = {
open: false,
title: 'Intent: Danger',
children: <p>Some content</p>,
intent: Intent.Danger,
};
export const Warning = Template.bind({});
Warning.args = {
open: false,
title: 'Intent: Warning',
children: <p>Some content</p>,
intent: Intent.Warning,
};
export const Success = Template.bind({});
Success.args = {
open: false,
title: 'Intent: Success',
children: <p>Some content</p>,
intent: Intent.Success,
};
@@ -0,0 +1,74 @@
import * as ToastPrimitive from '@radix-ui/react-toast';
import type { Intent } from '@vegaprotocol/ui-toolkit';
import { getIntentBorder, getIntentShadow } from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import type { ReactNode } from 'react';
import { useEffect, useRef } from 'react';
export interface ToastProps extends ToastPrimitive.ToastProps {
children?: ReactNode;
actions?: ReactNode;
altText?: string;
icon?: ReactNode;
intent: Intent;
}
export const Toast = ({
title,
actions,
intent,
children,
icon,
altText = 'toast',
...props
}: ToastProps) => {
const contentClasses = classNames(
'relative bg-white dark:bg-black px-28 py-24 max-w-[390px]',
// // Need to apply background and text colors again as content is rendered in a portal
// 'dark:bg-black dark:text-white-95 bg-white text-black-95',
getIntentShadow(intent),
getIntentBorder(intent)
);
const timerRef = useRef(0);
useEffect(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => clearTimeout(timerRef.current);
}, []);
return (
<ToastPrimitive.ToastProvider swipeDirection="right">
<ToastPrimitive.Root duration={100000} className="grid" {...props}>
<ToastPrimitive.Description className={contentClasses}>
<div className="flex gap-12 max-w-full">
{icon && <div className="pt-8 fill-current">{icon}</div>}
<div data-testid="toast-content" className="flex-1">
{title && (
<ToastPrimitive.Title
className={`text-ui font-bold text-black-95 dark:text-white-95 mt-0 mb-2`}
data-testid="toast-title"
>
{title}
</ToastPrimitive.Title>
)}
<div className="text-black-60 dark:text-white-60">{children}</div>
</div>
</div>
<ToastPrimitive.Close
className="p-2 absolute top-8 right-8 leading-[0] focus:outline-none focus-visible:outline-none focus-visible:border focus-visible:border-vega-yellow focus-visible:top-[7px] focus-visible:right-[7px]"
data-testid="toast-close"
aria-label="Close"
>
<span aria-hidden>×</span>
</ToastPrimitive.Close>
</ToastPrimitive.Description>
{actions && (
<ToastPrimitive.Action asChild altText={altText}>
{actions}
</ToastPrimitive.Action>
)}
</ToastPrimitive.Root>
<ToastPrimitive.ToastViewport className="z-20 fixed top-[3rem] right-16 flex flex-col p-25 gap-10 m-0" />
</ToastPrimitive.ToastProvider>
);
};
@@ -1,6 +1,6 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/react-helpers';
import { Dialog, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import { formatLabel, t } from '@vegaprotocol/react-helpers';
import { Toast, Icon, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import type { ReactNode } from 'react';
import type { VegaTxState } from '../use-vega-transaction';
import { VegaTxStatus } from '../use-vega-transaction';
@@ -37,15 +37,15 @@ export const VegaTransactionDialog = ({
<VegaDialog transaction={transaction} />
);
return (
<Dialog
<Toast
open={isOpen}
onChange={onChange}
onOpenChange={onChange}
intent={computedIntent}
title={computedTitle}
icon={computedIcon}
>
{content}
</Dialog>
</Toast>
);
};
@@ -72,7 +72,7 @@ export const VegaDialog = ({ transaction }: VegaDialogProps) => {
if (transaction.status === VegaTxStatus.Error) {
return (
<div data-testid={transaction.status}>
<p>{transaction.error}</p>
<p>{transaction.error && formatLabel(transaction.error)}</p>
</div>
);
}
+1
View File
@@ -30,6 +30,7 @@
"@radix-ui/react-select": "^0.1.1",
"@radix-ui/react-slider": "^1.0.0",
"@radix-ui/react-tabs": "^0.1.5",
"@radix-ui/react-toast": "^1.0.0",
"@radix-ui/react-tooltip": "^0.1.7",
"@sentry/nextjs": "^6.19.3",
"@sentry/react": "^6.19.2",
+27
View File
@@ -3791,6 +3791,25 @@
"@radix-ui/react-roving-focus" "0.1.5"
"@radix-ui/react-use-controllable-state" "0.1.0"
"@radix-ui/react-toast@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.0.0.tgz#0d808e7f13376ddc74c0dbb7a4ff1c0602778ae2"
integrity sha512-mdoF6rahgushdev0OX+9a7JKoH0xZAZBo2Ktf/s779S7EnkZeL3/MFiRIV5LpRP5CtASmfdSD3FLnEvG1RHRtQ==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/primitive" "1.0.0"
"@radix-ui/react-collection" "1.0.0"
"@radix-ui/react-compose-refs" "1.0.0"
"@radix-ui/react-context" "1.0.0"
"@radix-ui/react-dismissable-layer" "1.0.0"
"@radix-ui/react-portal" "1.0.0"
"@radix-ui/react-presence" "1.0.0"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/react-use-callback-ref" "1.0.0"
"@radix-ui/react-use-controllable-state" "1.0.0"
"@radix-ui/react-use-layout-effect" "1.0.0"
"@radix-ui/react-visually-hidden" "1.0.0"
"@radix-ui/react-tooltip@^0.1.7":
version "0.1.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-0.1.7.tgz#6f8c00d6e489565d14abf209ce0fb8853c8c8ee3"
@@ -3940,6 +3959,14 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "0.1.4"
"@radix-ui/react-visually-hidden@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.0.tgz#4d69d7e3b6d21ee4678ed6de5215dcd068394401"
integrity sha512-MwAhMdX+n6S4InwRKSnpUsp+lLkYG6izQF56ul6guSX2mBBLOMV9Frx7xJlkEe2GjKLzbNuHhaCS6e5gopmZNA==
dependencies:
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.0"
"@radix-ui/rect@0.1.1":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-0.1.1.tgz#95b5ba51f469bea6b1b841e2d427e17e37d38419"