This is great to track when some UI component needs to react to events outsdie its component heirachy.
Consider a Drawer
that can be closed when the user clicks "outside" the Drawer
. Another good case is using a Menu
whose dropdown should close when the user clicks "away".
Traditionally we would inspect the click event
to determine if the traget element is within the DOM heirachy. This problem breaks down in the following example.
When we place a SingleSelect
or any Menu
in the Drawer
we can no longer rely on the DOM heirachy to tell us what is "inside" the Drawer
.
Determining "insideness" when using the Menu
is dificult beacuse it "portals" its dropdown to a DOM element outside React's component heirachy (a sibling element to the body
).
If we only use the DOM heirachy then the dropdown will not be considered a child of the Drawer
.
{() => {
class StateManager extends React.Component {
constructor(props, context) {
super(props, context)
this.state = {
isOpen: false,
selectedItem: null,
items: [
{ id: 1, value: 'First Option' },
{ id: 2, value: 'Second Option' },
],
}
this.handleClickOutside = this.handleClickOutside.bind(this)
this.openDrawer = this.openDrawer.bind(this)
this.handleSelect = this.handleSelect.bind(this)
}
handleClickOutside() {
this.setState({ isOpen: false })
}
openDrawer() {
this.setState({
isOpen: true,
})
}
handleSelect(selectedItemId) {
const selectedItem = this.state.items.find(
item => item.id === selectedItemId,
)
this.setState({ selectedItem })
}
render() {
const renderAssetTypeOptions = ({ getItemProps }) => {
const group = this.state.items.map((item, index) => {
const itemProps = getItemProps({ item, index })
return (
<ListItem
onClick={itemProps.onClick}
onMouseDown={itemProps.onMouseDown}
id={itemProps.id}
selected={itemProps.isSelected || itemProps.isActive}
key={index}
>
<ListItemText primary={item.value} />
)
})
return <List bordered>{group}</List>
}
const drawer = (
<Drawer
open={this.state.isOpen}
placement="right"
handleClickOutside={this.handleClickOutside}
>
<SpacedGroup direction="vertical">
<p>
This is the drawer with a portaled select. Will the selection of
the option in the portaled dropdown be considered "outside"?
</p>
<SingleSelect
fullWidth
renderOptions={renderAssetTypeOptions}
onSelect={this.handleSelect}
selectedItem={this.state.selectedItem}
/>
</SpacedGroup>
</Drawer>
)
return (
<div>
<Button onClick={this.openDrawer}>Open Drawer</Button>
{drawer}
</div>
)
}
}
return <StateManager />
}}
</Playground>