Skip to content

Commit

Permalink
Upgrade from popperjs to floating-ui
Browse files Browse the repository at this point in the history
Our version of popperjs is obsolete and (I believe) no longer maintained. It
was renamed to floating-ui in a major version release a long time ago, and
unfortunately came with a host of API changes. And unfortunately for us, it's
now written in TypeScript instead of Flow, and does not provide any Flow types.

I had to convert a subset of their TS types by hand in
flow-typed/npm/@floating-ui/react_v0.26.x.js.

One upside is that the new version has a much larger scope and allows us to
remove a lot of code; the size of this diff is misleading, since most of the
additions are from the ported Flow types rather than actual executable code.

Here are a few notable instances where I was able to delete code:

 * Our `useReturnFocus` hook was no longer needed, as this functionality
   is now built-in.

 * Likewise for `useOutsideClickEffect`, although it's still needed by
   our `Autocomplete2` component.

 * Much of the code in Dialog.js and focusManagement.js was redundant.
   I removed the former entirely, as it was no longer necessary to share
   any code between the `ButtonPopover` and `Modal` components. Relatedly,
   I removed the `Popover` component because it wasn't used outside of
   `ButtonPopver`.

Things should mostly look and work the same, though there is some visual
difference in how our modals are displayed: they fill up the whole screen
vertically now (see the "Add a new $entity" dialogs for reference, which is
currently the only use of Modals). The popover arrows are also now rendered as
SVGs instead of with CSS.
  • Loading branch information
mwiencek authored and reosarevok committed Mar 25, 2024
1 parent da07f36 commit 0a3c6cd
Show file tree
Hide file tree
Showing 27 changed files with 619 additions and 614 deletions.
299 changes: 299 additions & 0 deletions flow-typed/npm/@floating-ui/react_v0.26.x.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
/*
* @flow strict
*
* MIT License
*
* Copyright (C) 2021 Floating UI contributors
* Copyright (C) 2024 MetaBrainz Foundation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/

declare module '@floating-ui/react' {
/*
* Note: These types are incomplete, and mainly express the parts of the
* API we actually use.
*/

// https://github.com/floating-ui/floating-ui/blob/672e458/packages/utils/src/index.ts
declare export type Alignment = 'start' | 'end';
declare export type AlignedPlacement =
| 'top-start' | 'top-end'
| 'right-start' | 'right-end'
| 'bottom-start' | 'bottom-end'
| 'left-start' | 'left-end';
declare export type Axis = 'x' | 'y';
declare export type Length = 'width' | 'height';
declare export type Placement = Side | AlignedPlacement;
declare export type Side = 'top' | 'right' | 'bottom' | 'left';
declare export type Strategy = 'absolute' | 'fixed';

// eslint-disable-next-line no-unused-vars
declare export type Coords = {+[key in Axis]: number};
// eslint-disable-next-line no-unused-vars
declare export type Dimensions = {+[key in Length]: number};
// eslint-disable-next-line no-unused-vars
declare export type SideObject = {+[key in Side]: number};

declare export type ClientRectObject = Rect & SideObject;
declare export type Padding = number | Partial<SideObject>;
declare export type Rect = Coords & Dimensions;

// https://floating-ui.com/docs/detectoverflow#boundary
declare export type Boundary =
| 'clippingAncestors'
| Element
| $ReadOnlyArray<Element>
| Rect;
declare export type ElementContext = 'reference' | 'floating';
declare export type RootBoundary = 'viewport' | 'document' | Rect;

declare export interface VirtualElement {
+contextElement?: Element,
getBoundingClientRect(): ClientRectObject,
}

declare export type ReferenceElement = Element | VirtualElement;
declare export type FloatingElement = HTMLElement;

declare export interface Elements {
+floating: FloatingElement,
+reference: ReferenceElement,
}

declare export type MiddlewareState = $ReadOnly<{
...Coords,
+elements: Elements,
+initialPlacement: Placement,
+placement: Placement,
+strategy: Strategy,
...
}>;

declare export type Derivable<T> = (state: MiddlewareState) => T;

declare export type MiddlewareReturn = $ReadOnly<{
...Partial<Coords>,
+data?: {+[key: string]: mixed},
}>;

declare export interface Middleware {
+fn: (state: MiddlewareState) =>
| Promise<MiddlewareReturn>
| MiddlewareReturn,
+name: string,
+options?: mixed,
}

declare export type DetectOverflowOptions = Partial<{
+altBoundary: boolean,
+boundary: Boundary,
+elementContext: ElementContext,
+padding: Padding,
+rootBoundary: RootBoundary,
}>;

declare export type ElementProps = {...};

declare export type FloatingContext = {...};

/*
* arrow()
*/
declare export type ArrowOptions = {
+element:
| Element
| null
| {current: Element | null},
+padding?: Padding,
};

