Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Popover: refactor to TypeScript #43823

Merged
merged 49 commits into from
Sep 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
867c532
Rename files
ciampo Sep 2, 2022
a911e6a
First iteration of Popover props
ciampo Sep 2, 2022
ddfe57b
Add types to component and default export
ciampo Sep 2, 2022
7f4059b
Type AnimatedWrapper, refactor logic to always render a `motion.div`
ciampo Sep 2, 2022
ed5ace4
Remove unnused props from `ArrowTriangle`
ciampo Sep 2, 2022
e2f18b7
Use intermediate variable for flip/resize fallback values when `__uns…
ciampo Sep 2, 2022
742c830
Type PopoverSlot
ciampo Sep 2, 2022
0fd0d43
Type resize middleware
ciampo Sep 2, 2022
c095b2f
Type onDialogClose & related
ciampo Sep 2, 2022
b984ff7
Type middleware array
ciampo Sep 2, 2022
6263832
Split and export different anchor ref types
ciampo Sep 2, 2022
702f95c
Type ownerDocument ev listeners useEffect
ciampo Sep 2, 2022
d9cde6d
Type internal state and ref callbacks
ciampo Sep 2, 2022
9ff0b8b
Type mergedFloatingRef
ciampo Sep 2, 2022
67afd28
Fix type error in AnimatedWrapper s style
ciampo Sep 2, 2022
eccc7ca
Type arrow styles
ciampo Sep 2, 2022
0c8c527
Simplify position pro definition
ciampo Sep 2, 2022
a7d16d3
Type placement-based utils
ciampo Sep 2, 2022
39eb435
Type getFrameOffset
ciampo Sep 2, 2022
6e46741
Type getReferenceOwnerDocument
ciampo Sep 2, 2022
b2698a0
Make top and bottom props on anchorRef mandatory
ciampo Sep 2, 2022
62555b5
Type getReferenceElement
ciampo Sep 2, 2022
4b9aa59
anchorRef.startContainer is never undefined
ciampo Sep 2, 2022
3d60bf9
No need to optional-chain anchorRef.current.ownerDocument
ciampo Sep 2, 2022
79c0918
Remove TODO
ciampo Sep 2, 2022
7b82718
Fix getFrameOffset signature
ciampo Sep 2, 2022
5feac47
Simplify anchorRef types
ciampo Sep 4, 2022
0d69c6e
Add type descriptions
ciampo Sep 4, 2022
448a8b0
Type `useDialog` hook
ciampo Sep 4, 2022
248522b
Add @deprecated tag for the range prop
ciampo Sep 4, 2022
f885dd3
Type Storybook examples
ciampo Sep 4, 2022
64b218f
refactor BorderControl and BorderBoxControl
ciampo Sep 4, 2022
3102956
Add JSDoc description and example snippet to exported component
ciampo Sep 4, 2022
92c96e5
Add `placement` s default value
ciampo Sep 4, 2022
b571090
README
ciampo Sep 4, 2022
bf068e9
CHANGELOGs
ciampo Sep 5, 2022
969bd60
Refine `position` types
ciampo Sep 5, 2022
dec13b2
Type the `shift` prop, mark `__unstableShift` as deprecated
ciampo Sep 5, 2022
fabedfa
focusOnMount typo
ciampo Sep 6, 2022
031fea4
Fix onFocusOutside README type
ciampo Sep 6, 2022
bf84afb
fix getAnchorRect README type
ciampo Sep 6, 2022
6c24c7b
Fix anchorRef README type
ciampo Sep 6, 2022
8261fdf
Fix children README type
ciampo Sep 6, 2022
e4807e0
Fix isAlternate README type
ciampo Sep 6, 2022
27875f8
Remove unstable props from README, add deprecated range prop
ciampo Sep 6, 2022
5754ee1
Improve focusOnMount types on derived components
ciampo Sep 6, 2022
25d4391
Do not display controls for `children` prop
ciampo Sep 6, 2022
78a25e8
Storybook feedback
ciampo Sep 6, 2022
dac81ea
Restore they way popoverProps was passed in BorderControl
ciampo Sep 6, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
- `NavigatorButton`: updated to satisfy `react/exhaustive-deps` eslint rule ([#42051](https://github.com/WordPress/gutenberg/pull/42051))
- `TabPanel`: Refactor away from `_.partial()` ([#43895](https://github.com/WordPress/gutenberg/pull/43895/)).
- `Panel`: Refactor tests to `@testing-library/react` ([#43896](https://github.com/WordPress/gutenberg/pull/43896)).
- `Popover`: refactor to TypeScript ([#43823](https://github.com/WordPress/gutenberg/pull/43823/)).
- `BorderControl` and `BorderBoxControl`: replace temporary types with `Popover`'s types ([#43823](https://github.com/WordPress/gutenberg/pull/43823/)).

## 20.0.0 (2022-08-24)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/**
* External dependencies
*/
import type { ComponentProps } from 'react';
/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -38,7 +42,9 @@ const BorderBoxControlSplitControls = (
} = useBorderBoxControlSplitControls( props );
const containerRef = useRef();
const mergedRef = useMergeRefs( [ containerRef, forwardedRef ] );
const popoverProps = popoverPlacement
const popoverProps: ComponentProps<
typeof BorderControl
>[ '__unstablePopoverProps' ] = popoverPlacement
? {
placement: popoverPlacement,
offset: popoverOffset,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import type { ComponentProps } from 'react';

/**
* WordPress dependencies
*/
Expand Down Expand Up @@ -64,7 +69,9 @@ const BorderBoxControl = (
} = useBorderBoxControl( props );
const containerRef = useRef();
const mergedRef = useMergeRefs( [ containerRef, forwardedRef ] );
const popoverProps = popoverPlacement
const popoverProps: ComponentProps<
typeof BorderControl
>[ '__unstablePopoverProps' ] = popoverPlacement
? {
placement: popoverPlacement,
offset: popoverOffset,
Expand Down
9 changes: 5 additions & 4 deletions packages/components/src/border-box-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Internal dependencies
*/
import type { Border, ColorProps, LabelProps } from '../border-control/types';
import type { PopoverProps } from '../popover/types';

export type Borders = {
top?: Border;
Expand Down Expand Up @@ -29,11 +30,11 @@ export type BorderBoxControlProps = ColorProps &
/**
* The position of the color popovers compared to the control wrapper.
*/
popoverPlacement?: string;
popoverPlacement?: PopoverProps[ 'placement' ];
/**
* The space between the popover and the control wrapper.
*/
popoverOffset?: number;
popoverOffset?: PopoverProps[ 'offset' ];
/**
* An object representing the current border configuration.
*
Expand Down Expand Up @@ -103,11 +104,11 @@ export type SplitControlsProps = ColorProps & {
/**
* The position of the color popovers compared to the control wrapper.
*/
popoverPlacement?: string;
popoverPlacement?: PopoverProps[ 'placement' ];
/**
* The space between the popover and the control wrapper.
*/
popoverOffset?: number;
popoverOffset?: PopoverProps[ 'offset' ];
/**
* An object representing the current border configuration. It contains
* properties for each side, with each side an object reflecting the border
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,7 @@ import { useBorderControlDropdown } from './hook';
import { StyledLabel } from '../../base-control/styles/base-control-styles';
import DropdownContentWrapper from '../../dropdown/dropdown-content-wrapper';

import type {
Color,
ColorOrigin,
Colors,
DropdownProps,
PopoverProps,
} from '../types';
import type { Color, ColorOrigin, Colors, DropdownProps } from '../types';

const noop = () => undefined;
const getColorObject = (
Expand Down Expand Up @@ -188,7 +182,8 @@ const BorderControlDropdown = (
</Button>
);

const renderContent = ( { onClose }: PopoverProps ) => (
// TODO: update types once Dropdown component is refactored to TypeScript.
const renderContent = ( { onClose }: { onClose: () => void } ) => (
<>
<DropdownContentWrapper paddingSize="medium">
<VStack className={ popoverControlsClassName } spacing={ 6 }>
Expand Down
16 changes: 7 additions & 9 deletions packages/components/src/border-control/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
*/
import type { CSSProperties } from 'react';

/**
* Internal dependencies
*/
import type { PopoverProps } from '../popover/types';

export type Border = {
color?: CSSProperties[ 'borderColor' ];
style?: CSSProperties[ 'borderStyle' ];
Expand Down Expand Up @@ -83,7 +88,7 @@ export type BorderControlProps = ColorProps &
/**
* An internal prop used to control the visibility of the dropdown.
*/
__unstablePopoverProps?: Record< string, unknown >;
__unstablePopoverProps?: Omit< PopoverProps, 'children' >;
/**
* If opted into, sanitizing the border means that if no width or color
* have been selected, the border style is also cleared and `undefined`
Expand Down Expand Up @@ -132,7 +137,7 @@ export type DropdownProps = ColorProps & {
/**
* An internal prop used to control the visibility of the dropdown.
*/
__unstablePopoverProps?: Record< string, unknown >;
__unstablePopoverProps?: Omit< PopoverProps, 'children' >;
/**
* This controls whether to render border style options.
*
Expand Down Expand Up @@ -176,10 +181,3 @@ export type StylePickerProps = LabelProps & {
*/
value?: string;
};

export type PopoverProps = {
/**
* Callback function to invoke when closing the border dropdown's popover.
*/
onClose: () => void;
};
10 changes: 6 additions & 4 deletions packages/components/src/dropdown/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dropdown

Dropdown is a React component to render a button that opens a floating content modal when clicked.
Dropdown is a React component to render a button that opens a floating content modal when clicked.

This component takes care of updating the state of the dropdown menu (opened/closed), handles closing the menu when clicking outside and uses render props to render the button and the content.

Expand Down Expand Up @@ -91,11 +91,13 @@ Set this to customize the text that is shown in the dropdown's header when it is

### focusOnMount

By default, the _first tabbable element_ in the popover will receive focus when it mounts. This is the same as setting `focusOnMount` to `"firstElement"`. If you want to focus the container instead, you can set `focusOnMount` to `"container"`.
By default, the _first tabbable element_ in the popover will receive focus when it mounts. This is the same as setting this prop to `"firstElement"`.

Set this prop to `false` to disable focus switching entirely. This should only be set when an appropriately accessible substitute behavior exists.
Specifying a `true` value will focus the container instead.

- Type: `String` or `Boolean`
Specifying a `false` value disables the focus handling entirely (this should only be done when an appropriately accessible substitute behavior exists).

- Type: `'firstElement' | boolean`
- Required: No
- Default: `"firstElement"`

Expand Down
2 changes: 1 addition & 1 deletion packages/components/src/dropdown/stories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default {
focusOnMount: {
control: {
type: 'radio',
options: [ 'firstElement', 'container', false ],
options: [ 'firstElement', true, false ],
},
},
headerTitle: { control: { type: 'text' } },
Expand Down
133 changes: 70 additions & 63 deletions packages/components/src/popover/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Popover

Popover is a React component to render a floating content modal. It is similar in purpose to a tooltip, but renders content of any sort, not only simple text. It anchors itself to its parent node, optionally by a specified direction. If the popover exceeds the bounds of the page in the direction it opens, its position will be flipped automatically.
`Popover` renders its content in a floating modal. If no explicit anchor is passed via props, it anchors to its parent element by default.

The behavior of the popover when it exceeds the viewport's edges can be controlled via its props.

## Usage

Expand Down Expand Up @@ -49,130 +51,135 @@ render(

The component accepts the following props. Props not included in this set will be applied to the element wrapping Popover content.

### focusOnMount

By default, the _first tabblable element_ in the popover will receive focus when it mounts. This is the same as setting `focusOnMount` to `"firstElement"`. If you want to focus the container instead, you can set `focusOnMount` to `"container"`.
### `anchorRect`: `DomRectWithOwnerDocument`

Set this prop to `false` to disable focus changing entirely. This should only be set when an appropriately accessible substitute behavior exists.
An object extending a `DOMRect` with an additional optional `ownerDocument` property, used to specify a fixed popover position.

- Type: `String` or `Boolean`
- Required: No
- Default: `"firstElement"`

### placement
### `anchorRef`: `Element | PopoverAnchorRefReference | PopoverAnchorRefTopBottom | Range`

Used to specify a fixed popover position. It can be an `Element`, a React reference to an `element`, an object with a `top` and a `bottom` properties (both pointing to elements), or a `range`.

The direction in which the popover should open relative to its parent node or anchor node.
- Required: No

The available base placements are 'top', 'right', 'bottom', 'left'.
### `animate`: `boolean`

Each of these base placements has an alignment in the form -start and -end. For example, 'right-start', or 'bottom-end'. These allow you to align the tooltip to the edges of the button, rather than centering it.
Whether the popover should animate when opening.

- Type: `String`
- Required: No
- Default: `"bottom-start"`
- Default: `true`

### `children`: `ReactNode`

### flip
The `children` elements rendered as the popover's content.

Specifies whether the `Popover` should flip across its axis if there isn't space for it in the normal placement.
- Required: Yes

When the using a 'top' placement, the `Popover` will switch to a 'bottom' placement. When using a 'left' placement, the popover will switch to a 'right' placement.
### `expandOnMobile`: `boolean`

The `Popover` will retain its alignment of 'start' or 'end' when flipping.
Show the popover fullscreen on mobile viewports.

- Type: `Boolean`
- Required: No
- Default: `true`

### resize
### `flip`: `boolean`

Specifies whether the popover should flip across its axis if there isn't space for it in the normal placement.

When the using a 'top' placement, the popover will switch to a 'bottom' placement. When using a 'left' placement, the popover will switch to a `right' placement.

