Skip to content

Commit

Permalink
[components] Add boundaryElement property to popover component
Browse files Browse the repository at this point in the history
  • Loading branch information
mariuslundgard authored and rexxars committed Oct 6, 2020
1 parent c3a16f2 commit 0e57923
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 20 deletions.
25 changes: 21 additions & 4 deletions packages/@sanity/components/src/menuButton/menuButton.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
/* eslint-disable react/require-default-props */

import Button from 'part:@sanity/components/buttons/default'
import {ClickOutside} from 'part:@sanity/components/click-outside'
import {Popover} from 'part:@sanity/components/popover'
import React, {useCallback} from 'react'

interface MenuButtonProps {
// @todo: typings
buttonProps?: any
boundaryElement?: HTMLElement | null
buttonProps?: any // @todo: button typings
menu?: React.ReactNode
placement?: string
open?: boolean
setOpen?: (val: boolean) => void
}

export function MenuButton(props: MenuButtonProps & React.HTMLProps<HTMLDivElement>) {
const {buttonProps, children, menu, open, placement, setOpen, ...restProps} = props
const {
boundaryElement,
buttonProps,
children,
menu,
open,
placement,
setOpen,
...restProps
} = props

const handleClickOutside = useCallback(() => {
if (!setOpen) return
setOpen(false)
Expand All @@ -28,7 +40,12 @@ export function MenuButton(props: MenuButtonProps & React.HTMLProps<HTMLDivEleme
<ClickOutside onClickOutside={handleClickOutside}>
{ref => (
<div {...restProps} ref={ref}>
<Popover content={menu} open={open} placement={placement}>
<Popover
boundaryElement={boundaryElement}
content={menu}
open={open}
placement={placement}
>
<div>
<Button {...buttonProps} onClick={handleButtonClick}>
{children}
Expand Down
25 changes: 11 additions & 14 deletions packages/@sanity/components/src/panes/DefaultPane.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class Pane extends React.PureComponent {

actionHandlers = {}

rootElement = null

scrollFrameId = null

constructor(props) {
Expand All @@ -127,24 +129,14 @@ class Pane extends React.PureComponent {
this.setState({isMenuOpen: false})
}

// Triggered by pane menu button
// handleToggleMenu = () => {
// this.setState(prevState => ({isMenuOpen: !prevState.isMenuOpen}))
// }

setContextMenuOpen = val => {
this.setState({isMenuOpen: val})
}

setInitialValueMenuOpen = val => {
this.setState({isInitialValueMenuOpen: val})
// this.setState(prevState => ({isInitialValueMenuOpen: !prevState.isInitialValueMenuOpen}))
}

// handleToggleInitialValueTemplateMenu = () => {
// this.setState(prevState => ({isInitialValueMenuOpen: !prevState.isInitialValueMenuOpen}))
// }

handleCloseTemplateMenu = () => {
this.setState({isInitialValueMenuOpen: false})
}
Expand All @@ -166,7 +158,6 @@ class Pane extends React.PureComponent {
handleMenuAction = item => {
this.setContextMenuOpen(false)

// this.handleCloseContextMenu()
if (typeof item.action === 'function') {
item.action(item.params)
return
Expand Down Expand Up @@ -246,14 +237,14 @@ class Pane extends React.PureComponent {

{action.action === 'toggleTemplateSelectionMenu' && (
<MenuButton
boundaryElement={this.rootElement}
buttonProps={{
'aria-label': 'Menu',
'aria-haspopup': 'menu',
'aria-expanded': this.state.isInitialValueMenuOpen,
'aria-controls': this.templateMenuId,
icon: Icon,
kind: 'simple',
// onClick: this.handleToggleInitialValueTemplateMenu,
padding: 'small',
selected: this.state.isInitialValueMenuOpen,
title: 'Create new document'
Expand All @@ -264,7 +255,7 @@ class Pane extends React.PureComponent {
</div>
}
open={this.state.isInitialValueMenuOpen}
placement="bottom-end"
placement="bottom"
setOpen={this.setInitialValueMenuOpen}
/>
)}
Expand Down Expand Up @@ -297,6 +288,7 @@ class Pane extends React.PureComponent {
return (
<div className={styles.headerToolContainer}>
<MenuButton
boundaryElement={this.rootElement}
buttonProps={{
'aria-label': 'Menu',
'aria-haspopup': 'menu',
Expand All @@ -320,7 +312,7 @@ class Pane extends React.PureComponent {
/>
}
open={isMenuOpen}
placement="bottom-end"
placement="bottom"
setOpen={this.setContextMenuOpen}
/>
</div>
Expand All @@ -344,6 +336,10 @@ class Pane extends React.PureComponent {
)
}

setRootElement = el => {
this.rootElement = el
}

// eslint-disable-next-line complexity
render() {
const {
Expand Down Expand Up @@ -378,6 +374,7 @@ class Pane extends React.PureComponent {
isSelected ? styles.isActive : styles.isDisabled
])}
onClick={this.handleRootClick}
ref={this.setRootElement}
>
<div className={styles.header}>
<div className={styles.headerContent}>
Expand Down
6 changes: 4 additions & 2 deletions packages/@sanity/components/src/popover/popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const applyModifer: Modifier<'applyMaxSize', {}> = {
}

interface PopoverProps {
targetElement?: HTMLElement | null
boundaryElement?: HTMLElement | null
children: JSX.Element
className?: string
content: React.ReactNode
Expand All @@ -40,11 +40,13 @@ interface PopoverProps {
| 'bottom'
| 'bottom-start'
| 'bottom-end'
targetElement?: HTMLElement | null
tone?: 'navbar'
}

export function Popover(props: PopoverProps & Omit<React.HTMLProps<HTMLDivElement>, 'children'>) {
const {
boundaryElement,
className,
content,
disabled,
Expand All @@ -70,7 +72,7 @@ export function Popover(props: PopoverProps & Omit<React.HTMLProps<HTMLDivElemen
name: 'preventOverflow',
options: {
altAxis: true,
rootBoundary: 'viewport',
boundary: boundaryElement || undefined,
padding: 8
}
},
Expand Down
68 changes: 68 additions & 0 deletions packages/@sanity/components/src/popover/stories/boundary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import {Popover} from 'part:@sanity/components/popover'
import {boolean, select, text} from 'part:@sanity/storybook/addons/knobs'
import Sanity from 'part:@sanity/storybook/addons/sanity'
import {CenteredContainer} from 'part:@sanity/storybook/components'
import React, {useRef} from 'react'

export function BoundaryStory() {
const boundary = select(
'Boundary',
{viewport: 'Viewport', wrapper: 'Wrapper'},
'viewport',
'Props'
)
const content = text('Content', 'Content', 'Props')
const open = boolean('Open', true, 'Props')
const placement = select(
'Placement',
{
'top-start': 'Top start',
top: 'Top',
'top-end': 'Top end',
'right-start': 'Right start',
right: 'Right',
'right-end': 'Right end',
'bottom-start': 'Bottom start',
bottom: 'Bottom',
'bottom-end': 'Bottom end',
'left-start': 'Left start',
left: 'Left',
'left-end': 'Left end'
},
'bottom',
'Props'
)

return (
<Sanity part="part:@sanity/components/dialogs/default" propTables={[Popover]}>
<CenteredContainer>
<PopoverExample boundary={boundary} content={content} open={open} placement={placement} />
</CenteredContainer>
</Sanity>
)
}

function PopoverExample({boundary, content, open, placement}) {
const boundaryRef = useRef(null)

return (
<div ref={boundaryRef} style={{minWidth: 200, minHeight: 200, outline: '1px solid red'}}>
<Popover
boundaryElement={boundary === 'wrapper' ? boundaryRef.current : null}
content={content ? <div style={{padding: '1em'}}>{content}</div> : null}
open={open}
placement={placement}
>
<span
style={{
display: 'inline-block',
padding: '0.25em',
background: 'rgba(127, 127, 127, 0.5)'
}}
>
Reference
</span>
</Popover>
</div>
)
}
2 changes: 2 additions & 0 deletions packages/@sanity/components/src/popover/story.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {storiesOf} from 'part:@sanity/storybook'
import {withKnobs} from 'part:@sanity/storybook/addons/knobs'
import {BoundaryStory} from './stories/boundary'
import {DefaultStory} from './stories/default'
import {MoveRefStory} from './stories/moveRef'

storiesOf('@sanity/components/popover', module)
.addDecorator(withKnobs)
.add('Default', DefaultStory)
.add('Move reference', MoveRefStory)
.add('Boundary', BoundaryStory)

0 comments on commit 0e57923

Please sign in to comment.