diff --git a/.changeset/chatty-apes-speak.md b/.changeset/chatty-apes-speak.md index 8ca39314628..5c672ec2763 100644 --- a/.changeset/chatty-apes-speak.md +++ b/.changeset/chatty-apes-speak.md @@ -11,7 +11,6 @@ Breaking changes: * HTML structure output may have changed * Some passed props from our component to reakit and not documented as a usage as been removed. If you need a different usage let us knwow, now we own the code * Tabs props API has been completly changed -* Tooltip do not use cloneElement and accept only children as function now. Components changed: * Accordion (useId) diff --git a/packages/design-system/src/components/Popover/Popover.stories.tsx b/packages/design-system/src/components/Popover/Popover.stories.tsx index a720e16c55d..fd16a80295e 100644 --- a/packages/design-system/src/components/Popover/Popover.stories.tsx +++ b/packages/design-system/src/components/Popover/Popover.stories.tsx @@ -1,7 +1,7 @@ import { forwardRef, Ref } from 'react'; import { action } from '@storybook/addon-actions'; -import { Popover, ButtonPrimary, ButtonIcon, StackVertical, Form } from '../../'; -import { DisclosureFnProps } from '../Disclosure/Disclosure'; +import { Popover, ButtonPrimary, StackVertical, Form } from '../../'; +import { PopoverTriggerProps } from './'; export default { component: Popover, @@ -10,9 +10,18 @@ export default { const EasyPopover = () => Hello hello; /* eslint-disable-next-line react/display-name */ -const OpenPopover = forwardRef((props: any, ref: Ref) => { +const OpenPopover = forwardRef(({ onClick, ...props }: any, ref: Ref) => { return ( - + { + if (onClick) { + onClick(); + } + action('Clicked disclosure'); + }} + {...props} + ref={ref} + > Open popover ); @@ -20,51 +29,41 @@ const OpenPopover = forwardRef((props: any, ref: Ref) => { export const DefaultStory = () => (
- }> - Text Content + +
); -export const DisclosureStory = () => ( +export const ChildrenAsFunctionStory = () => (
- - Open popover - - } - > - Text Content + + {(props: PopoverTriggerProps) => }
); -export const FormDisclosureStory = () => ( +export const FormFocusStory = () => (
- } - > - Text Content + +
); -export const WithoutPaddingStory = () => ( -
- } isFixed hasPadding={false}> - Text Content without padding +export const FixedStory = () => ( +
+ +
); -export const WithFunctionAsChildren = () => ( +export const WithContentAsFunction = () => (
- }> - {(popover: DisclosureFnProps) => ( + ( <> There is some content @@ -73,6 +72,8 @@ export const WithFunctionAsChildren = () => ( )} + > +
); diff --git a/packages/design-system/src/components/Popover/Popover.tsx b/packages/design-system/src/components/Popover/Popover.tsx index 470d34058a4..30f76d88a16 100644 --- a/packages/design-system/src/components/Popover/Popover.tsx +++ b/packages/design-system/src/components/Popover/Popover.tsx @@ -1,69 +1,65 @@ -import { useRef } from 'react'; -import type { ReactNode } from 'react'; +import { useRef, Fragment } from 'react'; +import type { ReactNode, MouseEvent } from 'react'; import tokens from '@talend/design-tokens'; import { Placement, FloatingArrow, FloatingPortal } from '@floating-ui/react'; import { usePopover } from './usePopover'; -import { Disclosure } from '../Disclosure/Disclosure'; import theme from './Popover.module.scss'; +import { renderOrClone, ChildrenOrFn } from '../../renderOrClone'; -interface PopoverOptions { - disclosure?: ReactNode; +type PopoverOptions = { + popup: ReactNode | ((props: any) => ReactNode); initialOpen?: boolean; placement?: Placement; modal?: boolean; open?: boolean; isFixed?: boolean; onOpenChange?: (open: boolean) => void; -} +}; + +export type PopoverProps = { + children: ChildrenOrFn; +} & PopoverOptions; export function Popover({ children, - disclosure, modal = true, isFixed = false, + popup, ...restOptions -}: { - children: ReactNode; -} & PopoverOptions) { +}: PopoverProps) { // This can accept any props as options, e.g. `placement`, // or other positioning options. const arrowRef = useRef(null); const popover = usePopover({ modal, arrowRef, ...restOptions }); - let childrenRendered = children; - if (typeof children === 'function') { - childrenRendered = children(popover); - } - let content = ( -
- - {childrenRendered} -
- ); - if (isFixed) { - content = {content}; - } - const disclosureProps = popover.getReferenceProps({ onClick: e => e.stopPropagation() }); + const Wrapper = isFixed ? FloatingPortal : Fragment; + const onClick = (e: MouseEvent) => { + e.preventDefault(); + }; + const childrenProps = popover.getReferenceProps({ onClick }); return ( <> - - {disclosure} - - {content} + {renderOrClone(children, { ...childrenProps, ref: popover.refs.setReference })} + +
+ + {typeof popup === 'function' ? popup(popover.getFloatingProps()) : popup} +
+
); } diff --git a/packages/design-system/src/components/Popover/index.ts b/packages/design-system/src/components/Popover/index.ts index e6c2c3dbb49..234ca06ee7e 100644 --- a/packages/design-system/src/components/Popover/index.ts +++ b/packages/design-system/src/components/Popover/index.ts @@ -1,3 +1,4 @@ import { Popover } from './Popover'; - +export type { PopoverProps } from './Popover'; +export type { PopoverTriggerProps } from './usePopover'; export default Popover; diff --git a/packages/design-system/src/components/Popover/usePopover.tsx b/packages/design-system/src/components/Popover/usePopover.tsx index af7e654701e..db596ea3cbf 100644 --- a/packages/design-system/src/components/Popover/usePopover.tsx +++ b/packages/design-system/src/components/Popover/usePopover.tsx @@ -17,14 +17,36 @@ import { const ARROW_HEIGHT = 7; const GAP = 2; -interface PopoverOptions { +type PopoverOptions = { initialOpen?: boolean; arrowRef?: MutableRefObject; placement?: Placement; modal?: boolean; open?: boolean; onOpenChange?: (open: boolean) => void; -} +}; + +export type PopoverTriggerProps = { + onClick?: (event: any) => void; + ref: any; +}; + +export type UsePopoverType = { + // floating-ui types + refs: any; + getFloatingProps: (props?: any) => any; + floatingStyles: any; + context: any; + getReferenceProps: (props?: any) => any; + // local state + open: boolean; + setOpen: (open: boolean) => void; + modal?: boolean; + labelId?: string; + descriptionId?: string; + setLabelId: (id: string) => void; + setDescriptionId: (id: string) => void; +}; export function usePopover({ initialOpen = false, @@ -33,7 +55,7 @@ export function usePopover({ modal, open: controlledOpen, onOpenChange: setControlledOpen, -}: PopoverOptions = {}) { +}: PopoverOptions = {}): UsePopoverType { const [uncontrolledOpen, setUncontrolledOpen] = useState(initialOpen); const [labelId, setLabelId] = useState(); const [descriptionId, setDescriptionId] = useState(); diff --git a/packages/design-system/src/components/Tooltip/Tooltip.tsx b/packages/design-system/src/components/Tooltip/Tooltip.tsx index 3652c250d55..5758163d713 100644 --- a/packages/design-system/src/components/Tooltip/Tooltip.tsx +++ b/packages/design-system/src/components/Tooltip/Tooltip.tsx @@ -20,6 +20,7 @@ import { import { useId } from '../../useId'; import styles from './Tooltip.module.scss'; +import { renderOrClone } from '../../renderOrClone'; export type Placement = | 'top-start' @@ -84,13 +85,11 @@ const Tooltip = ({ id, children, title, placement = 'top', ...rest }: TooltipPro return ( <> - {children( - { - ...getReferenceProps(), - 'aria-describedby': safeId, - }, - floating.refs.setReference, - )} + {renderOrClone(children, { + ...getReferenceProps(), + 'aria-describedby': safeId, + ref: floating.refs.setReference, + })}
ReactNode); + +export function renderOrClone(children: ChildrenOrFn, props: any = {}): ReactNode { + if (typeof children === 'function') { + return children(props); + } + return children && props ? React.cloneElement(children, props) : children; +}