declare export function arrow(
options?: ArrowOptions,
): Middleware;

/*
* autoPlacement()
*/
declare export function autoPlacement(): Middleware;

/*
* shift()
*/
declare export function shift(): Middleware;

/*
* useClick()
*/
declare export function useClick(
context: FloatingContext,
): ElementProps;

/*
* useDismiss()
*/
declare export interface UseDismissProps {
outsidePress?: boolean | ((event: MouseEvent) => boolean),
outsidePressEvent?: 'pointerdown' | 'mousedown' | 'click',
}

declare export function useDismiss(
context: FloatingContext,
props: UseDismissProps,
): ElementProps;

/*
* useFloating()
*/
declare export interface UseFloatingOptions {
middleware?: $ReadOnlyArray<?Middleware | false>,
nodeId?: string,
onOpenChange?: (
open: boolean,
event: Event,
reason: string,
) => void,
open?: boolean,
strategy?: 'fixed' | 'absolute',
}

declare export interface UseFloatingReturn {
context: FloatingContext,
floatingStyles: {...},
refs: {
floating: {current: HTMLElement | null},
reference: {current: ReferenceElement | null},
setFloating(node: HTMLElement | null): void,
setReference(node: ReferenceElement | null): void,
},
}

declare export function useFloating(
options?: UseFloatingOptions,
): UseFloatingReturn;

/*
* useFloatingNodeId()
*/
declare export function useFloatingNodeId(customParentId?: string): string;

/*
* useFloatingParentNodeId()
*/
declare export function useFloatingParentNodeId(): string | null;

/*
* useInteractions()
*/
declare type UseInteractionsProps = {
+[prop: string]: empty,
};

declare export type UseInteractionsReturn = {
+getFloatingProps: (
userProps?: UseInteractionsProps,
) => UseInteractionsProps,
+getReferenceProps: (
userProps?: UseInteractionsProps,
) => UseInteractionsProps,
...
};

declare export function useInteractions(
propsList: $ReadOnlyArray<ElementProps | void>,
): UseInteractionsReturn;

/*
* FloatingArrow
*/
declare export type FloatingArrowProps = {
+context: FloatingContext,
+d?: string,
+fill?: string,
+height?: number,
+staticOffset?: string | number | null,
+stroke?: string,
+strokeWidth?: number,
+tipRadius?: number,
+width?: number,
};

declare export const FloatingArrow:
React$AbstractComponent<FloatingArrowProps, Element>;

/*
* FloatingFocusManager
*/
declare export type FloatingFocusManagerProps = {
+children: React$Node,
+closeOnFocusOut?: boolean,
+context: FloatingContext,
+initialFocus?: number | {current: HTMLElement | null},
+modal?: boolean,
+returnFocus?: boolean,
};

declare export const FloatingFocusManager:
React$AbstractComponent<FloatingFocusManagerProps>;

/*
* FloatingNode
*/
declare export type FloatingNodeProps = {
+children: React$Node,
+id?: string,
};

declare export const FloatingNode:
React$AbstractComponent<FloatingNodeProps>;

/*
* FloatingOverlay
*/
declare export type FloatingOverlayProps = {
+children: React$Node,
+className?: string,
+lockScroll?: boolean,
+onClick?: (SyntheticMouseEvent<HTMLDivElement>) => mixed,
};

declare export const FloatingOverlay:
React$AbstractComponent<FloatingOverlayProps>;

/*
* FloatingPortal
*/
declare export type FloatingPortalProps = {
+children: React$Node,
+id?: string,
};

declare export const FloatingPortal:
React$AbstractComponent<FloatingPortalProps>;

/*
* FloatingTree
*/
declare export type FloatingTreeProps = {
+children: React$Node,
};

declare export const FloatingTree:
React$AbstractComponent<FloatingTreeProps>;
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@babel/preset-flow": "7.24.1",
"@babel/register": "7.23.7",
"@babel/runtime": "7.24.1",
"@popperjs/core": "2.5.3",
"@floating-ui/react": "0.26.9",
"@sentry/browser": "5.10.2",
"@sentry/node": "5.10.2",
"babel-loader": "9.1.3",
Expand Down
3 changes: 0 additions & 3 deletions root/forms/dialog.tt
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@

<script>
$(document)
.on("click", ":button", function () {
containingDialog.current.adjustDialogSize(window);
})
.on("keydown", function (event) {
// $.ui can't esc-close a dialog if focus is within an iframe,
// so we handle it ourselves.
Expand Down
1 change: 1 addition & 0 deletions root/static/scripts/common/components/Autocomplete2.js
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ const Autocomplete2 = (React.memo(<T: EntityItemT>(
stopRequests();
if (isOpen) {
event.preventDefault();
event.stopPropagation();
dispatch(HIDE_MENU);
}
break;
Expand Down

0 comments on commit 0a3c6cd

Please sign in to comment.