Skip to content

Commit 161e3fd

Browse files
authoredDec 11, 2024··
feat(UnderlinePanels): Convert UnderlinePanels to CSS modules behind team feature flag (#5357)
* convert UnderlineItem * convert item list * additional css module migration * formatting * fix selectors * Migrate Loading Counter * key off of FF * add toggle for the tabContainer * update to take additional optional dependencies * update resize observer to key off of feature flag * fix lint issue
1 parent 5a8138a commit 161e3fd

File tree

6 files changed

+440
-128
lines changed

6 files changed

+440
-128
lines changed
 

‎.changeset/slimy-chefs-divide.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
Convert UnderlinePanels to CSS modules behind feature flags
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.StyledUnderlineWrapper {
2+
width: 100%;
3+
overflow-x: auto;
4+
overflow-y: hidden;
5+
-webkit-overflow-scrolling: auto;
6+
}
7+
8+
.StyledUnderlineWrapper[data-icons-visible='false'] [data-component='icon'] {
9+
display: none;
10+
}

‎packages/react/src/experimental/UnderlinePanels/UnderlinePanels.tsx

+49-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {Children, isValidElement, cloneElement, useState, useRef, type FC, type PropsWithChildren} from 'react'
22
import {TabContainerElement} from '@github/tab-container-element'
3+
import type {IconProps} from '@primer/octicons-react'
34
import {createComponent} from '../../utils/create-component'
45
import {
56
StyledUnderlineItemList,
@@ -10,11 +11,15 @@ import {
1011
import Box, {type BoxProps} from '../../Box'
1112
import {useId} from '../../hooks'
1213
import {invariant} from '../../utils/invariant'
13-
import type {IconProps} from '@primer/octicons-react'
1414
import {merge, type BetterSystemStyleObject, type SxProp} from '../../sx'
1515
import {defaultSxProp} from '../../utils/defaultSxProp'
1616
import {useResizeObserver, type ResizeObserverEntry} from '../../hooks/useResizeObserver'
1717
import useIsomorphicLayoutEffect from '../../utils/useIsomorphicLayoutEffect'
18+
import {useFeatureFlag} from '../../FeatureFlags'
19+
import classes from './UnderlinePanels.module.css'
20+
import {toggleStyledComponent} from '../../internal/utils/toggleStyledComponent'
21+
22+
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'
1823

1924
export type UnderlinePanelsProps = {
2025
/**
@@ -59,6 +64,12 @@ export type PanelProps = Omit<BoxProps, 'as'>
5964

6065
const TabContainerComponent = createComponent(TabContainerElement, 'tab-container')
6166

67+
const StyledTabContainerComponent = toggleStyledComponent(
68+
CSS_MODULES_FEATURE_FLAG,
69+
'tab-container',
70+
TabContainerComponent,
71+
)
72+
6273
const UnderlinePanels: FC<UnderlinePanelsProps> = ({
6374
'aria-label': ariaLabel,
6475
'aria-labelledby': ariaLabelledBy,
@@ -102,6 +113,8 @@ const UnderlinePanels: FC<UnderlinePanelsProps> = ({
102113
)
103114
const tabsHaveIcons = tabs.current.some(tab => React.isValidElement(tab) && tab.props.icon)
104115

116+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
117+
105118
// this is a workaround to get the list's width on the first render
106119
const [listWidth, setListWidth] = useState(0)
107120
useIsomorphicLayoutEffect(() => {
@@ -114,15 +127,19 @@ const UnderlinePanels: FC<UnderlinePanelsProps> = ({
114127

115128
// when the wrapper resizes, check if the icons should be visible
116129
// by comparing the wrapper width to the list width
117-
useResizeObserver((resizeObserverEntries: ResizeObserverEntry[]) => {
118-
if (!tabsHaveIcons) {
119-
return
120-
}
121-
122-
const wrapperWidth = resizeObserverEntries[0].contentRect.width
123-
124-
setIconsVisible(wrapperWidth > listWidth)
125-
}, wrapperRef)
130+
useResizeObserver(
131+
(resizeObserverEntries: ResizeObserverEntry[]) => {
132+
if (!tabsHaveIcons) {
133+
return
134+
}
135+
136+
const wrapperWidth = resizeObserverEntries[0].contentRect.width
137+
138+
setIconsVisible(wrapperWidth > listWidth)
139+
},
140+
wrapperRef,
141+
[enabled],
142+
)
126143

127144
if (__DEV__) {
128145
// only one tab can be selected at a time
@@ -141,8 +158,28 @@ const UnderlinePanels: FC<UnderlinePanelsProps> = ({
141158
)
142159
}
143160

161+
if (enabled) {
162+
return (
163+
<StyledTabContainerComponent>
164+
<StyledUnderlineWrapper
165+
ref={wrapperRef}
166+
slot="tablist-wrapper"
167+
data-icons-visible={iconsVisible}
168+
sx={sxProp}
169+
className={classes.StyledUnderlineWrapper}
170+
{...props}
171+
>
172+
<StyledUnderlineItemList ref={listRef} aria-label={ariaLabel} aria-labelledby={ariaLabelledBy} role="tablist">
173+
{tabs.current}
174+
</StyledUnderlineItemList>
175+
</StyledUnderlineWrapper>
176+
{tabPanels.current}
177+
</StyledTabContainerComponent>
178+
)
179+
}
180+
144181
return (
145-
<TabContainerComponent>
182+
<StyledTabContainerComponent>
146183
<StyledUnderlineWrapper
147184
ref={wrapperRef}
148185
slot="tablist-wrapper"
@@ -166,7 +203,7 @@ const UnderlinePanels: FC<UnderlinePanelsProps> = ({
166203
</StyledUnderlineItemList>
167204
</StyledUnderlineWrapper>
168205
{tabPanels.current}
169-
</TabContainerComponent>
206+
</StyledTabContainerComponent>
170207
)
171208
}
172209

‎packages/react/src/hooks/useResizeObserver.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ export interface ResizeObserverEntry {
99
contentRect: DOMRectReadOnly
1010
}
1111

12-
export function useResizeObserver<T extends HTMLElement>(callback: ResizeObserverCallback, target?: RefObject<T>) {
12+
export function useResizeObserver<T extends HTMLElement>(
13+
callback: ResizeObserverCallback,
14+
target?: RefObject<T>,
15+
depsArray: unknown[] = [],
16+
) {
1317
const savedCallback = useRef(callback)
1418

1519
useLayoutEffect(() => {
@@ -31,5 +35,6 @@ export function useResizeObserver<T extends HTMLElement>(callback: ResizeObserve
3135
return () => {
3236
observer.disconnect()
3337
}
34-
}, [target])
38+
// eslint-disable-next-line react-hooks/exhaustive-deps
39+
}, [target, ...depsArray])
3540
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
.UnderlineWrapper {
2+
display: flex;
3+
/* stylelint-disable-next-line primer/spacing */
4+
padding-inline: var(--stack-padding-normal);
5+
justify-content: flex-start;
6+
align-items: center;
7+
8+
/* make space for the underline */
9+
min-height: var(--control-xlarge-size, 48px);
10+
11+
/* using a box-shadow instead of a border to accomodate 'overflow-y: hidden' on UnderlinePanels */
12+
/* stylelint-disable-next-line primer/box-shadow */
13+
box-shadow: inset 0 -1px var(--borderColor-muted);
14+
}
15+
16+
.UnderlineItemList {
17+
position: relative;
18+
display: flex;
19+
padding: 0;
20+
margin: 0;
21+
white-space: nowrap;
22+
list-style: none;
23+
align-items: center;
24+
gap: 8px;
25+
}
26+
27+
.UnderlineItem {
28+
/* underline tab specific styles */
29+
position: relative;
30+
display: inline-flex;
31+
font: inherit;
32+
font-size: var(--text-body-size-medium);
33+
line-height: var(--text-body-lineHeight-medium, 1.4285);
34+
color: var(--fgColor-default);
35+
text-align: center;
36+
text-decoration: none;
37+
cursor: pointer;
38+
background-color: transparent;
39+
border: 0;
40+
border-radius: var(--borderRadius-medium, var(--borderRadius-small));
41+
42+
/* button resets */
43+
appearance: none;
44+
padding-inline: var(--base-size-8);
45+
padding-block: var(--base-size-6);
46+
align-items: center;
47+
48+
@media (hover: hover) {
49+
&:hover {
50+
text-decoration: none;
51+
background-color: var(--bgColor-neutral-muted);
52+
transition: background-color 0.12s ease-out;
53+
}
54+
}
55+
}
56+
57+
.UnderlineItem:focus {
58+
outline: 2px solid transparent;
59+
/* stylelint-disable-next-line primer/box-shadow */
60+
box-shadow: inset 0 0 0 2px var(--fgColor-accent);
61+
62+
/* where focus-visible is supported, remove the focus box-shadow */
63+
&:not(:focus-visible) {
64+
box-shadow: none;
65+
}
66+
}
67+
68+
.UnderlineItem:focus-visible {
69+
outline: 2px solid transparent;
70+
/* stylelint-disable-next-line primer/box-shadow */
71+
box-shadow: inset 0 0 0 2px var(--fgColor-accent);
72+
}
73+
74+
/* renders a visibly hidden "copy" of the label in bold, reserving box space for when label becomes bold on selected */
75+
.UnderlineItem [data-content]::before {
76+
display: block;
77+
height: 0;
78+
font-weight: var(--base-text-weight-semibold);
79+
white-space: nowrap;
80+
visibility: hidden;
81+
content: attr(data-content);
82+
}
83+
84+
.UnderlineItem [data-component='icon'] {
85+
display: inline-flex;
86+
color: var(--fgColor-muted);
87+
align-items: center;
88+
margin-inline-end: var(--base-size-8);
89+
}
90+
91+
.UnderlineItem [data-component='counter'] {
92+
margin-inline-start: var(--base-size-8);
93+
display: flex;
94+
align-items: center;
95+
}
96+
97+
.UnderlineItem::after {
98+
position: absolute;
99+
right: 50%;
100+
101+
/* TODO: see if we can simplify this positioning */
102+
103+
/* 48px total height / 2 (24px) + 1px */
104+
/* stylelint-disable-next-line primer/spacing */
105+
bottom: calc(50% - calc(var(--control-xlarge-size, var(--base-size-48)) / 2 + 1px));
106+
width: 100%;
107+
height: 2px;
108+
content: '';
109+
background-color: transparent;
110+
border-radius: 0;
111+
transform: translate(50%, -50%);
112+
}
113+
114+
.UnderlineItem[aria-current]:not([aria-current='false']) [data-component='text'],
115+
.UnderlineItem[aria-selected='true'] [data-component='text'] {
116+
font-weight: var(--base-text-weight-semibold);
117+
}
118+
119+
.UnderlineItem[aria-current]:not([aria-current='false'])::after,
120+
.UnderlineItem[aria-selected='true']::after {
121+
/* stylelint-disable-next-line primer/colors */
122+
background-color: var(--underlineNav-borderColor-active, var(--color-primer-border-active, #fd8c73));
123+
124+
@media (forced-colors: active) {
125+
/* Support for Window Force Color Mode https://learn.microsoft.com/en-us/fluent-ui/web-components/design-system/high-contrast */
126+
background-color: LinkText;
127+
}
128+
}
129+
130+
.LoadingCounter {
131+
display: inline-block;
132+
width: 1.5rem;
133+
height: 1rem; /* 16px */
134+
background-color: var(--bgColor-neutral-muted);
135+
border-color: var(--borderColor-default);
136+
/* stylelint-disable-next-line primer/borders */
137+
border-radius: 20px;
138+
animation: loadingCounterKeyFrames 1.2s ease-in-out infinite alternate;
139+
}
140+
141+
@keyframes loadingCounterKeyFrames {
142+
from {
143+
opacity: 1;
144+
}
145+
146+
to {
147+
opacity: 0.2;
148+
}
149+
}

‎packages/react/src/internal/components/UnderlineTabbedInterface.tsx

+220-114
Original file line numberDiff line numberDiff line change
@@ -9,153 +9,233 @@ import sx, {type SxProp} from '../../sx'
99
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../../utils/polymorphic'
1010
import {defaultSxProp} from '../../utils/defaultSxProp'
1111
import {get} from '../../constants'
12+
import {toggleStyledComponent} from '../utils/toggleStyledComponent'
13+
14+
import classes from './UnderlineTabbedInterface.module.css'
15+
import {useFeatureFlag} from '../../FeatureFlags'
16+
import {clsx} from 'clsx'
1217

1318
// The gap between the list items. It is a constant because the gap is used to calculate the possible number of items that can fit in the container.
1419
export const GAP = 8
1520

16-
export const StyledUnderlineWrapper = styled.div`
17-
display: flex;
18-
padding-inline: var(--stack-padding-normal, ${get('space.3')});
19-
justify-content: flex-start;
20-
align-items: center;
21-
/* make space for the underline */
22-
min-height: var(--control-xlarge-size, 48px);
23-
/* using a box-shadow instead of a border to accomodate 'overflow-y: hidden' on UnderlinePanels */
24-
box-shadow: inset 0px -1px var(--borderColor-muted, ${get('colors.border.muted')});
25-
26-
${sx};
27-
`
21+
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'
2822

29-
export const StyledUnderlineItemList = styled.ul`
30-
display: flex;
31-
list-style: none;
32-
white-space: nowrap;
33-
padding: 0;
34-
margin: 0;
35-
align-items: center;
36-
gap: ${GAP}px;
37-
position: relative;
38-
`
23+
const StyledComponentUnderlineWrapper = toggleStyledComponent(
24+
CSS_MODULES_FEATURE_FLAG,
25+
'div',
26+
styled.div`
27+
display: flex;
28+
padding-inline: var(--stack-padding-normal, ${get('space.3')});
29+
justify-content: flex-start;
30+
align-items: center;
31+
/* make space for the underline */
32+
min-height: var(--control-xlarge-size, 48px);
33+
/* using a box-shadow instead of a border to accomodate 'overflow-y: hidden' on UnderlinePanels */
34+
box-shadow: inset 0px -1px var(--borderColor-muted, ${get('colors.border.muted')});
3935
40-
export const StyledUnderlineItem = styled.div`
41-
/* button resets */
42-
appearance: none;
43-
background-color: transparent;
44-
border: 0;
45-
cursor: pointer;
46-
font: inherit;
47-
48-
/* underline tab specific styles */
49-
position: relative;
50-
display: inline-flex;
51-
color: ${get('colors.fg.default')};
52-
text-align: center;
53-
text-decoration: none;
54-
line-height: var(--text-body-lineHeight-medium, 1.4285);
55-
border-radius: var(--borderRadius-medium, ${get('radii.2')});
56-
font-size: var(--text-body-size-medium, ${get('fontSizes.1')});
57-
padding-inline: var(--control-medium-paddingInline-condensed, ${get('space.2')});
58-
padding-block: var(--control-medium-paddingBlock, 6px);
59-
align-items: center;
60-
61-
@media (hover: hover) {
62-
&:hover {
63-
background-color: var(--bgColor-neutral-muted, ${get('colors.neutral.subtle')});
64-
transition: background 0.12s ease-out;
65-
text-decoration: none;
66-
}
67-
}
36+
${sx};
37+
`,
38+
)
6839

69-
&:focus: {
70-
outline: 2px solid transparent;
71-
box-shadow: inset 0 0 0 2px var(--fgColor-accent, ${get('colors.accent.fg')});
40+
type StyledUnderlineWrapperProps = {
41+
slot?: string
42+
as?: React.ElementType
43+
className?: string
44+
} & SxProp
7245

73-
/* where focus-visible is supported, remove the focus box-shadow */
74-
&:not(:focus-visible) {
75-
box-shadow: none;
76-
}
77-
}
46+
export const StyledUnderlineWrapper = forwardRef(
47+
({children, className, ...rest}: PropsWithChildren<StyledUnderlineWrapperProps>, forwardedRef) => {
48+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
7849

79-
&:focus-visible {
80-
outline: 2px solid transparent;
81-
box-shadow: inset 0 0 0 2px var(--fgColor-accent, ${get('colors.accent.fg')});
82-
}
50+
if (enabled) {
51+
return (
52+
<StyledComponentUnderlineWrapper
53+
className={clsx(classes.UnderlineWrapper, className)}
54+
ref={forwardedRef}
55+
{...rest}
56+
>
57+
{children}
58+
</StyledComponentUnderlineWrapper>
59+
)
60+
}
61+
return (
62+
<StyledComponentUnderlineWrapper className={className} ref={forwardedRef} {...rest}>
63+
{children}
64+
</StyledComponentUnderlineWrapper>
65+
)
66+
},
67+
)
8368

84-
/* renders a visibly hidden "copy" of the label in bold, reserving box space for when label becomes bold on selected */
85-
[data-content]::before {
86-
content: attr(data-content);
87-
display: block;
88-
height: 0;
89-
font-weight: var(--base-text-weight-semibold, ${get('fontWeights.semibold')});
90-
visibility: hidden;
69+
const StyledComponentUnderlineItemList = toggleStyledComponent(
70+
CSS_MODULES_FEATURE_FLAG,
71+
'ul',
72+
styled.ul`
73+
display: flex;
74+
list-style: none;
9175
white-space: nowrap;
92-
}
93-
94-
[data-component='icon'] {
95-
color: var(--fgColor-muted, ${get('colors.fg.muted')});
76+
padding: 0;
77+
margin: 0;
9678
align-items: center;
97-
display: inline-flex;
98-
margin-inline-end: var(--control-medium-gap, ${get('space.2')});
99-
}
79+
gap: ${GAP}px;
80+
position: relative;
81+
`,
82+
)
10083

101-
[data-component='counter'] {
102-
margin-inline-start: var(--control-medium-gap, ${get('space.2')});
103-
display: flex;
104-
align-items: center;
84+
export const StyledUnderlineItemList = forwardRef(({children, ...rest}: PropsWithChildren, forwardedRef) => {
85+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
86+
87+
if (enabled) {
88+
return (
89+
<StyledComponentUnderlineItemList className={classes.UnderlineItemList} ref={forwardedRef} {...rest}>
90+
{children}
91+
</StyledComponentUnderlineItemList>
92+
)
10593
}
10694

107-
/* selected state styles */
108-
&::after {
109-
position: absolute;
110-
right: 50%;
111-
/* TODO: see if we can simplify this positioning */
112-
/* 48px total height / 2 (24px) + 1px */
113-
bottom: calc(50% - calc(var(--control-xlarge-size, 48px) / 2 + 1px));
114-
width: 100%;
115-
height: 2px;
116-
content: '';
95+
return (
96+
<StyledComponentUnderlineItemList ref={forwardedRef} {...rest}>
97+
{children}
98+
</StyledComponentUnderlineItemList>
99+
)
100+
}) as PolymorphicForwardRefComponent<'ul'>
101+
102+
export const StyledUnderlineItem = toggleStyledComponent(
103+
CSS_MODULES_FEATURE_FLAG,
104+
'div',
105+
styled.div`
106+
/* button resets */
107+
appearance: none;
117108
background-color: transparent;
118-
border-radius: 0;
119-
transform: translate(50%, -50%);
120-
}
109+
border: 0;
110+
cursor: pointer;
111+
font: inherit;
112+
113+
/* underline tab specific styles */
114+
position: relative;
115+
display: inline-flex;
116+
color: ${get('colors.fg.default')};
117+
text-align: center;
118+
text-decoration: none;
119+
line-height: var(--text-body-lineHeight-medium, 1.4285);
120+
border-radius: var(--borderRadius-medium, ${get('radii.2')});
121+
font-size: var(--text-body-size-medium, ${get('fontSizes.1')});
122+
padding-inline: var(--control-medium-paddingInline-condensed, ${get('space.2')});
123+
padding-block: var(--control-medium-paddingBlock, 6px);
124+
align-items: center;
121125
122-
&[aria-current]:not([aria-current='false']),
123-
&[aria-selected='true'] {
124-
[data-component='text'] {
126+
@media (hover: hover) {
127+
&:hover {
128+
background-color: var(--bgColor-neutral-muted, ${get('colors.neutral.subtle')});
129+
transition: background 0.12s ease-out;
130+
text-decoration: none;
131+
}
132+
}
133+
134+
&:focus: {
135+
outline: 2px solid transparent;
136+
box-shadow: inset 0 0 0 2px var(--fgColor-accent, ${get('colors.accent.fg')});
137+
138+
/* where focus-visible is supported, remove the focus box-shadow */
139+
&:not(:focus-visible) {
140+
box-shadow: none;
141+
}
142+
}
143+
144+
&:focus-visible {
145+
outline: 2px solid transparent;
146+
box-shadow: inset 0 0 0 2px var(--fgColor-accent, ${get('colors.accent.fg')});
147+
}
148+
149+
/* renders a visibly hidden "copy" of the label in bold, reserving box space for when label becomes bold on selected */
150+
[data-content]::before {
151+
content: attr(data-content);
152+
display: block;
153+
height: 0;
125154
font-weight: var(--base-text-weight-semibold, ${get('fontWeights.semibold')});
155+
visibility: hidden;
156+
white-space: nowrap;
157+
}
158+
159+
[data-component='icon'] {
160+
color: var(--fgColor-muted, ${get('colors.fg.muted')});
161+
align-items: center;
162+
display: inline-flex;
163+
margin-inline-end: var(--control-medium-gap, ${get('space.2')});
126164
}
127165
166+
[data-component='counter'] {
167+
margin-inline-start: var(--control-medium-gap, ${get('space.2')});
168+
display: flex;
169+
align-items: center;
170+
}
171+
172+
/* selected state styles */
128173
&::after {
129-
background-color: var(--underlineNav-borderColor-active, var(--color-primer-border-active, #fd8c73));
174+
position: absolute;
175+
right: 50%;
176+
/* TODO: see if we can simplify this positioning */
177+
/* 48px total height / 2 (24px) + 1px */
178+
bottom: calc(50% - calc(var(--control-xlarge-size, 48px) / 2 + 1px));
179+
width: 100%;
180+
height: 2px;
181+
content: '';
182+
background-color: transparent;
183+
border-radius: 0;
184+
transform: translate(50%, -50%);
130185
}
131-
}
132186
133-
@media (forced-colors: active) {
134187
&[aria-current]:not([aria-current='false']),
135188
&[aria-selected='true'] {
136-
::after {
137-
// Support for Window Force Color Mode https://learn.microsoft.com/en-us/fluent-ui/web-components/design-system/high-contrast
138-
background-color: LinkText;
189+
[data-component='text'] {
190+
font-weight: var(--base-text-weight-semibold, ${get('fontWeights.semibold')});
191+
}
192+
193+
&::after {
194+
background-color: var(--underlineNav-borderColor-active, var(--color-primer-border-active, #fd8c73));
139195
}
140196
}
141-
}
142-
${sx};
143-
`
197+
198+
@media (forced-colors: active) {
199+
&[aria-current]:not([aria-current='false']),
200+
&[aria-selected='true'] {
201+
::after {
202+
// Support for Window Force Color Mode https://learn.microsoft.com/en-us/fluent-ui/web-components/design-system/high-contrast
203+
background-color: LinkText;
204+
}
205+
}
206+
}
207+
${sx};
208+
`,
209+
)
144210

145211
const loadingKeyframes = keyframes`
146212
from { opacity: 1; }
147213
to { opacity: 0.2; }
148214
`
149215

150-
export const LoadingCounter = styled.span`
151-
animation: ${loadingKeyframes} 1.2s ease-in-out infinite alternate;
152-
background-color: var(--bgColor-neutral-muted, ${get('colors.neutral.subtle')});
153-
border-color: var(--borderColor-default, ${get('colors.border.default')});
154-
width: 1.5rem;
155-
height: 1rem; /*16px*/
156-
display: inline-block;
157-
border-radius: 20px;
158-
`
216+
export const StyledComponentLoadingCounter = toggleStyledComponent(
217+
CSS_MODULES_FEATURE_FLAG,
218+
'span',
219+
styled.span`
220+
animation: ${loadingKeyframes} 1.2s ease-in-out infinite alternate;
221+
background-color: var(--bgColor-neutral-muted, ${get('colors.neutral.subtle')});
222+
border-color: var(--borderColor-default, ${get('colors.border.default')});
223+
width: 1.5rem;
224+
height: 1rem; /*16px*/
225+
display: inline-block;
226+
border-radius: 20px;
227+
`,
228+
)
229+
230+
export const LoadingCounter = () => {
231+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
232+
233+
if (enabled) {
234+
return <StyledComponentLoadingCounter className={classes.LoadingCounter} />
235+
}
236+
237+
return <StyledComponentLoadingCounter />
238+
}
159239

160240
// We can uncomment these when/if we add overflow behavior
161241
// to the UnderlinePanels component
@@ -212,6 +292,32 @@ export const UnderlineItem = forwardRef(
212292
}: PropsWithChildren<UnderlineItemProps>,
213293
forwardedRef,
214294
) => {
295+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
296+
297+
if (enabled) {
298+
return (
299+
<StyledUnderlineItem ref={forwardedRef} as={as} sx={sxProp} className={classes.UnderlineItem} {...rest}>
300+
{iconsVisible && Icon && <span data-component="icon">{isElement(Icon) ? Icon : <Icon />}</span>}
301+
{children && (
302+
<span data-component="text" data-content={children}>
303+
{children}
304+
</span>
305+
)}
306+
{counter !== undefined ? (
307+
loadingCounters ? (
308+
<span data-component="counter">
309+
<LoadingCounter />
310+
</span>
311+
) : (
312+
<span data-component="counter">
313+
<CounterLabel>{counter}</CounterLabel>
314+
</span>
315+
)
316+
) : null}
317+
</StyledUnderlineItem>
318+
)
319+
}
320+
215321
return (
216322
<StyledUnderlineItem ref={forwardedRef} as={as} sx={sxProp} {...rest}>
217323
{iconsVisible && Icon && <span data-component="icon">{isElement(Icon) ? Icon : <Icon />}</span>}

0 commit comments

Comments
 (0)
Please sign in to comment.