Adjusts the height of the `Popover` to prevent overflow.
The popover will retain its alignment of 'start' or 'end' when flipping.

- Type: `Boolean`
- Required: No
- Default: `true`

### shift
### `focusOnMount`: `'firstElement' | boolean`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed container as a possible option as it looks like it was removed in #27699


Enables the `Popover` to shift in order to stay in view when meeting the viewport edges.
By default, the _first tabbable element_ in the popover will receive focus when it mounts. This is the same as setting this prop to `"firstElement"`.

Specifying a `true` value will focus the container instead.

Specifying a `false` value disables the focus handling entirely (this should only be done when an appropriately accessible substitute behavior exists).

- Type: `Boolean`
- Required: No
- Default: `false`
- Default: `"firstElement"`

### `onFocusOutside`: `( event: SyntheticEvent ) => void`

### offset
A callback invoked when the focus leaves the opened popover. This should only be provided in advanced use-cases when a popover should close under specific circumstances (for example, if the new `document.activeElement` is content of or otherwise controlling popover visibility).

The distance (in pixels) between the anchor and popover.
When not provided, the `onClose` callback will be called instead.

- Type: `Number`
- Required: No

### children
### `getAnchorRect`: `( fallbackReferenceElement: Element | null ) => DomRectWithOwnerDocument`

