From 884025cafe31edacf50c0db743b1202d1c9f8b3a Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Tue, 1 Oct 2024 11:14:31 +0200 Subject: [PATCH 01/13] fix(AppFrame): top bar changes and nav item interface changes --- .../src/components/AppFrame/AppFrame.mdx | 45 ++++++++++++ .../components/AppFrame/AppFrame.stories.tsx | 15 ++-- .../src/components/AppFrame/AppFrame.tsx | 4 +- .../components/NavigationItem/types.ts | 4 +- .../NavigationTopBar.module.scss | 35 ++++++--- .../NavigationTopBar/NavigationTopBar.tsx | 73 ++++++++++++------- .../src/components/AppFrame/constants.ts | 2 + .../components/AppFrame/stories-helpers.tsx | 48 +++++------- .../src/components/AppFrame/types.ts | 5 -- 9 files changed, 150 insertions(+), 81 deletions(-) create mode 100644 packages/react-components/src/components/AppFrame/constants.ts 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.stories.tsx b/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx index 433b68756..a57c1cb53 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..14b0cc1b2 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(() => { 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..20cb34b61 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; @@ -17,8 +19,13 @@ $mobile-breakpoint: 768px; padding: 10px 0; } + &__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/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..13f720e18 100644 --- a/packages/react-components/src/components/AppFrame/stories-helpers.tsx +++ b/packages/react-components/src/components/AppFrame/stories-helpers.tsx @@ -27,21 +27,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 +69,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 +154,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,18 +190,18 @@ 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()} /> ); diff --git a/packages/react-components/src/components/AppFrame/types.ts b/packages/react-components/src/components/AppFrame/types.ts index 53a12a9c1..e8a2b5cee 100644 --- a/packages/react-components/src/components/AppFrame/types.ts +++ b/packages/react-components/src/components/AppFrame/types.ts @@ -35,11 +35,6 @@ export interface IAppFrameProps extends ComponentCoreProps { * The CSS class for the content container */ contentClassName?: string; - /** - * The value that will determine on which resolution mobile view will be displayed - * @default 705 - */ - mobileViewBreakpoint?: number; } export type { INavigationProps } from './components/Navigation/types'; From de8ab211657128719a0f9fcdeb5cb4b806581a93 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Tue, 1 Oct 2024 13:31:52 +0200 Subject: [PATCH 02/13] fix(AppFrame): top bar title fixed height --- .../components/NavigationTopBar/NavigationTopBar.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 20cb34b61..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 @@ -16,7 +16,7 @@ $mobile-alert-size: 70px; display: flex; align-items: center; justify-content: center; - padding: 10px 0; + height: 36px; } &__alerts-wrapper { From 445ae1a91b89bde7f9ea0f2b9bdc6a1b007eae08 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Tue, 1 Oct 2024 14:26:29 +0200 Subject: [PATCH 03/13] fix(AppFrame): nav styles update --- .../components/MobileNavigation/MobileNavigation.module.scss | 1 - .../AppFrame/components/Navigation/Navigation.module.scss | 1 - 2 files changed, 2 deletions(-) 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/Navigation/Navigation.module.scss b/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss index bc7fbd957..95c83ab04 100644 --- a/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss +++ b/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss @@ -6,7 +6,6 @@ $base-class: 'navigation'; flex-shrink: 0; align-items: center; justify-content: space-between; - z-index: 1; background-color: var(--navbar-background); padding-top: 6px; padding-bottom: 6px; From 994b2b552de9e757c1e52d5b6dbefcc7e5765f50 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Tue, 1 Oct 2024 15:01:08 +0200 Subject: [PATCH 04/13] fix(AppFrame): top bar styles update --- .../components/NavigationTopBar/NavigationTopBar.module.scss | 2 -- 1 file changed, 2 deletions(-) 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 24ac27ce5..40c8aebc8 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 @@ -7,7 +7,6 @@ $mobile-alert-size: 70px; .#{$base-class} { display: flex; - position: relative; flex-direction: column; width: 100%; overflow: hidden; @@ -25,7 +24,6 @@ $mobile-alert-size: 70px; &__alert { display: flex; - position: relative; align-items: center; justify-content: space-evenly; transition: all var(--transition-duration-moderate-1) ease-in-out; From 4c852baefd902e555656e79c6826edb775c5967e Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Wed, 2 Oct 2024 09:21:33 +0200 Subject: [PATCH 05/13] fix(AppFrame): new alert in docs --- .../components/AppFrame/AppFrame.stories.tsx | 2 +- .../NavigationTopBar.module.scss | 2 ++ .../components/NavigationTopBar/examples.tsx | 22 +++++++++++++++++++ .../components/AppFrame/stories-helpers.tsx | 2 ++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx b/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx index a57c1cb53..af26475d5 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx +++ b/packages/react-components/src/components/AppFrame/AppFrame.stories.tsx @@ -237,7 +237,7 @@ export const Default = (): React.ReactElement => { > ); }; + +export const InfoAlert: React.FC<{ + show: boolean; + onClose: () => void; +}> = ({ show, onClose }) => { + return ( + + Info alert + + ); +}; diff --git a/packages/react-components/src/components/AppFrame/stories-helpers.tsx b/packages/react-components/src/components/AppFrame/stories-helpers.tsx index 13f720e18..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'; @@ -203,6 +204,7 @@ export const ExampleTopBar: React.FC<{ show={visibleAlert === 2} onClose={() => closeAlert()} /> + closeAlert()} /> ); }; From 0018d81f8dbc2aeec097adacc78b837bfd39243d Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Wed, 2 Oct 2024 10:54:22 +0200 Subject: [PATCH 06/13] fix(AppFrame): remove div for mobile nav --- packages/react-components/src/components/AppFrame/AppFrame.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-components/src/components/AppFrame/AppFrame.tsx b/packages/react-components/src/components/AppFrame/AppFrame.tsx index 14b0cc1b2..c157ee1c2 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.tsx +++ b/packages/react-components/src/components/AppFrame/AppFrame.tsx @@ -115,7 +115,7 @@ const Frame = (props: IAppFrameProps) => { > {topBar}
-
{mobileNavigation}
+ {mobileNavigation} )} From 942761f4b7a781e3ead92ae8374f97712be20e3b Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Wed, 2 Oct 2024 12:51:32 +0200 Subject: [PATCH 07/13] fix(AppFrame): styles for app container --- .../src/components/AppFrame/AppFrame.module.scss | 1 + 1 file changed, 1 insertion(+) 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; From e937043fbaab35de6180c38d43a7ba1aeca3adfa Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Wed, 2 Oct 2024 14:49:38 +0200 Subject: [PATCH 08/13] fix(AppFrame): zindex for nav --- .../AppFrame/components/Navigation/Navigation.module.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss b/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss index 95c83ab04..bc7fbd957 100644 --- a/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss +++ b/packages/react-components/src/components/AppFrame/components/Navigation/Navigation.module.scss @@ -6,6 +6,7 @@ $base-class: 'navigation'; flex-shrink: 0; align-items: center; justify-content: space-between; + z-index: 1; background-color: var(--navbar-background); padding-top: 6px; padding-bottom: 6px; From 14db4cde182766a2501c22b30368275a796ebf6a Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Wed, 2 Oct 2024 15:12:08 +0200 Subject: [PATCH 09/13] fix(AppFrame): product switcher tooltip fix --- .../src/components/ProductSwitcher/ProductSwitcher.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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" From 872c0ea6dd2ea38c636c75f9a148d4144311f4f8 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Thu, 3 Oct 2024 11:06:37 +0200 Subject: [PATCH 10/13] fix(AppFrame): possibility to push initial state for the side nav visibility --- .../src/components/AppFrame/AppFrame.tsx | 14 +++++++++----- .../src/components/AppFrame/types.ts | 4 ++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/react-components/src/components/AppFrame/AppFrame.tsx b/packages/react-components/src/components/AppFrame/AppFrame.tsx index c157ee1c2..c9f98f6c2 100644 --- a/packages/react-components/src/components/AppFrame/AppFrame.tsx +++ b/packages/react-components/src/components/AppFrame/AppFrame.tsx @@ -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/types.ts b/packages/react-components/src/components/AppFrame/types.ts index e8a2b5cee..16dfdc430 100644 --- a/packages/react-components/src/components/AppFrame/types.ts +++ b/packages/react-components/src/components/AppFrame/types.ts @@ -35,6 +35,10 @@ export interface IAppFrameProps extends ComponentCoreProps { * The CSS class for the content container */ contentClassName?: string; + /** + * Set the initial visibility of the side navigation bar + */ + isSideNavigationVisible?: boolean; } export type { INavigationProps } from './components/Navigation/types'; From dcc19f397e2e92fc866a5cd5d583eccc6925a653 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Thu, 3 Oct 2024 13:53:09 +0200 Subject: [PATCH 11/13] fix(AppFrame): useAnimation fixes --- .../src/components/Accordion/Accordion.tsx | 6 ++--- .../AccordionAnimatedLabel.module.scss | 14 +---------- .../SideNavigationGroup.tsx | 6 ++--- .../src/hooks/useAnimations.ts | 23 +++++++------------ 4 files changed, 15 insertions(+), 34 deletions(-) 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/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/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, }; }; From 15a72f61a344a87acbae7d611aafe664d35c6bc2 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Thu, 3 Oct 2024 14:29:37 +0200 Subject: [PATCH 12/13] fix(AppFrame): accordion tests fix --- .../src/components/Accordion/Accordion.spec.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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); }); From 39e20ca5037ffb2c696a65794462a3be3e616718 Mon Sep 17 00:00:00 2001 From: Marcin Sawicki Date: Thu, 3 Oct 2024 14:36:45 +0200 Subject: [PATCH 13/13] fix(AppFrame): side nav group test fixes --- .../SideNavigationGroup.spec.tsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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', () => {