Skip to content

Commit bb2d2a9

Browse files
authored
Merge pull request #689 from lumapps/feat/dialogVisibilityChange
feat(dialog): add onVisibilityChange prop
2 parents d59d192 + 7317319 commit bb2d2a9

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Added
1111

1212
- Switch: add attribute `aria-checked` for screen readers correct behavior.
13+
- Dialog: add `onVisibilityChange` prop to trigger an action when the dialog is actually visible / invisible.
1314

1415
## [1.0.22][] - 2021-08-25
1516

packages/lumx-react/src/components/dialog/Dialog.stories.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,28 @@ export const ScrollableDialog = ({ theme }: any) => {
198198
);
199199
};
200200

201+
export const WithAnimationCallbacks = ({ theme }: any) => {
202+
const { button, buttonRef, closeDialog, isOpen } = useOpenButton(theme);
203+
const handleVisibiltyCallback = (isVisible: boolean) => {
204+
alert(isVisible ? 'OPEN' : 'CLOSE');
205+
};
206+
207+
return (
208+
<>
209+
{button}
210+
<Dialog
211+
isOpen={isOpen}
212+
onClose={closeDialog}
213+
size={Size.regular}
214+
parentElement={buttonRef}
215+
onVisibilityChange={handleVisibiltyCallback}
216+
>
217+
{content}
218+
</Dialog>
219+
</>
220+
);
221+
};
222+
201223
export const ScrollableDialogWithHeaderAndFooter = ({ theme }: any) => {
202224
const { button, buttonRef, closeDialog, isOpen } = useOpenButton(theme);
203225
return (

packages/lumx-react/src/components/dialog/Dialog.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ export interface DialogProps extends GenericProps {
5454
dialogProps?: GenericProps;
5555
/** On close callback. */
5656
onClose?(): void;
57+
/** Callback called when the open animation starts and the close animation finishes. */
58+
onVisibilityChange?(isVisible: boolean): void;
5759
}
5860

5961
export type DialogSizes = Extract<Size, 'tiny' | 'regular' | 'big' | 'huge'>;
@@ -108,6 +110,7 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
108110
size,
109111
zIndex,
110112
dialogProps,
113+
onVisibilityChange,
111114
...forwardedProps
112115
} = props;
113116

@@ -158,7 +161,7 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
158161
const footerChildContent = footerChildProps?.children;
159162

160163
// eslint-disable-next-line react-hooks/rules-of-hooks
161-
const isVisible = useDelayedVisibility(Boolean(isOpen), DIALOG_TRANSITION_DURATION);
164+
const isVisible = useDelayedVisibility(Boolean(isOpen), DIALOG_TRANSITION_DURATION, onVisibilityChange);
162165

163166
// eslint-disable-next-line react-hooks/rules-of-hooks
164167
const clickAwayRefs = useRef([wrapperRef]);

packages/lumx-react/src/components/dialog/__snapshots__/Dialog.test.tsx.snap

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,3 +752,46 @@ Aquitani, tertiam.
752752
</Dialog>
753753
</Fragment>
754754
`;
755+
756+
exports[`<Dialog> Snapshots and structure should render story WithAnimationCallbacks 1`] = `
757+
<Fragment>
758+
<Button
759+
emphasis="high"
760+
onClick={[Function]}
761+
size="m"
762+
theme="light"
763+
>
764+
Open dialog
765+
</Button>
766+
<Dialog
767+
isOpen={true}
768+
onClose={[Function]}
769+
onVisibilityChange={[Function]}
770+
parentElement={
771+
Object {
772+
"current": undefined,
773+
}
774+
}
775+
size="regular"
776+
>
777+
<div
778+
className="lumx-spacing-padding"
779+
>
780+
781+
Nihil hic munitissimus habendi senatus locus, nihil horum? At nos hinc posthac, sitientis piros
782+
Afros. Magna pars studiorum, prodita quaerimus. Integer legentibus erat a ante historiarum
783+
dapibus. Praeterea iter est quasdam res quas ex communi. Ullamco laboris nisi ut aliquid ex ea
784+
commodi consequat. Inmensae subtilitatis, obscuris et malesuada fames. Me non paenitet nullum
785+
festiviorem excogitasse ad hoc. Cum ceteris in veneratione tui montes, nascetur mus. Etiam
786+
habebis sem dicantur magna mollis euismod. Quis aute iure reprehenderit in voluptate velit esse.
787+
Phasellus laoreet lorem vel dolor tempus vehicula. Ambitioni dedisse scripsisse iudicaretur.
788+
Paullum deliquit, ponderibus modulisque suis ratio utitur. Ab illo tempore, ab est sed
789+
immemorabili. Nec dubitamus multa iter quae et nos invenerat. Tu quoque, Brute, fili mi, nihil
790+
timor populi, nihil! Morbi fringilla convallis sapien, id pulvinar odio volutpat. Cras mattis
791+
iudicium purus sit amet fermentum. Vivamus sagittis lacus vel augue laoreet rutrum faucibus.
792+
Quisque ut dolor gravida, placerat libero vel, euismod. Unam incolunt Belgae, aliam Aquitani,
793+
tertiam. Cras mattis iudicium purus sit amet fermentum
794+
</div>
795+
</Dialog>
796+
</Fragment>
797+
`;

packages/lumx-react/src/hooks/useDelayedVisibility.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { useEffect, useState } from 'react';
1+
import { useEffect, useState, useRef } from 'react';
22

33
/**
44
* Returns true if the component is visible taking into account the component's
55
* own visibility and the animations delay
66
*
77
* @param isComponentVisible Whether the component intends to be visible or not.
88
* @param transitionDuration time in ms that the transition takes for the specific component.
9+
* @param onVisibilityChange Callback called when the visibility changes.
910
* @return true if the component should be rendered
1011
*/
11-
export function useDelayedVisibility(isComponentVisible: boolean, transitionDuration: number): boolean {
12+
export function useDelayedVisibility(
13+
isComponentVisible: boolean,
14+
transitionDuration: number,
15+
onVisibilityChange?: (isVisible: boolean) => void,
16+
): boolean {
1217
// Delay visibility to account for the 400ms of CSS opacity animation.
1318
const [isVisible, setVisible] = useState(isComponentVisible);
1419

@@ -20,5 +25,20 @@ export function useDelayedVisibility(isComponentVisible: boolean, transitionDura
2025
}
2126
}, [isComponentVisible, transitionDuration]);
2227

28+
/**
29+
* Since we don't want onVisibiltyChange function to trigger itself if when it changes,
30+
* we store the previous visibility and only trigger when visibility is different
31+
* than previous value.
32+
*/
33+
34+
const previousVisibility = useRef(isVisible);
35+
36+
useEffect(() => {
37+
if (onVisibilityChange && previousVisibility.current !== isVisible) {
38+
onVisibilityChange(isVisible);
39+
previousVisibility.current = isVisible;
40+
}
41+
}, [isVisible, onVisibilityChange]);
42+
2343
return isComponentVisible || isVisible;
2444
}

0 commit comments

Comments
 (0)