The content to be displayed within the popover.
A function returning the same value as the one expected by the `anchorRect` prop, used to specify a dynamic popover position.

- Type: `Element`
- Required: Yes
- Required: No

### className
### `headerTitle`: `string`

An optional additional class name to apply to the rendered popover.
Used to customize the header text shown when the popover is toggled to fullscreen on mobile viewports (see the `expandOnMobile` prop).

- Type: `String`
- Required: No

### onClose
### `isAlternate`: `boolean`

A callback invoked when the popover should be closed.
Used to enable a different visual style for the popover.

- Type: `Function`
- Required: No

### onFocusOutside

A callback invoked when the focus leaves the opened popover. This should only be provided in advanced use-cases when a Popover should close under specific circumstances; for example, if the new `document.activeElement` is content of or otherwise controlling Popover visibility.
### `noArrow`: `boolean`

Defaults to `onClose` when not provided.
Used to show/hide the arrow that points at the popover's anchor.

- Type: `Function`
- Required: No
- Default: `true`

### expandOnMobile
### `offset`: `number`

Opt-in prop to show popovers fullscreen on mobile, pass `false` in this prop to avoid this behavior.
The distance (in px) between the anchor and the popover.

- Type: `Boolean`
- Required: No
- Default: `false`

### headerTitle
### `onClose`: `() => void`

