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

Remove react-transition-group dependency #2391

Merged
merged 34 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d543d87
🛫
r100-stack Jan 6, 2025
182b4a8
🛫
r100-stack Jan 6, 2025
c2fe3b4
modified repo for the new year (2025)
smmr-dn Jan 6, 2025
1e43853
revert App.tsx
smmr-dn Jan 6, 2025
f2dd813
📈
r100-stack Jan 6, 2025
87a1663
Merge remote-tracking branch 'origin/uyen/modified-new-year-repo' int…
r100-stack Jan 6, 2025
2880bad
Merge branch 'r/themeprovider-children-state-loss-fix' into r/react-t…
r100-stack Jan 6, 2025
eb344b3
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 8, 2025
0454c60
Dialog unit test fixes
r100-stack Jan 9, 2025
0a4bc2e
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 9, 2025
e28fc35
Fix toast
r100-stack Jan 9, 2025
eff3128
Add JS animations
r100-stack Jan 10, 2025
22e06cd
Fix unit tests
r100-stack Jan 10, 2025
96ff004
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 21, 2025
26a1172
Comments
r100-stack Jan 21, 2025
99c9bd6
Separate hook
r100-stack Jan 21, 2025
8b891a7
Testing without mocks
r100-stack Jan 21, 2025
6942306
Cleaner custom hook
r100-stack Jan 21, 2025
76bede6
Cleanup
r100-stack Jan 21, 2025
4197180
`animate?.()`
r100-stack Jan 21, 2025
1455427
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 22, 2025
cbac390
Remove duplicate animateOutToRef
r100-stack Jan 22, 2025
b4876c8
onExit called before onClose
r100-stack Jan 23, 2025
8112a64
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 23, 2025
630e7c9
Leftover
r100-stack Jan 23, 2025
ec56aaa
Fix Dialog subcomponents cannot self exist
r100-stack Jan 23, 2025
4344c56
Fix a few more tests
r100-stack Jan 23, 2025
14b84cf
Separate DialogMain context
r100-stack Jan 24, 2025
8ee4f16
Minimize diff
r100-stack Jan 24, 2025
de24f21
Leftover
r100-stack Jan 24, 2025
e4a0688
Changeset
r100-stack Jan 24, 2025
e33e1a1
Copyright
r100-stack Jan 24, 2025
0afd50e
Merge branch 'main' into r/react-transition-group-removal
r100-stack Jan 28, 2025
24783fb
Comments
r100-stack Jan 28, 2025
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
5 changes: 5 additions & 0 deletions .changeset/rude-books-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@itwin/itwinui-css': minor
---

Animation related classes are now deprecated: `.iui-enter`, `.iui-enter-active`, `.iui-exit`, `.iui-exit-active`.
7 changes: 7 additions & 0 deletions .changeset/tidy-geckos-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@itwin/itwinui-react': minor
---

Removed dependency on `react-transition-group`. Notable changes in components:
* `useToaster`: Animations have been reworked to directly use the web animations API.
* `Dialog` and `Modal`: Exit animations have been temporarily removed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/react-workshop/src/Toasts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ describe('Toasts', () => {
cy.get('#ladle-root').within(() => {
cy.get('button').first().click();
});

// Wait for entry animation to complete
cy.wait(240);
cy.compareSnapshot(testName);
});
});
Expand Down
31 changes: 0 additions & 31 deletions packages/itwinui-css/src/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -41,37 +41,6 @@
}
}

/// Classes for react-transition-group
/// Used for expand/collapse transitions. Needs height/width to be set in JS.
@mixin iui-transition-group {
$transition-rule:
opacity var(--iui-duration-1) ease-out,
width var(--iui-duration-1) ease-out,
height var(--iui-duration-1) ease-out;

&.iui-enter {
opacity: 0;
}

&.iui-enter-active {
opacity: 1;
@media (prefers-reduced-motion: no-preference) {
transition: $transition-rule;
}
}

&.iui-exit {
opacity: 1;
}

&.iui-exit-active {
opacity: 0;
@media (prefers-reduced-motion: no-preference) {
transition: $transition-rule;
}
}
}

