Skip to content

Commit

Permalink
Merge branch 'develop' into focus-trap-hook
Browse files Browse the repository at this point in the history
  • Loading branch information
scurker authored Dec 18, 2024
2 parents 75db09a + dc7e4ac commit 82421c5
Show file tree
Hide file tree
Showing 24 changed files with 951 additions and 191 deletions.
189 changes: 189 additions & 0 deletions docs/pages/components/AnchoredOverlay.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
title: AnchoredOverlay
description: A component that displays an anchored layered element relative to a target element.
source: https://github.com/dequelabs/cauldron/tree/develop/packages/react/src/components/AnchoredOverlay/index.tsx
---

import { useRef, useState } from 'react'
import { Select, Button, AnchoredOverlay } from '@deque/cauldron-react'
export const placements = [
'top',
'top-start',
'top-end',
'right',
'right-start',
'right-end',
'bottom',
'bottom-start',
'bottom-end',
'left',
'left-start',
'left-end',
'auto',
'auto-start',
'auto-end'
]

```jsx
import { AnchoredOverlay } from '@deque/cauldron-react'
```

Under the hood, `AnchoredOverlay` uses [floating-ui](https://floating-ui.com/) to dynamically position an overlay element relative to a target element. It is intentionally un-styled to be composed with other components, such as [Tooltip]('./Tooltip'), [Popover](./Popover), or via more complex overlay components.

<Note>
`AnchoredOverlay` is a positioning component and does not include built-in accessibility features like ARIA attributes, focus management, or keyboard interactions that would be needed for components like tooltips, dialogs, or popovers. When using `AnchoredOverlay`, you'll need to implement these accessibility patterns yourself based on your specific use case.
</Note>

## Examples

### Placement

By default, initial placement is set to `auto` when it is not set via props. However the placement can [dynamically change](https://floating-ui.com/docs/autoplacement) when using `auto` or [flip](https://floating-ui.com/docs/flip) when using positional placement.

If there are presentation elements that are dependent on the position of the `AnchoredOverlay`, you should use `onPlacementChange` to keep these presentation elements in sync with any updated placements.

```jsx example
function AnchoredOverlayExample() {
const [placement, setPlacement] = useState('top')
const [open, setOpen] = useState(false)
const targetRef = useRef()
const handlePlacementChange = ({ target }) => setPlacement(target.value);
const toggleOpen = () => setOpen(!open)
const handleClose = () => setOpen(false)

return (
<>
<Select
label="Placement"
options={placements.map(placement => ({ value: placement }))}
onChange={handlePlacementChange}
/>
<Button
ref={targetRef}
onFocus={toggleOpen}
onBlur={handleClose}
aria-describedby="anchored-overlay"
>
Anchor Element
</Button>
<AnchoredOverlay
id="anchored-overlay"
target={targetRef}
open={open}
placement={placement}
onOpenChange={openState => setOpen(openState)}
style={{
padding: 'var(--space-small)',
backgroundColor: 'var(--panel-background-color)',
display: open ? 'block' : 'none'
}}
>
Anchored Overlay Element with placement {placement}
</AnchoredOverlay>
</>
)
}
```

### Offset

Optionally, an `offset` value can be set which will offset the aligning edge of the overlay element relative to its anchor.

```jsx example
function AnchoredOverlayWithOffsetExample() {
const [placement, setPlacement] = useState('top')
const [open, setOpen] = useState(false)
const targetRef = useRef()
const handlePlacementChange = ({ target }) => setPlacement(target.value);
const toggleOpen = () => setOpen(!open)
const handleClose = () => setOpen(false)

return (
<>
<Select
label="Placement"
options={placements.map(placement => ({ value: placement }))}
onChange={handlePlacementChange}
/>
<Button
ref={targetRef}
onFocus={toggleOpen}
onBlur={handleClose}
aria-describedby="anchored-overlay-offset"
>
Anchor Element
</Button>
<AnchoredOverlay
id="anchored-overlay-offset"
target={targetRef}
open={open}
placement={placement}
onOpenChange={openState => setOpen(openState)}
offset={20}
style={{
padding: 'var(--space-small)',
backgroundColor: 'var(--panel-background-color)',
display: open ? 'block' : 'none'
}}
>
Anchored Overlay Element with offset placement {placement}
</AnchoredOverlay>
</>
)
}
```

## Props

<ComponentProps
children={true}
className={true}
refType="HTMLElement"
props={[
{
name: 'target',
type: ['HTMLElement', 'React.MutableRefObject<HTMLElement>', 'React.RefObject<HTMLElement>'],
required: true,
description: 'A target element or ref to attach the overlay anchor element.'
},
{
name: 'placement',
type: 'string',
defaultValue: 'auto',
description: 'Positional placement value to anchor the overlay element relative to its anchored target.'
},
{
name: 'open',
type: 'boolean',
defaultValue: 'false',
description: 'Determines if the overlay anchor is currently visible.'
},
{
name: 'onOpenChange',
type: '(open: boolean) => void',
description: 'A callback function that is called when the overlay state changes.'
},
{
name: 'onPlacementChange',
type: '(placement: Placement) => void',
description: 'A callback function that is called when the placement of the overlay changes.'
},
{
name: 'offset',
type: 'number',
description: 'An optional offset number to position the anchor element from its anchored target.'
},
{
name: 'as',
type: 'React.ElementType',
defaultValue: 'div',
description: 'The element type to render as.'
}
]}
/>

## Related Components

- [Tooltip](./Tooltip)
- [Popover](./Popover)

26 changes: 25 additions & 1 deletion docs/pages/components/Listbox.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ function ControlledListboxExample() {
<Listbox
aria-labelledby="listbox-controlled-example"
value={value}
onSelect={handleSelect}
onSelectionChange={handleSelect}
>
<ListboxOption>One</ListboxOption>
<ListboxOption>Two</ListboxOption>
Expand All @@ -128,6 +128,25 @@ Uncontrolled listboxes will automatically set `aria-selected="true"` for the sel
</>
```

### Multiselect

Listboxes can also support multiple selection of listbox options.

```jsx example
<>
<div id="listbox-multiselect-example">Multiselect Listbox</div>
<Listbox aria-labelledby="listbox-multiselect-example" multiselect>
<ListboxOption>One</ListboxOption>
<ListboxOption>Two</ListboxOption>
<ListboxOption>Three</ListboxOption>
</Listbox>
</>
```

<Note>
Multiselect Listbox components will pass in array values for the selected options in `onSelectionChange` and expect an array of values for `value` and `defaultValue` props.
</Note>

## Props

### Listbox
Expand Down Expand Up @@ -180,6 +199,11 @@ Uncontrolled listboxes will automatically set `aria-selected="true"` for the sel
type: 'boolean',
description: 'When set, sets the listbox option as "aria-disabled="true" and removes the element from key navigation.'
},
{
name: 'selected',
type: 'boolean',
description: 'When set, sets the listbox option as "aria-selected="true".'
},
{
name: 'activeClass',
type: 'string',
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/components/Popover.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Popover, Button } from '@deque/cauldron-react'

## Examples

Cauldron's tooltip relies on [Popper](https://popper.js.org/) to position tooltips dynamically. Popover can be triggered from any focusable element via a `target` attribute pointed to an HTMLElement or React ref object.
Cauldron's tooltip relies on [Floating UI](https://floating-ui.com/) to position tooltips dynamically. Popover can be triggered from any focusable element via a `target` attribute pointed to an HTMLElement or React ref object.

### Prompt Popover

Expand Down
4 changes: 2 additions & 2 deletions docs/pages/components/Table.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -336,15 +336,15 @@ function SortableTableExample() {

### Grid Layout

The Table component supports an optional css grid layout that can specify column alignment and width definitions per column.
The Table component supports an optional css grid layout that can specify column alignment and width and max-width definitions per column.

```jsx example
<Table
layout="grid"
columns={[
{ width: 'max-content', align: 'start' },
{ width: 'max-content', align: 'start' },
{ width: '1fr', align: 'end' }
{ width: 'auto', maxWidth: '250', align: 'end' }
]}>
<TableHead>
<TableRow>
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/components/Tooltip.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Tooltip } from '@deque/cauldron-react'

## Examples

Cauldron's tooltip relies on [Popper](https://popper.js.org/) to position tooltips dynamically. Tooltips can be triggered from any focusable element via a `target` attribute pointed to an HTMLElement or React ref object.
Cauldron's tooltip relies on [Floating UI](https://floating-ui.com/) to position tooltips dynamically. Tooltips can be triggered from any focusable element via a `target` attribute pointed to an HTMLElement or React ref object.

<Note>

Expand Down
3 changes: 1 addition & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,11 @@
"test": "jest --maxWorkers=1 --coverage"
},
"dependencies": {
"@popperjs/core": "^2.5.4",
"@floating-ui/react-dom": "^2.1.2",
"classnames": "^2.2.6",
"focusable": "^2.3.0",
"keyname": "^0.1.0",
"react-id-generator": "^3.0.1",
"react-popper": "^2.2.4",
"react-syntax-highlighter": "^15.5.0",
"tslib": "^2.4.0"
},
Expand Down
Loading

0 comments on commit 82421c5

Please sign in to comment.