diff --git a/.changeset/few-worms-hang.md b/.changeset/few-worms-hang.md new file mode 100644 index 00000000000..9928bce2a3e --- /dev/null +++ b/.changeset/few-worms-hang.md @@ -0,0 +1,5 @@ +--- +'@kadena/react-ui': minor +--- + +Imporve `Dialog` and `Modal` components diff --git a/packages/libs/react-ui/package.json b/packages/libs/react-ui/package.json index 323e78b5c22..3c6db7304e8 100644 --- a/packages/libs/react-ui/package.json +++ b/packages/libs/react-ui/package.json @@ -43,6 +43,7 @@ }, "dependencies": { "@kadena/fonts": "~0.0.1", + "@react-aria/utils": "^3.21.1", "@vanilla-extract/css": "1.13.0", "@vanilla-extract/recipes": "0.4.0", "@vanilla-extract/sprinkles": "1.6.1", @@ -76,6 +77,7 @@ "@storybook/react-webpack5": "^7.4.0", "@storybook/theming": "^7.4.0", "@testing-library/react": "~14.0.0", + "@testing-library/user-event": "~14.5.1", "@types/lodash.mapvalues": "^4.6.7", "@types/lodash.omit": "^4.5.7", "@types/node": "^18.17.14", @@ -84,6 +86,7 @@ "@vanilla-extract/vite-plugin": "^3.9.0", "@vanilla-extract/webpack-plugin": "2.2.0", "@vitest/coverage-v8": "^0.34.6", + "@vitest/ui": "^0.34.6", "babel-plugin-module-resolver": "^5.0.0", "chromatic": "6.20.0", "copyfiles": "2.4.1", diff --git a/packages/libs/react-ui/src/components/Button/Button.tsx b/packages/libs/react-ui/src/components/Button/Button.tsx index 3465900b5a4..cd2e71ff8dd 100644 --- a/packages/libs/react-ui/src/components/Button/Button.tsx +++ b/packages/libs/react-ui/src/components/Button/Button.tsx @@ -2,7 +2,6 @@ import { SystemIcon } from '@components/Icon'; import cn from 'classnames'; import type { ButtonHTMLAttributes, FC, ReactNode } from 'react'; import React from 'react'; -import type { PressEvent } from 'react-aria'; import type { colorVariants, typeVariants } from './Button.css'; import { activeClass, diff --git a/packages/libs/react-ui/src/components/Dialog/Dialog.css.ts b/packages/libs/react-ui/src/components/Dialog/Dialog.css.ts new file mode 100644 index 00000000000..3e2a9084e0c --- /dev/null +++ b/packages/libs/react-ui/src/components/Dialog/Dialog.css.ts @@ -0,0 +1,53 @@ +import { sprinkles } from '@theme/sprinkles.css'; +import { responsiveStyle } from '@theme/themeUtils'; +import { vars } from '@theme/vars.css'; +import { style } from '@vanilla-extract/css'; + +export const openModal = style([ + { + height: '100vh', + overflowY: 'hidden', + }, +]); + +export const overlayClass = style([ + sprinkles({ + position: 'relative', + pointerEvents: 'initial', + overflow: 'auto', + width: '100%', + }), + responsiveStyle({ + xs: { + maxHeight: '100svh', + maxWidth: '100vw', + inset: 0, + }, + md: { + maxWidth: vars.sizes.$maxContent, + maxHeight: '75vh', + }, + }), +]); + +export const closeButtonClass = style([ + sprinkles({ + position: 'absolute', + top: '$md', + right: '$md', + display: 'flex', + alignItems: 'center', + background: 'none', + border: 'none', + padding: '$sm', + cursor: 'pointer', + color: 'inherit', + }), +]); + +export const titleWrapperClass = style([ + sprinkles({ + marginBottom: '$4', + marginRight: '$20', + }), +]); diff --git a/packages/libs/react-ui/src/components/Dialog/Dialog.stories.tsx b/packages/libs/react-ui/src/components/Dialog/Dialog.stories.tsx new file mode 100644 index 00000000000..1c9cd917a19 --- /dev/null +++ b/packages/libs/react-ui/src/components/Dialog/Dialog.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React, { useState } from 'react'; +import { Button } from '../Button'; +import type { IModalProps } from '../Modal'; +import { Text } from '../Typography'; +import { Dialog } from './Dialog'; + +const meta: Meta<{ title?: string } & IModalProps> = { + title: 'Overlays/Dialog', + parameters: { + docs: { + description: { + component: ` +A Dialog is a type of modal that is used to display information or prompt the user for input. It is a blocking modal, which means it will trap focus within itself and will not allow the user to interact with the rest of the page until it is closed. It is also dismissable, which means it can be closed by clicking on the close button or pressing the escape key. Dialogs are used for important information that requires the user to take action before continuing. +`, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +export const DialogStory: Story = { + name: 'Dialog', + render: () => { + const [isOpen, setIsOpen] = useState(false); + return ( + <> + + setIsOpen(isOpen)} + title={ + <> +

Dialog Title

+

Dialog description

+ + } + > + {(state) => ( + <> + + Dessert gummies pie biscuit chocolate bar cheesecake. Toffee + chocolate bar ice cream cake jujubes pudding fruitcake marzipan. + Donut sweet oat cake dragée candy cupcake biscuit. Carrot cake + sesame snaps marzipan gummies marshmallow topping cake apple pie + pudding. Toffee sweet halvah cake liquorice chupa chups sugar + plum. Tootsie roll marshmallow gummi bears apple pie cake + jujubes pudding. Halvah apple pie tiramisu bear claw caramels + cookie dessert cotton candy. Jelly-o sweet sugar plum topping + topping jujubes powder shortbread lemon drops. Chupa chups + muffin oat cake chupa chups cookie liquorice oat cake tootsie + roll. Gingerbread dessert donut pastry muffin powder sugar plum. + Chupa chups bonbon topping jelly beans pastry. Soufflé chupa + chups wafer fruitcake lollipop apple pie bonbon tart bonbon. + + + + )} +
+ + ); + }, +}; diff --git a/packages/libs/react-ui/src/components/Dialog/Dialog.test.tsx b/packages/libs/react-ui/src/components/Dialog/Dialog.test.tsx new file mode 100644 index 00000000000..72851ed9a0a --- /dev/null +++ b/packages/libs/react-ui/src/components/Dialog/Dialog.test.tsx @@ -0,0 +1,60 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; +import { describe, expect, it } from 'vitest'; + +import { Dialog } from './Dialog'; + +describe('Modal', () => { + it('should render the provided children', () => { + render( + +
Hello, world!
+
, + ); + + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); + }); + + it('should render the provided title', () => { + render( + +
Hello, world!
+
, + ); + + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); + expect(screen.getByLabelText('Title')).toHaveAttribute('role', 'dialog'); + }); + + it('should use custom aria-label correctly', () => { + render( + +
Hello, world!
+
, + ); + expect(screen.getByLabelText('my own label')).toHaveAttribute( + 'role', + 'dialog', + ); + }); + + it('should render the dialog when defaultOpen is true', () => { + render( + +
Hello, world!
+
, + ); + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); + }); + it('should dismiss the dialog when the escape key is pressed', async () => { + render( + +
Hello, world!
+
, + ); + + await userEvent.type(document.body, '{esc}'); + expect(screen.queryByText('Hello, world!')).not.toBeInTheDocument(); + }); +}); diff --git a/packages/libs/react-ui/src/components/Dialog/Dialog.tsx b/packages/libs/react-ui/src/components/Dialog/Dialog.tsx new file mode 100644 index 00000000000..3b477a5adf1 --- /dev/null +++ b/packages/libs/react-ui/src/components/Dialog/Dialog.tsx @@ -0,0 +1,118 @@ +import { useObjectRef } from '@react-aria/utils'; +import classNames from 'classnames'; +import type { FC, ReactNode } from 'react'; +import React from 'react'; +import type { AriaDialogProps } from 'react-aria'; +import { mergeProps, useDialog } from 'react-aria'; +import type { OverlayTriggerState } from 'react-stately'; +import { useOverlayTriggerState } from 'react-stately'; +import { containerClass as cardContainerClass } from '../Card/Card.css'; +import { SystemIcon } from '../Icon'; +import type { IModalProps } from '../Modal/Modal'; +import { Modal } from '../Modal/Modal'; +import { Heading } from '../Typography'; +import { + closeButtonClass, + overlayClass, + titleWrapperClass, +} from './Dialog.css'; + +interface IBaseDialogProps + extends Omit, + AriaDialogProps { + className?: string; + title?: ReactNode; + children?: ((state: OverlayTriggerState) => ReactNode) | ReactNode; +} + +const BaseDialog = React.forwardRef( + (props, ref) => { + const { + title, + className, + children, + isDismissable = true, + state, + ...rest + } = props; + const dialogRef = useObjectRef(ref); + const { dialogProps, titleProps } = useDialog( + { + role: props.role ?? 'dialog', + ...rest, + }, + dialogRef, + ); + + return ( +
+ {isDismissable && ( + + )} + + {title && ( +
+ {typeof title === 'string' ? ( + {title} + ) : ( + title + )} +
+ )} + {typeof children === 'function' ? children(state) : children} +
+ ); + }, +); + +BaseDialog.displayName = 'BaseDialog'; + +export interface IDialogProps extends Omit { + children?: ((state: OverlayTriggerState) => ReactNode) | ReactNode; + isOpen?: boolean; + defaultOpen?: boolean; + onOpenChange?: (isOpen: boolean) => void; +} + +export const Dialog: FC = ({ + title, + className, + children, + isDismissable = true, + isKeyboardDismissDisabled, + isOpen, + defaultOpen, + onOpenChange, + ...props +}) => { + const state = useOverlayTriggerState({ + isOpen, + defaultOpen, + onOpenChange, + }); + + return ( + + + {children} + + + ); +}; diff --git a/packages/libs/react-ui/src/components/Dialog/index.ts b/packages/libs/react-ui/src/components/Dialog/index.ts new file mode 100644 index 00000000000..3961f887003 --- /dev/null +++ b/packages/libs/react-ui/src/components/Dialog/index.ts @@ -0,0 +1 @@ +export { Dialog, type IDialogProps } from './Dialog'; diff --git a/packages/libs/react-ui/src/components/Modal/Dialog.tsx b/packages/libs/react-ui/src/components/Modal/Dialog.tsx deleted file mode 100644 index a3d2288105e..00000000000 --- a/packages/libs/react-ui/src/components/Modal/Dialog.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { containerClass as cardContainerClass } from '@components/Card/Card.css'; -import { SystemIcon } from '@components/Icon'; -import { Heading } from '@components/Typography'; -import classNames from 'classnames'; -import type { FC, ReactNode } from 'react'; -import React from 'react'; -import type { AriaDialogProps } from 'react-aria'; -import { useDialog } from 'react-aria'; -import { closeButtonClass, overlayClass, titleWrapperClass } from './Modal.css'; - -interface IDialogProps extends AriaDialogProps { - title?: string; - children: ReactNode; - onClose?: () => void; -} - -export const Dialog: FC = ({ - title, - children, - onClose, - ...props -}) => { - const ref = React.useRef(null); - const { dialogProps, titleProps } = useDialog(props, ref); - - console.log({ dialogProps, titleProps }); - - return ( -
- {onClose && ( - - )} - - {title && ( -
- - {title} - -
- )} - {children} -
- ); -}; diff --git a/packages/libs/react-ui/src/components/Modal/Modal.css.ts b/packages/libs/react-ui/src/components/Modal/Modal.css.ts index 06815083ad0..768bb43c19e 100644 --- a/packages/libs/react-ui/src/components/Modal/Modal.css.ts +++ b/packages/libs/react-ui/src/components/Modal/Modal.css.ts @@ -1,75 +1,12 @@ -import { sprinkles } from '@theme/sprinkles.css'; -import { responsiveStyle } from '@theme/themeUtils'; -import { vars } from '@theme/vars.css'; import { style } from '@vanilla-extract/css'; -export const openModal = style([ - { - height: '100vh', - overflowY: 'hidden', - }, -]); - -export const underlayClass = style([ - sprinkles({ - position: 'fixed', - cursor: 'pointer', - inset: 0, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }), - { - selectors: { - '&::before': { - content: '', - position: 'absolute', - inset: 0, - backgroundColor: vars.colors.$neutral5, - opacity: 0.5, - }, - }, - }, -]); - -export const overlayClass = style([ - sprinkles({ - position: 'relative', - pointerEvents: 'initial', - overflow: 'auto', - width: '100%', - }), - responsiveStyle({ - xs: { - maxHeight: '100svh', - maxWidth: '100vw', - inset: 0, - }, - md: { - maxWidth: vars.sizes.$maxContent, - maxHeight: '75vh', - }, - }), -]); - -export const closeButtonClass = style([ - sprinkles({ - position: 'absolute', - top: '$md', - right: '$md', - display: 'flex', - alignItems: 'center', - background: 'none', - border: 'none', - padding: '$sm', - cursor: 'pointer', - color: 'inherit', - }), -]); - -export const titleWrapperClass = style([ - sprinkles({ - marginBottom: '$4', - marginRight: '$20', - }), -]); +export const underlayClass = style({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + position: 'fixed', + cursor: 'pointer', + inset: 0, + // TODO: use the correct alpha color from the new design tokens + backgroundColor: 'rgba(0, 0, 0, 0.5)', +}); diff --git a/packages/libs/react-ui/src/components/Modal/Modal.stories.tsx b/packages/libs/react-ui/src/components/Modal/Modal.stories.tsx index 2fb0a6126e2..1961e14117b 100644 --- a/packages/libs/react-ui/src/components/Modal/Modal.stories.tsx +++ b/packages/libs/react-ui/src/components/Modal/Modal.stories.tsx @@ -1,48 +1,62 @@ -import { Button } from '@components/Button'; -import type { IModalProps } from '@components/Modal'; -import { Modal } from '@components/Modal'; import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; +import { Button } from '../Button'; +import type { IModalProps } from '../Modal'; +import { Modal } from './Modal'; import { ModalContent } from './StoryComponents'; -import { useModal } from './useModal'; +import { useOverlayTrigger } from 'react-aria'; +import { useOverlayTriggerState } from 'react-stately'; const meta: Meta<{ title?: string } & IModalProps> = { - title: 'Layout/Modal', + title: 'Overlays/Modal', parameters: { docs: { description: { component: - 'The component library exposes a `ModalProvider` and `useModal` hook that can be used with an element with id "modalportal" to display content in a modal.

To render a modal you need to add `
` as the last child of the document body and wrap your content in the `ModalProvider` component. Then you can pass jsx and a title to the `renderModal` function from the `useModal` hook to render content in the modal.

See the code for this story for an example.', + 'This is a generic modal component that can be used to render any content inside a modal., if you want to render a modal with a specific layout, you can use the `Dialog` component.', }, }, }, - argTypes: { - title: { - control: { - type: 'text', - }, - description: 'Title of the modal.', - }, - }, }; export default meta; -type Story = StoryObj<{ title?: string } & IModalProps>; +type Story = StoryObj; export const Primary: Story = { name: 'Modal', - args: { - title: 'Modal Title', - }, - render: ({ title }) => { - const { triggerProps, modalProps } = useModal(); + render: () => { + const state = useOverlayTriggerState({}); + const { triggerProps, overlayProps } = useOverlayTrigger( + { type: 'dialog' }, + state, + ); return ( <> - - - + + + {(ariaModalProps, ref) => ( +
+ + +
+ )}
); diff --git a/packages/libs/react-ui/src/components/Modal/Modal.test.tsx b/packages/libs/react-ui/src/components/Modal/Modal.test.tsx new file mode 100644 index 00000000000..93b2f4fafb2 --- /dev/null +++ b/packages/libs/react-ui/src/components/Modal/Modal.test.tsx @@ -0,0 +1,89 @@ +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { FC } from 'react'; +import React from 'react'; +import { describe, expect, it, vi } from 'vitest'; + +import { useOverlayTriggerState } from 'react-stately'; +import type { IModalProps } from './Modal'; +import { Modal } from './Modal'; + +interface ITestBedProps extends Omit { + isOpen?: boolean; + defaultOpen?: boolean; + onOpenChange?: (isOpen: boolean) => void; +} +const TestBed: FC = ({ + isOpen, + defaultOpen, + onOpenChange, + children, + ...props +}) => { + const state = useOverlayTriggerState({ + isOpen, + defaultOpen, + onOpenChange, + }); + return ( + + {children} + + ); +}; +describe('Modal', () => { + it('should render the provided children', () => { + render( + +
Hello, world!
+
, + ); + + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); + }); + + it('should render the modal when defaultOpen is true', () => { + render( + +
Hello, world!
+
, + ); + expect(screen.getByText('Hello, world!')).toBeInTheDocument(); + }); + it('should dismiss the modal when the escape key is pressed', async () => { + render( + +
Hello, world!
+
, + ); + await userEvent.type(document.body, '{esc}'); + expect(screen.queryByText('Hello, world!')).not.toBeInTheDocument(); + }); + + it('should not dismiss the modal isDismissable is false', async () => { + const onClose = vi.fn(); + const TestBed2: FC = () => { + const state = useOverlayTriggerState({ + isOpen: true, + onOpenChange: onClose, + }); + return ( + + {(props, ref) => { + return ( +
+
Hello, world!
+ +
+ ); + }} +
+ ); + }; + + render(); + + await userEvent.click(screen.getByText('Close')); + expect(onClose).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/libs/react-ui/src/components/Modal/Modal.tsx b/packages/libs/react-ui/src/components/Modal/Modal.tsx index b6980d0a074..0829f2a3093 100644 --- a/packages/libs/react-ui/src/components/Modal/Modal.tsx +++ b/packages/libs/react-ui/src/components/Modal/Modal.tsx @@ -1,65 +1,62 @@ 'use client'; -import type { FC } from 'react'; -import React from 'react'; - -import type { AriaDialogProps, AriaModalOverlayProps } from 'react-aria'; +import { mergeRefs } from '@react-aria/utils'; +import clsx from 'classnames'; +import type { FC, ReactElement, Ref } from 'react'; +import React, { cloneElement, useRef } from 'react'; +import type { AriaModalOverlayProps, ModalOverlayAria } from 'react-aria'; import { Overlay, useModalOverlay } from 'react-aria'; import type { OverlayTriggerState } from 'react-stately'; -import { Dialog } from './Dialog'; import { underlayClass } from './Modal.css'; -export interface IModalProps - extends AriaModalOverlayProps, - Omit, - AriaDialogProps { - children: React.ReactNode; - title?: string; - open?: () => void; - close?: () => void; - toggle?: () => void; +export interface IModalProps extends AriaModalOverlayProps { + className?: string; + state: OverlayTriggerState; + children: + | ReactElement + | (( + modalProps: ModalOverlayAria['modalProps'], + ref: Ref, + ) => ReactElement); } export const Modal: FC = ({ - title, + className, children, - isOpen, - setOpen, + state, isDismissable = true, - isKeyboardDismissDisabled = false, - open = () => setOpen(true), - close = () => setOpen(false), - toggle = () => setOpen(!isOpen), - ...dialogProps + isKeyboardDismissDisabled, }) => { - const state = { - isOpen, - setOpen, - open, - close, - toggle, - }; - const modalRef = React.useRef(null); + const nodeRef = useRef(null); + const underlayRef = useRef(null); const { modalProps, underlayProps } = useModalOverlay( - { isDismissable, isKeyboardDismissDisabled }, + { + isDismissable, + isKeyboardDismissDisabled, + }, state, - modalRef, + nodeRef, ); - if (!state.isOpen) return null; + if (!state.isOpen) { + return null; + } return ( -
-
- - {children} - -
+
+ {typeof children === 'function' + ? children(modalProps, nodeRef) + : cloneElement(children, { + ...children.props, + ...modalProps, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ref: mergeRefs(nodeRef, (children as any).ref), + })}
); diff --git a/packages/libs/react-ui/src/components/Modal/StoryComponents.tsx b/packages/libs/react-ui/src/components/Modal/StoryComponents.tsx index f54c6642785..64e8f15d2a9 100644 --- a/packages/libs/react-ui/src/components/Modal/StoryComponents.tsx +++ b/packages/libs/react-ui/src/components/Modal/StoryComponents.tsx @@ -1,8 +1,8 @@ -import { Link } from '@components/Link'; -import { Stack } from '@components/Stack'; -import { Text } from '@components/Typography'; import type { FC } from 'react'; import React from 'react'; +import { Link } from '../Link'; +import { Stack } from '../Stack'; +import { Text } from '../Typography'; export const ModalContent: FC = () => { return ( diff --git a/packages/libs/react-ui/src/components/Modal/useModal.ts b/packages/libs/react-ui/src/components/Modal/useModal.ts deleted file mode 100644 index 2e42d118c6f..00000000000 --- a/packages/libs/react-ui/src/components/Modal/useModal.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { DOMProps } from '@react-types/shared'; - -import type { AriaButtonProps } from 'react-aria'; -import { useOverlayTrigger } from 'react-aria'; -import type { OverlayTriggerProps, OverlayTriggerState } from 'react-stately'; -import { useOverlayTriggerState } from 'react-stately'; - -interface IModalReturn { - triggerProps: Omit & { - onClick?: () => void; - }; - modalProps: OverlayTriggerState & DOMProps; -} - -export const useModal = (props?: OverlayTriggerProps): IModalReturn => { - const state = useOverlayTriggerState(props || {}); - const { triggerProps, overlayProps } = useOverlayTrigger( - { type: 'dialog' }, - state, - ); - - return { - triggerProps: { - ...triggerProps, - // NOTE: Currently we are not using react-aria for our button so we cannot use onPress - onClick: () => (state.isOpen ? state.close() : state.open()), - }, - modalProps: { ...state, ...overlayProps }, - }; -}; diff --git a/packages/libs/react-ui/src/components/Typography/Heading/Heading.tsx b/packages/libs/react-ui/src/components/Typography/Heading/Heading.tsx index 333f7ce7832..046775b39b9 100644 --- a/packages/libs/react-ui/src/components/Typography/Heading/Heading.tsx +++ b/packages/libs/react-ui/src/components/Typography/Heading/Heading.tsx @@ -1,4 +1,4 @@ -import type { FC } from 'react'; +import type { ComponentPropsWithRef, FC } from 'react'; import React from 'react'; import type { colorVariants, @@ -8,7 +8,7 @@ import type { import type { elementVariants } from './Heading.css'; import { heading } from './Heading.css'; -export interface IHeadingProps { +export interface IHeadingProps extends ComponentPropsWithRef<'h1'> { as?: keyof typeof elementVariants; variant?: keyof typeof elementVariants; font?: keyof typeof fontVariants; @@ -26,6 +26,7 @@ export const Heading: FC = ({ color = 'emphasize', transform = 'none', children, + ...props }) => { const classList = heading({ variant, @@ -37,17 +38,41 @@ export const Heading: FC = ({ switch (as) { case 'h2': - return

{children}

; + return ( +

+ {children} +

+ ); case 'h3': - return

{children}

; + return ( +

+ {children} +

+ ); case 'h4': - return

{children}

; + return ( +

+ {children} +

+ ); case 'h5': - return
{children}
; + return ( +
+ {children} +
+ ); case 'h6': - return
{children}
; + return ( +
+ {children} +
+ ); case 'h1': default: - return

{children}

; + return ( +

+ {children} +

+ ); } }; diff --git a/packages/libs/react-ui/src/components/index.ts b/packages/libs/react-ui/src/components/index.ts index f65c5b9f1d6..e3cec1dc125 100644 --- a/packages/libs/react-ui/src/components/index.ts +++ b/packages/libs/react-ui/src/components/index.ts @@ -79,6 +79,7 @@ export { Breadcrumbs } from './Breadcrumbs'; export { Button } from './Button'; export { Card } from './Card'; export { ContentHeader } from './ContentHeader'; +export { Dialog, type IDialogProps } from './Dialog'; export { Divider } from './Divider/Divider'; export { Input, diff --git a/packages/libs/react-ui/tsconfig.json b/packages/libs/react-ui/tsconfig.json index 8bab327b9f9..294187aa6aa 100644 --- a/packages/libs/react-ui/tsconfig.json +++ b/packages/libs/react-ui/tsconfig.json @@ -16,6 +16,6 @@ "@utils/*": ["src/utils/*"] } }, - "include": ["./src", "./src/**/*"], + "include": ["./src"], "exclude": ["node_modules"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5f47b5eddf..1840d4cbe7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -793,7 +793,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/chainweb-node-client: dependencies: @@ -848,7 +848,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/chainweb-stream-client: dependencies: @@ -897,7 +897,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/chainwebjs: dependencies: @@ -955,7 +955,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/client: dependencies: @@ -1031,7 +1031,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/client-examples: dependencies: @@ -1159,7 +1159,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/cryptography-utils: dependencies: @@ -1211,7 +1211,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/kadena.js: dependencies: @@ -1226,7 +1226,7 @@ importers: version: link:../pactjs vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) webpack: specifier: ~5.88.2 version: 5.88.2(webpack-cli@4.9.2) @@ -1315,7 +1315,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/pactjs-generator: dependencies: @@ -1379,7 +1379,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/libs/react-components: dependencies: @@ -1465,6 +1465,9 @@ importers: '@kadena/fonts': specifier: ~0.0.1 version: 0.0.1 + '@react-aria/utils': + specifier: ^3.21.1 + version: 3.21.1(react@18.2.0) '@vanilla-extract/css': specifier: 1.13.0 version: 1.13.0 @@ -1559,6 +1562,9 @@ importers: '@testing-library/react': specifier: ~14.0.0 version: 14.0.0(react-dom@18.2.0)(react@18.2.0) + '@testing-library/user-event': + specifier: ~14.5.1 + version: 14.5.1(@testing-library/dom@9.3.1) '@types/lodash.mapvalues': specifier: ^4.6.7 version: 4.6.7 @@ -1583,6 +1589,9 @@ importers: '@vitest/coverage-v8': specifier: ^0.34.6 version: 0.34.6(vitest@0.34.6) + '@vitest/ui': + specifier: ^0.34.6 + version: 0.34.6(vitest@0.34.6) babel-plugin-module-resolver: specifier: ^5.0.0 version: 5.0.0 @@ -1633,7 +1642,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) vitest-dom: specifier: ^0.1.1 version: 0.1.1(vitest@0.34.6) @@ -1898,7 +1907,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/tools/lint-package: dependencies: @@ -2081,7 +2090,7 @@ importers: version: 5.2.2 vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages/tools/scripts: devDependencies: @@ -2120,7 +2129,7 @@ importers: version: 4.2.1(typescript@5.2.2) vitest: specifier: ^0.34.6 - version: 0.34.6(happy-dom@12.9.1) + version: 0.34.6(@vitest/ui@0.34.6) packages: @@ -6625,6 +6634,9 @@ packages: - domexception dev: true + /@polka/url@1.0.0-next.23: + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + /@pothos/core@3.33.0(graphql@16.8.0): resolution: {integrity: sha512-qg6LMEqFwqO9c9S6X2bXBtd2fRFnVHzPtEJMkOPuPgtOkosEpd6eGvBQP5NSzewOiIF3lXIyb5X8ncOoMvlUSw==} peerDependencies: @@ -10753,6 +10765,15 @@ packages: '@testing-library/dom': 9.3.1 dev: true + /@testing-library/user-event@14.5.1(@testing-library/dom@9.3.1): + resolution: {integrity: sha512-UCcUKrUYGj7ClomOo2SpNVvx4/fkd/2BbIHDCle8A0ax+P3bU7yJwDBDrS6ZwdTMARWTGODX1hEsCcO+7beJjg==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + dependencies: + '@testing-library/dom': 9.3.1 + dev: true + /@total-typescript/ts-reset@0.5.1: resolution: {integrity: sha512-AqlrT8YA1o7Ff5wPfMOL0pvL+1X+sw60NN6CcOCqs658emD6RfiXhF7Gu9QcfKBH7ELY2nInLhKSCWVoNL70MQ==} dev: true @@ -12211,6 +12232,20 @@ packages: dependencies: tinyspy: 2.2.0 + /@vitest/ui@0.34.6(vitest@0.34.6): + resolution: {integrity: sha512-/fxnCwGC0Txmr3tF3BwAbo3v6U2SkBTGR9UB8zo0Ztlx0BTOXHucE0gDHY7SjwEktCOHatiGmli9kZD6gYSoWQ==} + peerDependencies: + vitest: '>=0.30.1 <1' + dependencies: + '@vitest/utils': 0.34.6 + fast-glob: 3.3.1 + fflate: 0.8.1 + flatted: 3.2.7 + pathe: 1.1.1 + picocolors: 1.0.0 + sirv: 2.0.3 + vitest: 0.34.6(@vitest/ui@0.34.6) + /@vitest/utils@0.34.6: resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} dependencies: @@ -14798,6 +14833,7 @@ packages: /css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true /cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} @@ -16850,6 +16886,9 @@ packages: resolution: {integrity: sha512-3yurQZ2hD9VISAhJJP9bpYFNQrHHBXE2JxxjY5aLEcDi46RmAzJE2OC9FAde0yis5ElW0jTTzs0zfg/Cca4XqQ==} dev: true + /fflate@0.8.1: + resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + /figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -17772,6 +17811,7 @@ packages: webidl-conversions: 7.0.0 whatwg-encoding: 2.0.0 whatwg-mimetype: 3.0.0 + dev: true /hard-rejection@2.1.0: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} @@ -18140,6 +18180,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 + dev: true /icss-utils@5.1.0(postcss@8.4.29): resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} @@ -21092,6 +21133,10 @@ packages: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} + /mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + /ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} dev: true @@ -24088,6 +24133,7 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: true /scheduler@0.23.0: resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==} @@ -24395,6 +24441,14 @@ packages: semver: 7.5.4 dev: true + /sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.23 + mrmime: 1.0.1 + totalist: 3.0.1 + /sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} dev: true @@ -25508,6 +25562,10 @@ packages: engines: {node: '>=0.6'} dev: true + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + /tough-cookie@4.1.3: resolution: {integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==} engines: {node: '>=6'} @@ -26688,6 +26746,71 @@ packages: vitest: 0.34.6(happy-dom@12.9.1) dev: true + /vitest@0.34.6(@vitest/ui@0.34.6): + resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.7 + '@types/chai-subset': 1.3.3 + '@types/node': 18.17.14 + '@vitest/expect': 0.34.6 + '@vitest/runner': 0.34.6 + '@vitest/snapshot': 0.34.6 + '@vitest/spy': 0.34.6 + '@vitest/ui': 0.34.6(vitest@0.34.6) + '@vitest/utils': 0.34.6 + acorn: 8.10.0 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4(supports-color@5.5.0) + local-pkg: 0.4.3 + magic-string: 0.30.4 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.4.3 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.7.0 + vite: 4.4.8(@types/node@18.17.14) + vite-node: 0.34.6(@types/node@18.17.14) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + /vitest@0.34.6(happy-dom@12.9.1): resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} engines: {node: '>=v14.18.0'} @@ -26752,6 +26875,7 @@ packages: - sugarss - supports-color - terser + dev: true /vlq@0.2.3: resolution: {integrity: sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==} @@ -26846,6 +26970,7 @@ packages: /webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + dev: true /webpack-cli@4.9.2(webpack@5.88.2): resolution: {integrity: sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==} @@ -27024,10 +27149,12 @@ packages: engines: {node: '>=12'} dependencies: iconv-lite: 0.6.3 + dev: true /whatwg-mimetype@3.0.0: resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} engines: {node: '>=12'} + dev: true /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}