From a8eaf5ad645e2fa387e03c80a63a5497f83a5a21 Mon Sep 17 00:00:00 2001 From: Armagan Ersoz Date: Tue, 27 Sep 2022 21:23:22 +1000 Subject: [PATCH 1/4] loading state for counters - only for UnderlineNav --- src/UnderlineNav2/LoadingCounter.tsx | 32 +++++++++++++++++++++++ src/UnderlineNav2/UnderlineNav.tsx | 24 +++++++++++++++-- src/UnderlineNav2/UnderlineNavContext.tsx | 2 ++ src/UnderlineNav2/UnderlineNavItem.tsx | 5 +++- src/UnderlineNav2/examples.stories.tsx | 20 ++++++++++++++ src/UnderlineNav2/styles.ts | 4 ++- 6 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/UnderlineNav2/LoadingCounter.tsx diff --git a/src/UnderlineNav2/LoadingCounter.tsx b/src/UnderlineNav2/LoadingCounter.tsx new file mode 100644 index 00000000000..41f7a4f9099 --- /dev/null +++ b/src/UnderlineNav2/LoadingCounter.tsx @@ -0,0 +1,32 @@ +import React from 'react' +import styled from 'styled-components' + +import {useTheme, Theme} from '../ThemeProvider' + +export const LoadingCounter = () => { + const {theme} = useTheme() + + const getLoaderStyle = (currentTheme?: Theme) => { + return `@keyframes loading { + from { + opacity: 0.4; + } + to { + opacity: 0.8; + } + } + animation: loading 1.2s linear infinite alternate; + background-color: ${currentTheme?.colors.neutral.emphasis}; + border-color: ${currentTheme?.colors.border.default}; + width: 22px; + height: 16px; + display: inline-block; + border-radius: 20px;` + } + + const StyledLoadingCounter = styled.div` + ${getLoaderStyle(theme)} + ` + + return +} diff --git a/src/UnderlineNav2/UnderlineNav.tsx b/src/UnderlineNav2/UnderlineNav.tsx index 50900cc5fa1..7b3d4dc074e 100644 --- a/src/UnderlineNav2/UnderlineNav.tsx +++ b/src/UnderlineNav2/UnderlineNav.tsx @@ -15,6 +15,7 @@ import {ChildWidthArray, ResponsiveProps, OnScrollWithButtonEventType} from './t import {moreBtnStyles, getDividerStyle, getNavStyles, ulStyles, scrollStyles, moreMenuStyles} from './styles' import {LeftArrowButton, RightArrowButton} from './UnderlineNavArrowButton' import styled from 'styled-components' +import {LoadingCounter} from './LoadingCounter' export type UnderlineNavProps = { label: string @@ -22,6 +23,10 @@ export type UnderlineNavProps = { align?: 'right' sx?: SxProp variant?: 'default' | 'small' + /** + * loading state for all counters (to prevent multiple layout shifts) + */ + counterLoading?: boolean afterSelect?: (event: React.MouseEvent | React.KeyboardEvent) => void children: React.ReactNode } @@ -133,7 +138,16 @@ const calculatePossibleItems = (childWidthArray: ChildWidthArray, navWidth: numb export const UnderlineNav = forwardRef( ( - {as = 'nav', align, label, sx: sxProp = {}, afterSelect, variant = 'default', children}: UnderlineNavProps, + { + as = 'nav', + align, + label, + sx: sxProp = {}, + afterSelect, + variant = 'default', + counterLoading = false, + children + }: UnderlineNavProps, forwardedRef ) => { const backupRef = useRef(null) @@ -248,6 +262,7 @@ export const UnderlineNav = forwardRef( setSelectedLink, afterSelect: afterSelectHandler, variant, + counterLoading, iconsVisible }} > @@ -282,7 +297,12 @@ export const UnderlineNav = forwardRef( {actionElementChildren} - {actionElementProps.counter} + + {counterLoading ? ( + + ) : ( + {actionElementProps.counter} + )} ) diff --git a/src/UnderlineNav2/UnderlineNavContext.tsx b/src/UnderlineNav2/UnderlineNavContext.tsx index 6f614aa0864..4f4433d5310 100644 --- a/src/UnderlineNav2/UnderlineNavContext.tsx +++ b/src/UnderlineNav2/UnderlineNavContext.tsx @@ -7,6 +7,7 @@ export const UnderlineNavContext = createContext<{ setSelectedLink: (ref: RefObject) => void afterSelect?: (event: React.MouseEvent | React.KeyboardEvent) => void variant: 'default' | 'small' + counterLoading: boolean iconsVisible: boolean }>({ setChildrenWidth: () => null, @@ -14,5 +15,6 @@ export const UnderlineNavContext = createContext<{ selectedLink: undefined, setSelectedLink: () => null, variant: 'default', + counterLoading: false, iconsVisible: true }) diff --git a/src/UnderlineNav2/UnderlineNavItem.tsx b/src/UnderlineNav2/UnderlineNavItem.tsx index 65df8b14409..4a5deef4403 100644 --- a/src/UnderlineNav2/UnderlineNavItem.tsx +++ b/src/UnderlineNav2/UnderlineNavItem.tsx @@ -7,6 +7,7 @@ import {UnderlineNavContext} from './UnderlineNavContext' import CounterLabel from '../CounterLabel' import {useTheme} from '../ThemeProvider' import {getLinkStyles, wrapperStyles, iconWrapStyles, counterStyles} from './styles' +import {LoadingCounter} from './LoadingCounter' // adopted from React.AnchorHTMLAttributes type LinkProps = { @@ -70,6 +71,7 @@ export const UnderlineNavItem = forwardRef( setSelectedLink, afterSelect, variant, + counterLoading, iconsVisible } = useContext(UnderlineNavContext) const {theme} = useTheme() @@ -109,6 +111,7 @@ export const UnderlineNavItem = forwardRef( }, [onSelect, afterSelect, ref, setSelectedLink] ) + return ( - {counter} + {counterLoading ? : {counter}} )} diff --git a/src/UnderlineNav2/examples.stories.tsx b/src/UnderlineNav2/examples.stories.tsx index 3485f0b8cab..03c30455367 100644 --- a/src/UnderlineNav2/examples.stories.tsx +++ b/src/UnderlineNav2/examples.stories.tsx @@ -103,3 +103,23 @@ export const InternalResponsiveNav = (args: UnderlineNavProps) => { ) } + +export const CounterLoading = (args: UnderlineNavProps) => { + const [selectedIndex, setSelectedIndex] = React.useState(1) + + return ( + + {items.map((item, index) => ( + setSelectedIndex(index)} + counter={item.counter} + > + {item.navigation} + + ))} + + ) +} diff --git a/src/UnderlineNav2/styles.ts b/src/UnderlineNav2/styles.ts index 2754d725f87..dd05e995b25 100644 --- a/src/UnderlineNav2/styles.ts +++ b/src/UnderlineNav2/styles.ts @@ -25,7 +25,9 @@ const defaultVariantLinkStyles = { } export const counterStyles = { - marginLeft: 2 + marginLeft: 2, + display: 'flex', + alignItems: 'center' } export const getNavStyles = (theme?: Theme, props?: Partial>) => ({ From 2e60d63a33d80b11a8183609d4df26282f400556 Mon Sep 17 00:00:00 2001 From: Armagan Ersoz Date: Wed, 28 Sep 2022 12:59:20 +1000 Subject: [PATCH 2/4] CR feedback & changeset & docs and tests --- .changeset/four-cheetahs-hang.md | 5 +++ docs/content/drafts/UnderlineNav2.mdx | 18 ++++++++++ src/UnderlineNav2/LoadingCounter.tsx | 45 +++++++++---------------- src/UnderlineNav2/UnderlineNav.test.tsx | 15 +++++++++ 4 files changed, 53 insertions(+), 30 deletions(-) create mode 100644 .changeset/four-cheetahs-hang.md diff --git a/.changeset/four-cheetahs-hang.md b/.changeset/four-cheetahs-hang.md new file mode 100644 index 00000000000..817c9e03bad --- /dev/null +++ b/.changeset/four-cheetahs-hang.md @@ -0,0 +1,5 @@ +--- +'@primer/react': minor +--- + +UnderlineNav2: Introducing loading states for counters diff --git a/docs/content/drafts/UnderlineNav2.mdx b/docs/content/drafts/UnderlineNav2.mdx index dab8814d770..474971d7bfa 100644 --- a/docs/content/drafts/UnderlineNav2.mdx +++ b/docs/content/drafts/UnderlineNav2.mdx @@ -111,6 +111,18 @@ If there is still overflow, the component will behave depending on the pointer. ``` +### Loading State for Counters + +```jsx live drafts + + + Item 1 + + Item 2 + Item 3 + +``` + ## Props ### UnderlineNav @@ -119,6 +131,12 @@ If there is still overflow, the component will behave depending on the pointer. + { - const {theme} = useTheme() - - const getLoaderStyle = (currentTheme?: Theme) => { - return `@keyframes loading { - from { - opacity: 0.4; - } - to { - opacity: 0.8; - } - } - animation: loading 1.2s linear infinite alternate; - background-color: ${currentTheme?.colors.neutral.emphasis}; - border-color: ${currentTheme?.colors.border.default}; - width: 22px; - height: 16px; - display: inline-block; - border-radius: 20px;` - } - - const StyledLoadingCounter = styled.div` - ${getLoaderStyle(theme)} - ` - - return -} +export const LoadingCounter = styled.span` + animation: ${loading} 1.2s linear infinite alternate; + background-color: ${get('colors.neutral.emphasis')}; + border-color: ${get('colors.border.default')}; + width: 1.5rem; + height: 1rem; // 16px + display: inline-block; + border-radius: 20px; +` diff --git a/src/UnderlineNav2/UnderlineNav.test.tsx b/src/UnderlineNav2/UnderlineNav.test.tsx index c10eb2d266a..b6c98a931c8 100644 --- a/src/UnderlineNav2/UnderlineNav.test.tsx +++ b/src/UnderlineNav2/UnderlineNav.test.tsx @@ -94,4 +94,19 @@ describe('UnderlineNav', () => { expect(counter?.className).toContain('CounterLabel') expect(counter?.textContent).toBe('8') }) + test('respect counterLoading prop', () => { + const {getByText} = render( + + + Item 1 + + Item 2 + Item 3 + + ) + const item = getByText('Item 1').closest('a') + const counterLoading = item?.getElementsByTagName('span')[2] + expect(counterLoading?.className).toContain('LoadingCounter') + expect(counterLoading?.textContent).toBe('') + }) }) From e46955cb1a6c5c0c8cb8a6a7412ad7ed73c0ffdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arma=C4=9Fan?= Date: Thu, 29 Sep 2022 08:44:44 +1000 Subject: [PATCH 3/4] Update docs/content/drafts/UnderlineNav2.mdx Co-authored-by: Cole Bemis --- docs/content/drafts/UnderlineNav2.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/drafts/UnderlineNav2.mdx b/docs/content/drafts/UnderlineNav2.mdx index 474971d7bfa..46f712c675e 100644 --- a/docs/content/drafts/UnderlineNav2.mdx +++ b/docs/content/drafts/UnderlineNav2.mdx @@ -111,7 +111,7 @@ If there is still overflow, the component will behave depending on the pointer. ``` -### Loading State for Counters +### Loading state for counters ```jsx live drafts From 516749aa72b5bca25eea47cab7ab3ffb40320c7b Mon Sep 17 00:00:00 2001 From: Armagan Ersoz Date: Thu, 29 Sep 2022 09:20:17 +1000 Subject: [PATCH 4/4] prop rename to loadingCounters --- docs/content/drafts/UnderlineNav2.mdx | 12 ++++++------ src/UnderlineNav2/UnderlineNav.test.tsx | 10 +++++----- src/UnderlineNav2/UnderlineNav.tsx | 8 ++++---- src/UnderlineNav2/UnderlineNavContext.tsx | 4 ++-- src/UnderlineNav2/UnderlineNavItem.tsx | 4 ++-- src/UnderlineNav2/examples.stories.tsx | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/content/drafts/UnderlineNav2.mdx b/docs/content/drafts/UnderlineNav2.mdx index 46f712c675e..e66ede78c2a 100644 --- a/docs/content/drafts/UnderlineNav2.mdx +++ b/docs/content/drafts/UnderlineNav2.mdx @@ -23,7 +23,7 @@ import {UnderlineNav} from '@primer/react/drafts' ``` -### With icons +### With Icons ```jsx live drafts @@ -50,7 +50,7 @@ import {UnderlineNav} from '@primer/react/drafts' When overflow occurs, the component first hides icons if present to optimize for space and show as many items as possible. (Only for fine pointer devices) -#### Items without Icons +#### Items Without Icons ```jsx live drafts @@ -78,7 +78,7 @@ When overflow occurs, the component first hides icons if present to optimize for ``` -#### Display `More` menu +#### Display `More` Menu If there is still overflow, the component will behave depending on the pointer. @@ -114,7 +114,7 @@ If there is still overflow, the component will behave depending on the pointer. ### Loading state for counters ```jsx live drafts - + Item 1 @@ -132,10 +132,10 @@ If there is still overflow, the component will behave depending on the pointer. { expect(counter?.className).toContain('CounterLabel') expect(counter?.textContent).toBe('8') }) - test('respect counterLoading prop', () => { + test('respect loadingCounters prop', () => { const {getByText} = render( - + Item 1 @@ -105,8 +105,8 @@ describe('UnderlineNav', () => { ) const item = getByText('Item 1').closest('a') - const counterLoading = item?.getElementsByTagName('span')[2] - expect(counterLoading?.className).toContain('LoadingCounter') - expect(counterLoading?.textContent).toBe('') + const loadingCounter = item?.getElementsByTagName('span')[2] + expect(loadingCounter?.className).toContain('LoadingCounter') + expect(loadingCounter?.textContent).toBe('') }) }) diff --git a/src/UnderlineNav2/UnderlineNav.tsx b/src/UnderlineNav2/UnderlineNav.tsx index 7b3d4dc074e..23e3aee5091 100644 --- a/src/UnderlineNav2/UnderlineNav.tsx +++ b/src/UnderlineNav2/UnderlineNav.tsx @@ -26,7 +26,7 @@ export type UnderlineNavProps = { /** * loading state for all counters (to prevent multiple layout shifts) */ - counterLoading?: boolean + loadingCounters?: boolean afterSelect?: (event: React.MouseEvent | React.KeyboardEvent) => void children: React.ReactNode } @@ -145,7 +145,7 @@ export const UnderlineNav = forwardRef( sx: sxProp = {}, afterSelect, variant = 'default', - counterLoading = false, + loadingCounters = false, children }: UnderlineNavProps, forwardedRef @@ -262,7 +262,7 @@ export const UnderlineNav = forwardRef( setSelectedLink, afterSelect: afterSelectHandler, variant, - counterLoading, + loadingCounters, iconsVisible }} > @@ -298,7 +298,7 @@ export const UnderlineNav = forwardRef( {actionElementChildren} - {counterLoading ? ( + {loadingCounters ? ( ) : ( {actionElementProps.counter} diff --git a/src/UnderlineNav2/UnderlineNavContext.tsx b/src/UnderlineNav2/UnderlineNavContext.tsx index 4f4433d5310..9e810efc652 100644 --- a/src/UnderlineNav2/UnderlineNavContext.tsx +++ b/src/UnderlineNav2/UnderlineNavContext.tsx @@ -7,7 +7,7 @@ export const UnderlineNavContext = createContext<{ setSelectedLink: (ref: RefObject) => void afterSelect?: (event: React.MouseEvent | React.KeyboardEvent) => void variant: 'default' | 'small' - counterLoading: boolean + loadingCounters: boolean iconsVisible: boolean }>({ setChildrenWidth: () => null, @@ -15,6 +15,6 @@ export const UnderlineNavContext = createContext<{ selectedLink: undefined, setSelectedLink: () => null, variant: 'default', - counterLoading: false, + loadingCounters: false, iconsVisible: true }) diff --git a/src/UnderlineNav2/UnderlineNavItem.tsx b/src/UnderlineNav2/UnderlineNavItem.tsx index 4a5deef4403..3e50524f315 100644 --- a/src/UnderlineNav2/UnderlineNavItem.tsx +++ b/src/UnderlineNav2/UnderlineNavItem.tsx @@ -71,7 +71,7 @@ export const UnderlineNavItem = forwardRef( setSelectedLink, afterSelect, variant, - counterLoading, + loadingCounters, iconsVisible } = useContext(UnderlineNavContext) const {theme} = useTheme() @@ -142,7 +142,7 @@ export const UnderlineNavItem = forwardRef( )} {counter && ( - {counterLoading ? : {counter}} + {loadingCounters ? : {counter}} )} diff --git a/src/UnderlineNav2/examples.stories.tsx b/src/UnderlineNav2/examples.stories.tsx index 03c30455367..e0394ed8de6 100644 --- a/src/UnderlineNav2/examples.stories.tsx +++ b/src/UnderlineNav2/examples.stories.tsx @@ -104,11 +104,11 @@ export const InternalResponsiveNav = (args: UnderlineNavProps) => { ) } -export const CounterLoading = (args: UnderlineNavProps) => { +export const CountersLoadingState = (args: UnderlineNavProps) => { const [selectedIndex, setSelectedIndex] = React.useState(1) return ( - + {items.map((item, index) => (