@mixin safari-only {
@supports (-apple-pay-button-style: inherit) {
@content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,6 @@ $iui-side-navigation-icon-margins: calc(1.5 * var(--iui-size-m));
background-color: var(--iui-color-background);
border-inline-end: 1px solid var(--iui-color-border);

@include mixins.iui-transition-group;

&.iui-enter-active,
&.iui-exit-active {
display: flex;
}

&-content {
padding-block: 0 var(--iui-size-s);
padding-inline: var(--iui-size-s);
Expand Down
1 change: 0 additions & 1 deletion packages/itwinui-css/src/table/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@
border-inline-end: 1px solid transparent;
border-block-end: 1px solid var(--iui-color-border);
flex-shrink: 0;
@include mixins.iui-transition-group;
}

// #region Selection
Expand Down
4 changes: 1 addition & 3 deletions packages/itwinui-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,7 @@
"@tanstack/react-virtual": "^3.8.2",
"classnames": "^2.3.2",
"jotai": "^2.8.0",
"react-table": "^7.8.0",
"react-transition-group": "^4.4.5"
"react-table": "^7.8.0"
},
"devDependencies": {
"@swc/cli": "^0.5.1",
Expand All @@ -121,7 +120,6 @@
"@types/node": "*",
"@types/react": "*",
"@types/react-dom": "*",
"@types/react-transition-group": "^4.4.10",
"@vitest/coverage-v8": "^1.2.1",
"eslint": "^8",
"eslint-config-prettier": "^8.8.0",
Expand Down
7 changes: 0 additions & 7 deletions packages/itwinui-react/src/core/Dialog/Dialog.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,6 @@ it('should not stay in the DOM when isOpen=false', () => {

rerender(<Component isOpen={false} />);

// Should be there in the DOM until the exit animation is finished
dialogWrapper = container.querySelector('.iui-dialog-wrapper') as HTMLElement;
expect(dialogWrapper).toBeTruthy();

// Since timeout for the exit animation is 600ms
act(() => vi.advanceTimersByTime(600));

dialogWrapper = container.querySelector('.iui-dialog-wrapper') as HTMLElement;
expect(dialogWrapper).toBeFalsy();
});
61 changes: 29 additions & 32 deletions packages/itwinui-react/src/core/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@ import { DialogButtonBar } from './DialogButtonBar.js';
import { DialogMain } from './DialogMain.js';
import { useMergedRefs, Box, Portal } from '../../utils/index.js';
import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
import { Transition } from 'react-transition-group';

type DialogProps = {
/**
* Dialog content.
*/
children: React.ReactNode;
} & Omit<DialogContextProps, 'dialogRootRef'>;
} & DialogContextProps;

const DialogComponent = React.forwardRef((props, ref) => {
const {
Expand All @@ -42,37 +41,35 @@ const DialogComponent = React.forwardRef((props, ref) => {
} = props;

const dialogRootRef = React.useRef<HTMLDivElement>(null);
const mergedRefs = useMergedRefs(ref, dialogRootRef);

return (
<Transition in={isOpen} timeout={{ exit: 600 }} mountOnEnter unmountOnExit>
<DialogContext.Provider
value={{
isOpen,
onClose,
closeOnEsc,
closeOnExternalClick,
isDismissible,
preventDocumentScroll,
trapFocus,
setFocus,
isDraggable,
isResizable,
relativeTo,
dialogRootRef,
placement,
}}
>
<Portal portal={portal}>
<Box
className={cx('iui-dialog-wrapper', className)}
data-iui-relative={relativeTo === 'container'}
ref={useMergedRefs(ref, dialogRootRef)}
{...rest}
/>
</Portal>
</DialogContext.Provider>
</Transition>
);
return isOpen ? (
<DialogContext.Provider
value={{
isOpen,
onClose,
closeOnEsc,
closeOnExternalClick,
isDismissible,
preventDocumentScroll,
trapFocus,
setFocus,
isDraggable,
isResizable,
relativeTo,
placement,
}}
>
<Portal portal={portal}>
<Box
className={cx('iui-dialog-wrapper', className)}
data-iui-relative={relativeTo === 'container'}
ref={mergedRefs}
{...rest}
/>
</Portal>
</DialogContext.Provider>
) : null;
}) as PolymorphicForwardRefComponent<'div', DialogProps>;
if (process.env.NODE_ENV === 'development') {
DialogComponent.displayName = 'Dialog';
Expand Down
7 changes: 5 additions & 2 deletions packages/itwinui-react/src/core/Dialog/DialogBackdrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Backdrop } from '../Backdrop/Backdrop.js';
import type { BackdropProps } from '../Backdrop/Backdrop.js';
import { useMergedRefs } from '../../utils/index.js';
import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
import { useDialogContext } from './DialogContext.js';
import type { DialogContextProps } from './DialogContext.js';
import { useDialogContext, type DialogContextProps } from './DialogContext.js';
import { useDialogMainContext } from './DialogMainContext.js';
import cx from 'classnames';

type DialogBackdropProps = BackdropProps &
Expand All @@ -25,6 +25,8 @@ type DialogBackdropProps = BackdropProps &
*/
export const DialogBackdrop = React.forwardRef((props, ref) => {
const dialogContext = useDialogContext();
const dialogMainContext = useDialogMainContext();

const {
isVisible = dialogContext.isOpen,
isDismissible = dialogContext.isDismissible,
Expand All @@ -47,6 +49,7 @@ export const DialogBackdrop = React.forwardRef((props, ref) => {
return;
}
if (isDismissible && closeOnExternalClick && onClose) {
dialogMainContext?.beforeClose();
onClose(event);
}
onMouseDown?.(event);
Expand Down
76 changes: 44 additions & 32 deletions packages/itwinui-react/src/core/Dialog/DialogMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ import {
import type { PolymorphicForwardRefComponent } from '../../utils/index.js';
import { useDialogContext } from './DialogContext.js';
import type { DialogContextProps } from './DialogContext.js';
import { Transition } from 'react-transition-group';
import { DialogDragContext } from './DialogDragContext.js';
import { useDragAndDrop } from '../../utils/hooks/useDragAndDrop.js';
import { DialogMainContext } from './DialogMainContext.js';

export type DialogMainProps = {
/**
Expand Down Expand Up @@ -73,14 +73,16 @@ export const DialogMain = React.forwardRef((props, ref) => {
...rest
} = props;

const [style, setStyle] = React.useState<React.CSSProperties>();
const { dialogRootRef } = dialogContext;

const dialogRef = React.useRef<HTMLDivElement>(null);
const hasBeenResized = React.useRef(false);
const previousFocusedElement = React.useRef<HTMLElement | null>();

const [style, setStyle] = React.useState<React.CSSProperties>();
const hasBeenResized = React.useRef(false);

const originalBodyOverflow = React.useRef('');
React.useEffect(() => {
useLayoutEffect(() => {
if (isOpen) {
originalBodyOverflow.current = document.body.style.overflow;
}
Expand All @@ -107,7 +109,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
return () => {
ownerDocument.body.style.overflow = originalBodyOverflow.current;
};
}, [isOpen, preventDocumentScroll]);
}, [dialogRef, isOpen, preventDocumentScroll]);

const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.altKey) {
Expand All @@ -116,14 +118,15 @@ export const DialogMain = React.forwardRef((props, ref) => {
// Prevents React from resetting its properties
event.persist();
if (isDismissible && closeOnEsc && event.key === 'Escape' && onClose) {
beforeClose();
onClose(event);
}
onKeyDown?.(event);
};

const { onPointerDown, transform } = useDragAndDrop(
dialogRef,
dialogContext.dialogRootRef,
dialogRootRef,
isDraggable,
);
const handlePointerDown = React.useCallback(
Expand All @@ -149,7 +152,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
insetBlockStart: dialogRef.current?.offsetTop,
transform: `translate(${translateX}px,${translateY}px)`,
}));
}, [isDraggable, isOpen]);
}, [dialogRef, isDraggable, isOpen]);

const setResizeStyle = React.useCallback((newStyle: React.CSSProperties) => {
setStyle((oldStyle) => ({
Expand All @@ -158,6 +161,35 @@ export const DialogMain = React.forwardRef((props, ref) => {
}));
}, []);

/** Focuses dialog when opened. */
const onEnter = React.useCallback(() => {
previousFocusedElement.current = dialogRef.current?.ownerDocument
.activeElement as HTMLElement;
if (setFocus) {
dialogRef.current?.focus({ preventScroll: true });
}
}, [dialogRef, previousFocusedElement, setFocus]);

/** Brings back focus to the previously focused element when closed. */
const beforeClose = React.useCallback(() => {
if (
dialogRef.current?.contains(
dialogRef.current?.ownerDocument.activeElement,
)
) {
previousFocusedElement.current?.focus();
}
}, [dialogRef, previousFocusedElement]);

const mountRef = React.useCallback(
(element: HTMLElement | null) => {
if (element) {
onEnter();
}
},
[onEnter],
);

const content = (
<Box
className={cx(
Expand All @@ -171,7 +203,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
className,
)}
role='dialog'
ref={useMergedRefs(dialogRef, ref)}
ref={useMergedRefs(dialogRef, mountRef, ref)}
onKeyDown={handleKeyDown}
tabIndex={-1}
data-iui-placement={placement}
Expand All @@ -187,7 +219,7 @@ export const DialogMain = React.forwardRef((props, ref) => {
{isResizable && (
<Resizer
elementRef={dialogRef}
containerRef={dialogContext.dialogRootRef}
containerRef={dialogRootRef}
onResizeStart={() => {
if (!hasBeenResized.current) {
hasBeenResized.current = true;
Expand All @@ -204,34 +236,14 @@ export const DialogMain = React.forwardRef((props, ref) => {
);

return (
<Transition
in={isOpen}
appear={true}
timeout={{ exit: 600 }}
// Focuses dialog when opened
onEntered={() => {
previousFocusedElement.current = dialogRef.current?.ownerDocument
.activeElement as HTMLElement;
setFocus && dialogRef.current?.focus({ preventScroll: true });
}}
// Brings back focus to the previously focused element when closed
onExit={() => {
if (
dialogRef.current?.contains(
dialogRef.current?.ownerDocument.activeElement,
)
) {
previousFocusedElement.current?.focus();
}
}}
unmountOnExit={true}
nodeRef={dialogRef}
<DialogMainContext.Provider
value={React.useMemo(() => ({ beforeClose }), [beforeClose])}
>
<DialogDragContext.Provider value={{ onPointerDown: handlePointerDown }}>
{trapFocus && <FocusTrap>{content}</FocusTrap>}
{!trapFocus && content}
</DialogDragContext.Provider>
</Transition>
</DialogMainContext.Provider>
);
}) as PolymorphicForwardRefComponent<'div', DialogMainProps>;
if (process.env.NODE_ENV === 'development') {
Expand Down
Loading
Loading