Set this to customize the text that is shown in popover's header when it is fullscreen on mobile.
A callback invoked when the popover should be closed.

- Type: `String`
- Required: No

### noArrow
### `placement`: `'top' | 'top-start' | 'top-end' | 'right' | 'right-start' | 'right-end' | 'bottom' | 'bottom-start' | 'bottom-end' | 'left' | 'left-start' | 'left-end'`

Set this to hide the arrow which visually indicates what the popover is anchored to. Note that the arrow will not display if `position` is set to `"middle center"`.
Used to specify the popover's position with respect to its anchor.

- Type: `Boolean`
- Required: No
- Default: `true`
- Default: `"bottom-start"`

### anchorRect
### `position`: `[yAxis] [xAxis] [optionalCorner]`

_Note: use the `placement` prop instead when possible._

Legacy way to specify the popover's position with respect to its anchor.

Possible values:

- `yAxis`: `'top' | 'middle' | 'bottom'`
- `xAxis`: `'left' | 'center' | 'right'`
- `corner`: `'top' | 'right' | 'bottom' | 'left'`

A custom `DOMRect` object at which to position the popover. `anchorRect` is used when the position (custom `DOMRect` object) of the popover needs to be fixed at one location all the time.

- Type: `DOMRect`
- Required: No

### getAnchorRect
### `resize`: `boolean`

A callback function which is used to override the anchor value computation algorithm. `anchorRect` will take precedence over this prop, if both are passed together.
Adjusts the size of the popover to prevent its contents from going out of view when meeting the viewport edges.

- Required: No
- Default: `true`

If you need the `DOMRect` object i.e., the position of popover to be calculated on every time, the popover re-renders, then use `getAnchorRect`.
### `range`: `unknown`

`getAnchorRect` callback function receives a reference to the popover anchor element as a function parameter and it should return a `DOMRect` object. Noting that `getAnchorRect` can be called with `null`.
_Note: this prop is deprecated and has no effect on the component._

- Type: `Function`
- Required: No
Loading