From 79063e47dbdda17bc8aa85eca74875a9a230daef Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Thu, 3 Oct 2024 14:44:07 +0200 Subject: [PATCH] fix(AppFrame): top bar changes and nav item interface update (#1374) --- .../components/Accordion/Accordion.spec.tsx | 8 +- .../src/components/Accordion/Accordion.tsx | 6 +- .../AccordionAnimatedLabel.module.scss | 14 +--- .../src/components/AppFrame/AppFrame.mdx | 45 ++++++++++++ .../components/AppFrame/AppFrame.module.scss | 1 + .../components/AppFrame/AppFrame.stories.tsx | 15 ++-- .../src/components/AppFrame/AppFrame.tsx | 20 +++-- .../MobileNavigation.module.scss | 1 - .../components/NavigationItem/types.ts | 4 +- .../NavigationTopBar.module.scss | 37 +++++++--- .../NavigationTopBar/NavigationTopBar.tsx | 73 ++++++++++++------- .../components/NavigationTopBar/examples.tsx | 22 ++++++ .../SideNavigationGroup.spec.tsx | 16 ++-- .../SideNavigationGroup.tsx | 6 +- .../src/components/AppFrame/constants.ts | 2 + .../components/AppFrame/stories-helpers.tsx | 50 +++++-------- .../src/components/AppFrame/types.ts | 5 +- .../ProductSwitcher/ProductSwitcher.tsx | 3 +- .../src/hooks/useAnimations.ts | 23 ++---- 19 files changed, 223 insertions(+), 128 deletions(-) create mode 100644 packages/react-components/src/components/AppFrame/constants.ts diff --git a/packages/react-components/src/components/Accordion/Accordion.spec.tsx b/packages/react-components/src/components/Accordion/Accordion.spec.tsx index ff6cf8533..5f4573b86 100644 --- a/packages/react-components/src/components/Accordion/Accordion.spec.tsx +++ b/packages/react-components/src/components/Accordion/Accordion.spec.tsx @@ -41,7 +41,7 @@ describe(' component', () => { expect(getByText('Content')).toBeInTheDocument(); }); - it('should call onClose and onOpen handlers on label click', () => { + it('should call onClose and onOpen handlers on label click', async () => { const onClose = vi.fn(); const onOpen = vi.fn(); const { getByRole } = renderComponent({ @@ -50,8 +50,14 @@ describe(' component', () => { onOpen, }); + expect(getByRole('button')).toHaveAttribute('aria-expanded', 'false'); userEvent.click(getByRole('button')); expect(onOpen).toHaveBeenCalledTimes(1); + + await waitFor(() => { + expect(getByRole('button')).toHaveAttribute('aria-expanded', 'false'); + }); + userEvent.click(getByRole('button')); expect(onClose).toHaveBeenCalledTimes(1); }); diff --git a/packages/react-components/src/components/Accordion/Accordion.tsx b/packages/react-components/src/components/Accordion/Accordion.tsx index 598620cb9..ec1541860 100644 --- a/packages/react-components/src/components/Accordion/Accordion.tsx +++ b/packages/react-components/src/components/Accordion/Accordion.tsx @@ -33,7 +33,7 @@ export const Accordion: React.FC = ({ const { isOpen: isExpanded, isMounted, - setIsOpen, + setShouldBeVisible, } = useAnimations({ isVisible: currentlyOpen, elementRef: contentRef, @@ -51,10 +51,10 @@ export const Accordion: React.FC = ({ const handleExpandChange = (isExpanded: boolean) => { if (isExpanded) { onClose?.(); - !isControlled && setIsOpen(false); + !isControlled && setShouldBeVisible(false); } else { onOpen?.(); - !isControlled && setIsOpen(true); + !isControlled && setShouldBeVisible(true); } }; diff --git a/packages/react-components/src/components/Accordion/components/AccordionAnimatedLabel.module.scss b/packages/react-components/src/components/Accordion/components/AccordionAnimatedLabel.module.scss index ddce1b477..7901f5207 100644 --- a/packages/react-components/src/components/Accordion/components/AccordionAnimatedLabel.module.scss +++ b/packages/react-components/src/components/Accordion/components/AccordionAnimatedLabel.module.scss @@ -15,19 +15,7 @@ $base-class: 'accordion-animated-label'; max-width: 100%; &--visible { - animation: change-visibility var(--transition-duration-fast-2); - animation-fill-mode: forwards; + opacity: 1; } } } - -@keyframes change-visibility { - 0% { - opacity: 0; - } - - 70%, - 100% { - opacity: 1; - } -} diff --git a/packages/react-components/src/components/AppFrame/AppFrame.mdx b/packages/react-components/src/components/AppFrame/AppFrame.mdx index 12d7df6fa..f56e9766e 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.mdx +++ b/packages/react-components/src/components/AppFrame/AppFrame.mdx @@ -309,6 +309,51 @@ return ( ``` +#### How to build top bar alerts + +In general, alerts do not have height limits, which allows you to insert any amount of content. The container in which they are located also does not have such limits. + +However, it is important to remember to provide the best possible user experience, not to put too much information into the top bar, which could reduce the application window. + +Components layout changes depending on the browser window resolution: +- `width > 1110px` - standard layout with the option of setting the kind for cta buttons +- `width <= 1100px` - row layout, buttons are automatically changed to kind `text` (`link-inverted` for `warning` alert) +- `width <= 705px` - mobile version (no radiuses) + +It is also important to remember that you should not display a large number of alerts. For this purpose, we suggest a simple solution, consisting in hiding and showing only the currently expected alert (the decision is up to the project which alert has the highest priority) + +##### Example + +```jsx +import { NavigationTopBarAlert } from '@livechat/design-system-react-components'; + +const [visibleAlert, setVisibleAlert] = React.useState(null); + +return ( + + + Info + + + Warning + + + Error + + +); + +``` + ## Component API diff --git a/packages/react-components/src/components/AppFrame/AppFrame.module.scss b/packages/react-components/src/components/AppFrame/AppFrame.module.scss index 5437febd1..3af1915b2 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.module.scss +++ b/packages/react-components/src/components/AppFrame/AppFrame.module.scss @@ -16,6 +16,7 @@ $base-class: 'app-frame'; padding-bottom: 6px; width: 100%; height: 100%; + overflow: hidden; &--mobile { padding: 0; diff --git a/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx b/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx index 433b68756..af26475d5 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx +++ b/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx @@ -89,9 +89,7 @@ export const Default = (): React.ReactElement => { const [activeItem, setActiveItem] = React.useState('archives'); const [activeSubItem, setActiveSubItem] = React.useState(0); const [topBarVisible, setTopBarVisible] = React.useState(true); - const [visibleAlerts, setVisibleAlerts] = React.useState( - Array(3).fill(false) - ); + const [visibleAlert, setVisibleAlert] = React.useState(null); const { products } = useProductSwitcher({ env: 'labs', @@ -228,21 +226,22 @@ export const Default = (): React.ReactElement => { } sideNavigation={getSubNav()} topBar={ - topBarVisible || visibleAlerts.some((alert) => alert) ? ( + topBarVisible ? ( ) : null } > ); diff --git a/packages/react-components/src/components/AppFrame/AppFrame.tsx b/packages/react-components/src/components/AppFrame/AppFrame.tsx index c8b324229..c9f98f6c2 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.tsx +++ b/packages/react-components/src/components/AppFrame/AppFrame.tsx @@ -5,6 +5,7 @@ import cx from 'clsx'; import { useAnimations, useMobileViewDetector } from '../../hooks'; import { AppFrameProvider, useAppFrame } from '../../providers'; +import { MOBILE_BREAKPOINT } from './constants'; import { IAppFrameProps } from './types'; import styles from './AppFrame.module.scss'; @@ -23,7 +24,6 @@ const Frame = (props: IAppFrameProps) => { topBarClassName, sideNavigationContainerClassName, contentClassName, - mobileViewBreakpoint = 705, } = props; const mergedClassNames = cx(styles[baseClass], className); const { @@ -37,7 +37,7 @@ const Frame = (props: IAppFrameProps) => { elementRef: sideNavWrapperRef, }); const { isMobile, handleResizeRef } = useMobileViewDetector({ - mobileBreakpoint: mobileViewBreakpoint, + mobileBreakpoint: MOBILE_BREAKPOINT, }); React.useEffect(() => { @@ -115,7 +115,7 @@ const Frame = (props: IAppFrameProps) => { > {topBar} -
{mobileNavigation}
+ {mobileNavigation} )} @@ -124,8 +124,12 @@ const Frame = (props: IAppFrameProps) => { ); }; -export const AppFrame: React.FC = (props) => ( - - - -); +export const AppFrame: React.FC = (props) => { + const { isSideNavigationVisible, ...restProps } = props; + + return ( + + + + ); +}; diff --git a/packages/react-components/src/components/AppFrame/components/MobileNavigation/MobileNavigation.module.scss b/packages/react-components/src/components/AppFrame/components/MobileNavigation/MobileNavigation.module.scss index bb1f21e80..6de907c11 100644 --- a/packages/react-components/src/components/AppFrame/components/MobileNavigation/MobileNavigation.module.scss +++ b/packages/react-components/src/components/AppFrame/components/MobileNavigation/MobileNavigation.module.scss @@ -6,7 +6,6 @@ $base-class: 'mobile-navigation'; flex-shrink: 0; align-items: center; justify-content: space-around; - z-index: 1; background-color: var(--navbar-background); padding: 6px; } diff --git a/packages/react-components/src/components/AppFrame/components/NavigationItem/types.ts b/packages/react-components/src/components/AppFrame/components/NavigationItem/types.ts index a70b6cb85..90f4d601b 100644 --- a/packages/react-components/src/components/AppFrame/components/NavigationItem/types.ts +++ b/packages/react-components/src/components/AppFrame/components/NavigationItem/types.ts @@ -2,7 +2,9 @@ import * as React from 'react'; import { ComponentCoreProps } from '../../../../utils/types'; -export interface INavigationItemProps extends ComponentCoreProps { +export interface INavigationItemProps + extends ComponentCoreProps, + Omit, 'onClick'> { /** * The ID of the item */ diff --git a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.module.scss b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.module.scss index 5df1d3b78..24ac27ce5 100644 --- a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.module.scss +++ b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.module.scss @@ -1,12 +1,14 @@ /* stylelint-disable media-query-no-invalid */ $base-class: 'navigation-top-bar'; -$mobile-breakpoint: 768px; +$alerts-mobile-breakpoint: 810px; +$mobile-breakpoint: 705px; +$alert-size: 42px; +$mobile-alert-size: 70px; .#{$base-class} { display: flex; position: relative; flex-direction: column; - gap: var(--spacing-1); width: 100%; overflow: hidden; @@ -14,11 +16,16 @@ $mobile-breakpoint: 768px; display: flex; align-items: center; justify-content: center; - padding: 10px 0; + height: 36px; + } + + &__alerts-wrapper { + overflow: hidden; } &__alert { display: flex; + position: relative; align-items: center; justify-content: space-evenly; transition: all var(--transition-duration-moderate-1) ease-in-out; @@ -34,7 +41,11 @@ $mobile-breakpoint: 768px; &--open { padding-block: 5px; - min-height: 42px; + min-height: $alert-size; + + @media screen and (width <=$mobile-breakpoint) { + min-height: $mobile-alert-size; + } } &__wrapper { @@ -75,9 +86,9 @@ $mobile-breakpoint: 768px; justify-content: center; width: calc(100% - 62px); - @media screen and (width <=$mobile-breakpoint) { + @media screen and (width <=$alerts-mobile-breakpoint) { flex-direction: column; - gap: var(--spacing-2); + gap: 0; } } @@ -92,10 +103,11 @@ $mobile-breakpoint: 768px; padding: 0; color: inherit; - @media screen and (width <=$mobile-breakpoint) { + @media screen and (width <=$alerts-mobile-breakpoint) { position: absolute; - top: var(--spacing-3); + top: auto; right: var(--spacing-3); + bottom: auto; } } @@ -106,16 +118,19 @@ $mobile-breakpoint: 768px; align-items: center; justify-content: center; - @media screen and (width <=$mobile-breakpoint) { + @media screen and (width <=$alerts-mobile-breakpoint) { flex-shrink: 1; flex-wrap: wrap; } } - @media screen and (width <=$mobile-breakpoint) { - border-radius: 0; + @media screen and (width <=$alerts-mobile-breakpoint) { padding-inline: var(--spacing-10); text-align: center; } + + @media screen and (width <=$mobile-breakpoint) { + border-radius: 0; + } } } diff --git a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.tsx b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.tsx index 987ab1ed5..071f877ad 100644 --- a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.tsx +++ b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/NavigationTopBar.tsx @@ -4,8 +4,11 @@ import { Close } from '@livechat/design-system-icons'; import cx from 'clsx'; import { useAnimations } from '../../../../hooks'; +import { resizeCallback } from '../../../../hooks/helpers'; +import { NODE } from '../../../../hooks/types'; import { Button } from '../../../Button'; import { Icon } from '../../../Icon'; +import { ALERTS_MOBILE_BREAKPOINT } from '../../constants'; import { INavigationTopBarProps, @@ -14,6 +17,7 @@ import { } from './types'; import styles from './NavigationTopBar.module.scss'; + const baseClass = 'navigation-top-bar'; const alertClass = `${baseClass}__alert`; @@ -33,7 +37,7 @@ export const NavigationTopBar = ({ }: INavigationTopBarProps): React.ReactElement => { return (
- {children} +
{children}
{additionalNodes}
); @@ -89,17 +93,24 @@ export const NavigationTopBarAlert: React.FC = ({ secondaryCta, isVisible = true, }) => { + const [isMobile, setIsMobile] = React.useState(false); const alertRef = React.useRef(null); const { isMounted, isOpen } = useAnimations({ isVisible, elementRef: alertRef, }); - - const isMobile = window.matchMedia('(max-width: 768px)').matches; - - const secondaryCtaKind = kind === 'warning' ? 'link-inverted' : 'text'; - const primaryCtaKind = + const handleResizeRef = React.useCallback( + (node: NODE) => + resizeCallback(node, (newSize: DOMRectReadOnly) => + setIsMobile(newSize.width < ALERTS_MOBILE_BREAKPOINT) + ), + [] + ); + const mobileCtaKind = kind === 'warning' ? 'link-inverted' : 'text'; + const desktopCtaKind = kind === 'warning' ? 'plain-lock-black' : 'high-contrast'; + const customPrimaryCtaKind = primaryCta?.kind; + const customSecondaryCtaKind = secondaryCta?.kind; const Ctas = primaryCta || secondaryCta ? ( @@ -107,14 +118,24 @@ export const NavigationTopBarAlert: React.FC = ({ {primaryCta && ( )} {secondaryCta && ( - )} @@ -141,29 +162,31 @@ export const NavigationTopBarAlert: React.FC = ({ return ( <> {isMounted && ( -
+
- {Children} +
+ {Children} +
)} diff --git a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/examples.tsx b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/examples.tsx index 27a38fa38..c28aaa615 100644 --- a/packages/react-components/src/components/AppFrame/components/NavigationTopBar/examples.tsx +++ b/packages/react-components/src/components/AppFrame/components/NavigationTopBar/examples.tsx @@ -95,3 +95,25 @@ export const CustomBackgroundAlert: React.FC<{ ); }; + +export const InfoAlert: React.FC<{ + show: boolean; + onClose: () => void; +}> = ({ show, onClose }) => { + return ( + + Info alert + + ); +}; diff --git a/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.spec.tsx b/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.spec.tsx index b1604af51..47f1a4cc1 100644 --- a/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.spec.tsx +++ b/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.spec.tsx @@ -1,4 +1,4 @@ -import { render, userEvent, vi } from 'test-utils'; +import { render, userEvent, vi, waitFor } from 'test-utils'; import { SideNavigationGroup } from './SideNavigationGroup'; import { ISideNavigationGroupProps } from './types'; @@ -36,7 +36,7 @@ describe(' component', () => { expect(getByText('Side navigation label')).toBeInTheDocument(); }); - it('should render label as provided function includes state change when isCollapsible is set true', () => { + it('should render label as provided function includes state change when isCollapsible is set true', async () => { const handleLabel = (isOpen: boolean) => (
{isOpen ? 'Opened label' : 'Closed label'}
); @@ -49,7 +49,10 @@ describe(' component', () => { expect(toggle).toHaveTextContent('Closed label'); userEvent.click(toggle); - expect(toggle).toHaveTextContent('Opened label'); + + await waitFor(() => { + expect(toggle).toHaveTextContent('Opened label'); + }); }); it('should render rightNode as provided element if isCollapsible is set true', () => { @@ -62,7 +65,7 @@ describe(' component', () => { expect(getByText('Right node')).toBeInTheDocument(); }); - it('should render rightNode as provided function includes state change when isCollapsible is set true', () => { + it('should render rightNode as provided function includes state change when isCollapsible is set true', async () => { const handleRightNode = (isOpen: boolean) => (
{isOpen ? 'Opened node' : 'Closed node'}
); @@ -75,7 +78,10 @@ describe(' component', () => { expect(toggle).toHaveTextContent('Closed node'); userEvent.click(toggle); - expect(toggle).toHaveTextContent('Opened node'); + + await waitFor(() => { + expect(toggle).toHaveTextContent('Opened node'); + }); }); it('should call onItemHover when provided if user hovers the label', () => { diff --git a/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.tsx b/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.tsx index 60eef433b..6ef673d58 100644 --- a/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.tsx +++ b/packages/react-components/src/components/AppFrame/components/SideNavigationGroup/SideNavigationGroup.tsx @@ -31,7 +31,7 @@ export const SideNavigationGroup: React.FC = ({ const [listHeight, setListHeight] = React.useState(0); const hadActiveListElementsRef = React.useRef(false); const listWrapperRef = React.useRef(null); - const { isOpen, isMounted, setIsOpen } = useAnimations({ + const { isOpen, isMounted, setShouldBeVisible } = useAnimations({ isVisible: !isCollapsible || shouldOpenOnInit, elementRef: listWrapperRef, }); @@ -39,11 +39,11 @@ export const SideNavigationGroup: React.FC = ({ typeof rightNode === 'function' ? rightNode(isOpen) : rightNode; const localLabel = typeof label === 'function' ? label(isOpen) : label; - const openList = (): void => setIsOpen(true); + const openList = (): void => setShouldBeVisible(true); const toggle = (): void => { if (!isCollapsible) return; - setIsOpen((prev) => !prev); + setShouldBeVisible((prev) => !prev); }; const onSetListNode = (node: HTMLUListElement): void => { diff --git a/packages/react-components/src/components/AppFrame/constants.ts b/packages/react-components/src/components/AppFrame/constants.ts new file mode 100644 index 000000000..4ed3552f5 --- /dev/null +++ b/packages/react-components/src/components/AppFrame/constants.ts @@ -0,0 +1,2 @@ +export const MOBILE_BREAKPOINT = 705; +export const ALERTS_MOBILE_BREAKPOINT = 751; diff --git a/packages/react-components/src/components/AppFrame/stories-helpers.tsx b/packages/react-components/src/components/AppFrame/stories-helpers.tsx index 2d5e88f18..01c9d0c1c 100644 --- a/packages/react-components/src/components/AppFrame/stories-helpers.tsx +++ b/packages/react-components/src/components/AppFrame/stories-helpers.tsx @@ -19,6 +19,7 @@ import { ChameleonAlert, CustomBackgroundAlert, DisconnectedAlert, + InfoAlert, } from './components/NavigationTopBar/examples'; import { NavigationTopBar } from './components/NavigationTopBar/NavigationTopBar'; @@ -27,21 +28,22 @@ import './AppFrame.stories.css'; interface ExampleAppContentProps { showToggle: boolean; alerts?: boolean[]; - setAlerts?: (alerts: boolean[]) => void; topBarVisible: boolean; setTopBarVisible: (visible: boolean) => void; + visibleAlert: number | null; + setVisibleAlert: (index: number | null) => void; } export const ExampleAppContent: React.FC = ({ showToggle, alerts, - setAlerts, topBarVisible, setTopBarVisible, + visibleAlert, + setVisibleAlert, }) => { const { isSideNavigationVisible, toggleSideNavigationVisibility } = useAppFrame(); - const [allBannersVisible, setAllBannersVisible] = React.useState(false); return (
@@ -68,27 +70,16 @@ export const ExampleAppContent: React.FC = ({ />
)} - {alerts && setAlerts && ( + {alerts && ( <> -
- Toggle all alerts - { - setAllBannersVisible(!allBannersVisible); - setAlerts(alerts.map(() => !allBannersVisible)); - }} - /> -
- {alerts.map((show, index) => ( + {alerts.map((_, index) => (
Taggle alert {index + 1} - setAlerts(alerts.map((_, i) => (i === index ? !show : _))) + setVisibleAlert(index === visibleAlert ? null : index) } />
@@ -164,17 +155,15 @@ export const ExampleAppContent: React.FC = ({ }; export const ExampleTopBar: React.FC<{ - visibleAlerts: boolean[]; topBarVisible: boolean; - setAlerts: (alerts: boolean[]) => void; -}> = ({ visibleAlerts, setAlerts, topBarVisible }) => { + visibleAlert: number | null; + setVisibleAlert: (index: number | null) => void; +}> = ({ topBarVisible, visibleAlert, setVisibleAlert }) => { const [kind, setKind] = React.useState< 'info' | 'success' | 'warning' | 'error' >('warning'); - const closeAlert = (index: number) => { - setAlerts(visibleAlerts.map((_, i) => (i === index ? false : _))); - }; + const closeAlert = () => setVisibleAlert(null); const changeKind = () => { setKind((prevKind) => { @@ -202,19 +191,20 @@ export const ExampleTopBar: React.FC<{ } > closeAlert(0)} + show={visibleAlert === 0} + onClose={() => closeAlert()} /> closeAlert(1)} + show={visibleAlert === 1} + onClose={() => closeAlert()} kind={kind} changeKind={changeKind} /> closeAlert(2)} + show={visibleAlert === 2} + onClose={() => closeAlert()} /> + closeAlert()} /> ); }; diff --git a/packages/react-components/src/components/AppFrame/types.ts b/packages/react-components/src/components/AppFrame/types.ts index 53a12a9c1..16dfdc430 100644 --- a/packages/react-components/src/components/AppFrame/types.ts +++ b/packages/react-components/src/components/AppFrame/types.ts @@ -36,10 +36,9 @@ export interface IAppFrameProps extends ComponentCoreProps { */ contentClassName?: string; /** - * The value that will determine on which resolution mobile view will be displayed - * @default 705 + * Set the initial visibility of the side navigation bar */ - mobileViewBreakpoint?: number; + isSideNavigationVisible?: boolean; } export type { INavigationProps } from './components/Navigation/types'; diff --git a/packages/react-components/src/components/ProductSwitcher/ProductSwitcher.tsx b/packages/react-components/src/components/ProductSwitcher/ProductSwitcher.tsx index 8605b220e..6ff008c12 100644 --- a/packages/react-components/src/components/ProductSwitcher/ProductSwitcher.tsx +++ b/packages/react-components/src/components/ProductSwitcher/ProductSwitcher.tsx @@ -18,6 +18,7 @@ import { import { TextLogoFull } from '@livechat/design-system-icons'; import cx from 'clsx'; +import { ThemeClassName } from '../../providers'; import { Icon } from '../Icon'; import { Tooltip } from '../Tooltip'; import { Text } from '../Typography'; @@ -126,7 +127,7 @@ export const ProductSwitcher: FC = ({ arrowOffsetY={2} offsetMainAxis={10} hoverOnDelay={400} - className={styles[`${baseClass}__tooltip`]} + className={cx(styles[`${baseClass}__tooltip`], ThemeClassName.Light)} placement="right" floatingStrategy="fixed" kind="invert" diff --git a/packages/react-components/src/hooks/useAnimations.ts b/packages/react-components/src/hooks/useAnimations.ts index c8dcb97c4..3ea10bf18 100644 --- a/packages/react-components/src/hooks/useAnimations.ts +++ b/packages/react-components/src/hooks/useAnimations.ts @@ -8,7 +8,7 @@ interface UseAnimationsProps { interface IUseAnimations { isOpen: boolean; isMounted: boolean; - setIsOpen: React.Dispatch>; + setShouldBeVisible: React.Dispatch>; } export const useAnimations = ({ @@ -17,15 +17,17 @@ export const useAnimations = ({ }: UseAnimationsProps): IUseAnimations => { const [isMounted, setIsMounted] = React.useState(isVisible); const [isOpen, setIsOpen] = React.useState(isVisible); + const [shouldBeVisible, setShouldBeVisible] = React.useState(isVisible); // The main part of the logic responsible for managing the states used to animate the container opening/closing and mounting/unmounting the container elements React.useEffect(() => { const currentElement = elementRef.current; - if (!isOpen && currentElement) { + if (!shouldBeVisible && currentElement) { const handleTransitionEnd = () => setIsMounted(false); currentElement.addEventListener('transitionend', handleTransitionEnd); + setIsOpen(false); return () => { currentElement.removeEventListener( @@ -35,31 +37,22 @@ export const useAnimations = ({ }; } - if (isOpen) { + if (shouldBeVisible) { setIsMounted(true); requestAnimationFrame(() => setIsOpen(true)); return; } - - return setIsOpen(false); - }, [isOpen]); + }, [shouldBeVisible]); // Additional logic, dedicated to the container wrapper whose visibility is managed by the context React.useEffect(() => { - if (isVisible) { - setIsMounted(true); - requestAnimationFrame(() => setIsOpen(true)); - - return; - } - - return setIsOpen(false); + setShouldBeVisible(isVisible); }, [isVisible]); return { isOpen, isMounted, - setIsOpen, + setShouldBeVisible, }; };