Skip to content

Commit

Permalink
feat(popover): adjust styles when popover.close is used
Browse files Browse the repository at this point in the history
  • Loading branch information
Powerplex committed Jun 23, 2023
1 parent 7eb5f3e commit bd3a1fa
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 33 deletions.
23 changes: 8 additions & 15 deletions packages/components/popover/src/Popover.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,8 @@ export const Default: StoryFn = _args => {
</Popover.Trigger>
<Popover.Portal>
<Popover.Content>
{/* <header className="text-display-3">Title</header> */}
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud
exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure
dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
<header className="mb-md text-headline-2">Title</header>
<p>Are you sure you want to have that cookie now ?</p>
<Popover.Arrow />
<Popover.Close aria-label="Close the popover" />
</Popover.Content>
Expand All @@ -72,7 +67,8 @@ export const Controlled: StoryFn = () => {
</Popover.Anchor>
<Popover.Portal>
<Popover.Content onInteractOutside={() => setOpen(false)}>
Popover contents
<header className="mb-md text-headline-2">Title</header>
<p>Are you sure you want to have that cookie now ?</p>
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
Expand Down Expand Up @@ -104,7 +100,8 @@ export const Anchored: StoryFn = _args => {

<Popover.Portal>
<Popover.Content>
Popover contents
<header className="mb-md text-headline-2">Title</header>
<p>Are you sure you want to have that cookie now ?</p>
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
Expand Down Expand Up @@ -195,12 +192,8 @@ export const Positionning: StoryFn = _args => {
<Button>Trigger popover</Button>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content
side={currentSide}
align={currentAlign}
aria-label="Positionning example"
>
Popover contents
<Popover.Content side={currentSide} align={currentAlign}>
<p>Are you sure you want to have that cookie now ?</p>
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
Expand Down
2 changes: 1 addition & 1 deletion packages/components/popover/src/Popover.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ describe('Popover', () => {
// ...by default, content is rendered
expect(screen.getByText('Popover content')).toBeInTheDocument()

// When the user clicks outside of it
// When the user clicks on the close button
await user.click(screen.getByRole('button', { name: 'Close the popover' }))

// Then the content is no longer rendered
Expand Down
2 changes: 0 additions & 2 deletions packages/components/popover/src/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import { Portal } from './PopoverPortal'
import { Root, type RootProps } from './PopoverRoot'
import { Trigger } from './PopoverTrigger'

// todo: add 40px when Popover has a close button

Anchor.displayName = 'Popover.Anchor'
Arrow.displayName = 'Popover.Arrow'
Close.displayName = 'Popover.Close'
Expand Down
14 changes: 11 additions & 3 deletions packages/components/popover/src/PopoverClose.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@ import { Icon } from '@spark-ui/icon'
import { IconButton } from '@spark-ui/icon-button'
import { Close as CloseSVG } from '@spark-ui/icons/dist/icons/Close'
import { cx } from 'class-variance-authority'
import { forwardRef } from 'react'
import { forwardRef, useEffect } from 'react'

import { usePopover } from './PopoverContext'

export type CloseProps = RadixPopover.PopoverCloseProps & {
'aria-label': string
}

export const Close = forwardRef<HTMLButtonElement, CloseProps>(
({ 'aria-label': ariaLabel, className, ...rest }, ref) => {
const styles = cx('absolute top-md right-md', className)
const { setHasCloseButton } = usePopover()

useEffect(() => {
setHasCloseButton(true)

return () => setHasCloseButton(false)
})

return (
<RadixPopover.Close
data-spark-component="Popover.Close"
ref={ref}
className={styles}
className={cx('absolute top-md right-md', className)}
asChild
{...rest}
>
Expand Down
19 changes: 11 additions & 8 deletions packages/components/popover/src/PopoverContent.styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ import { cva, type VariantProps } from 'class-variance-authority'

export const styles = cva(
[
'z-popover',
'rounded-md',
'max-w-sz-384 p-lg',
'p-lg',
'bg-surface text-on-surface',
'shadow',
'focus:shadow-lg',
'pr-[40px]',
// directions styles
'will-change-[transform,opacity]',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-outline-high',
],
{
variants: {
Expand All @@ -18,14 +16,19 @@ export const styles = cva(
},
enforceBoundaries: {
true: ['max-w-[--radix-popper-available-width]'],
false: ['max-w-sz-384'],
},
/**
* When there is a close button, padding to the right side must be adjusted to avoid content overlapping with it.
*/
hasCloseButton: {
true: 'pr-[40px]',
},
// hasCloseButton: {
// true: 'pr-[40px]',
// },
},
defaultVariants: {
matchTriggerWidth: false,
enforceBoundaries: false,
hasCloseButton: false,
},
}
)
Expand Down
10 changes: 9 additions & 1 deletion packages/components/popover/src/PopoverContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as RadixPopover from '@radix-ui/react-popover'
import { forwardRef } from 'react'

import { styles, type StylesProps } from './PopoverContent.styles'
import { usePopover } from './PopoverContext'

export type ContentProps = RadixPopover.PopoverContentProps & StylesProps

Expand All @@ -27,9 +28,16 @@ export const Content = forwardRef<HTMLDivElement, ContentProps>(
},
ref
) => {
const { hasCloseButton } = usePopover()

return (
<RadixPopover.Content
className={styles({ enforceBoundaries: !!collisionBoundary, matchTriggerWidth, className })}
className={styles({
enforceBoundaries: !!collisionBoundary,
matchTriggerWidth,
hasCloseButton,
className,
})}
data-spark-component="popover-content"
ref={ref}
{...{
Expand Down
33 changes: 33 additions & 0 deletions packages/components/popover/src/PopoverContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createContext, type ReactNode, useContext, useState } from 'react'

export interface PopoverContextState {
hasCloseButton: boolean
setHasCloseButton: (value: boolean) => void
}

const PopoverContext = createContext<PopoverContextState | null>(null)

export const PopoverProvider = ({ children }: { children: ReactNode }) => {
const [hasCloseButton, setHasCloseButton] = useState(false)

return (
<PopoverContext.Provider
value={{
hasCloseButton,
setHasCloseButton,
}}
>
{children}
</PopoverContext.Provider>
)
}

export const usePopover = () => {
const context = useContext(PopoverContext)

if (!context) {
throw Error('usePopover must be used within a Popover provider')
}

return context
}
10 changes: 7 additions & 3 deletions packages/components/popover/src/PopoverRoot.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import * as RadixPopover from '@radix-ui/react-popover'

import { PopoverProvider } from './PopoverContext'

export type RootProps = RadixPopover.PopoverProps

export const Root = ({ children, modal = false, ...rest }: RootProps) => {
return (
<RadixPopover.Root data-spark-component="popover" modal={modal} {...rest}>
{children}
</RadixPopover.Root>
<PopoverProvider>
<RadixPopover.Root data-spark-component="popover" modal={modal} {...rest}>
{children}
</RadixPopover.Root>
</PopoverProvider>
)
}

Expand Down

0 comments on commit bd3a1fa

Please sign in to comment.