Skip to content

Commit 2da7336

Browse files
jonrohankeithamus
andauthored
feat(Tooltip): Convert Tooltip to CSS modules behind team flag (#5228)
* Convert Tooltip to CSS modules behind team flag * Create lazy-mugs-care.md * Moving the animation out and fixing the font Co-authored-by: Keith Cirkel <keithamus@users.noreply.github.com> * Fix types * Lint --------- Co-authored-by: Keith Cirkel <keithamus@users.noreply.github.com>
1 parent f743717 commit 2da7336

File tree

3 files changed

+235
-99
lines changed

3 files changed

+235
-99
lines changed

.changeset/lazy-mugs-care.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@primer/react": minor
3+
---
4+
5+
feat(Tooltip): Convert Tooltip to CSS modules behind team flag
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/* Animation definition */
2+
@keyframes tooltip-appear {
3+
from {
4+
opacity: 0;
5+
}
6+
7+
to {
8+
opacity: 1;
9+
}
10+
}
11+
12+
.Tooltip {
13+
/* Overriding the default popover styles */
14+
display: none;
15+
16+
&[popover] {
17+
position: absolute;
18+
width: max-content;
19+
max-width: 250px;
20+
/* stylelint-disable-next-line primer/spacing */
21+
padding: 0.5em 0.75em;
22+
margin: auto;
23+
24+
/* for scrollbar */
25+
overflow: visible;
26+
clip: auto;
27+
/* stylelint-disable-next-line primer/typography */
28+
font: normal normal 11px/1.5 var(--fontStack-system);
29+
color: var(--tooltip-fgColor);
30+
text-align: center;
31+
word-wrap: break-word;
32+
white-space: normal;
33+
background: var(--tooltip-bgColor);
34+
border: 0;
35+
border-radius: var(--borderRadius-medium);
36+
opacity: 0;
37+
-webkit-font-smoothing: subpixel-antialiased;
38+
inset: auto;
39+
}
40+
41+
/* class name in chrome is :popover-open */
42+
&[popover]:popover-open {
43+
display: block;
44+
}
45+
46+
/* class name in firefox and safari is \:popover-open */
47+
&[popover].\\:popover-open {
48+
display: block;
49+
}
50+
51+
@media (forced-colors: active) {
52+
outline: 1px solid transparent;
53+
}
54+
55+
/* This is needed to keep the tooltip open when the user leaves the trigger element to hover tooltip */
56+
&::after {
57+
position: absolute;
58+
right: 0;
59+
left: 0;
60+
display: block;
61+
height: var(--overlay-offset);
62+
content: '';
63+
}
64+
65+
/* South, East, Southeast, Southwest after */
66+
&[data-direction='n']::after,
67+
&[data-direction='ne']::after,
68+
&[data-direction='nw']::after {
69+
top: 100%;
70+
}
71+
72+
&[data-direction='s']::after,
73+
&[data-direction='se']::after,
74+
&[data-direction='sw']::after {
75+
bottom: 100%;
76+
}
77+
78+
&[data-direction='w']::after {
79+
position: absolute;
80+
bottom: 0;
81+
left: 100%;
82+
display: block;
83+
width: 8px;
84+
height: 100%;
85+
content: '';
86+
}
87+
88+
/* East before and after */
89+
&[data-direction='e']::after {
90+
position: absolute;
91+
right: 100%;
92+
bottom: 0;
93+
display: block;
94+
width: 8px;
95+
height: 100%;
96+
/* stylelint-disable-next-line primer/spacing */
97+
margin-left: -8px;
98+
content: '';
99+
}
100+
101+
/* Animation styles */
102+
&:popover-open,
103+
&:popover-open::before {
104+
animation-name: tooltip-appear;
105+
animation-duration: 0.1s;
106+
animation-fill-mode: forwards;
107+
animation-timing-function: ease-in;
108+
animation-delay: 0s;
109+
}
110+
111+
/* Animation styles */
112+
&.\\:popover-open,
113+
&.\\:popover-open::before {
114+
animation-name: tooltip-appear;
115+
animation-duration: 0.1s;
116+
animation-fill-mode: forwards;
117+
animation-timing-function: ease-in;
118+
animation-delay: 0s;
119+
}
120+
}

packages/react/src/TooltipV2/Tooltip.tsx

Lines changed: 110 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ import {invariant} from '../utils/invariant'
66
import {warning} from '../utils/warning'
77
import styled from 'styled-components'
88
import {get} from '../constants'
9-
import type {ComponentProps} from '../utils/types'
109
import {getAnchoredPosition} from '@primer/behaviors'
1110
import type {AnchorSide, AnchorAlignment} from '@primer/behaviors'
1211
import {isSupported, apply} from '@oddbird/popover-polyfill/fn'
12+
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
13+
import {clsx} from 'clsx'
14+
import classes from './Tooltip.module.css'
15+
import {useFeatureFlag} from '../FeatureFlags'
16+
17+
const CSS_MODULE_FEATURE_FLAG = 'primer_react_css_modules_team'
1318

1419
const animationStyles = `
1520
animation-name: tooltip-appear;
@@ -19,119 +24,123 @@ const animationStyles = `
1924
animation-delay: 0s;
2025
`
2126

22-
const StyledTooltip = styled.span`
23-
/* Overriding the default popover styles */
24-
display: none;
25-
&[popover] {
26-
position: absolute;
27-
padding: 0.5em 0.75em;
28-
width: max-content;
29-
margin: auto;
30-
clip: auto;
31-
white-space: normal;
32-
font: normal normal 11px/1.5 ${get('fonts.normal')};
33-
-webkit-font-smoothing: subpixel-antialiased;
34-
color: var(--tooltip-fgColor, ${get('colors.fg.onEmphasis')});
35-
text-align: center;
36-
word-wrap: break-word;
37-
background: var(--tooltip-bgColor, ${get('colors.neutral.emphasisPlus')});
38-
border-radius: ${get('radii.2')};
39-
border: 0;
40-
opacity: 0;
41-
max-width: 250px;
42-
inset: auto;
43-
/* for scrollbar */
44-
overflow: visible;
45-
}
46-
/* class name in chrome is :popover-open */
47-
&[popover]:popover-open {
48-
display: block;
49-
}
50-
/* class name in firefox and safari is \:popover-open */
51-
&[popover].\\:popover-open {
52-
display: block;
53-
}
27+
const StyledTooltip = toggleStyledComponent(
28+
CSS_MODULE_FEATURE_FLAG,
29+
'span',
30+
styled.span`
31+
/* Overriding the default popover styles */
32+
display: none;
33+
&[popover] {
34+
position: absolute;
35+
padding: 0.5em 0.75em;
36+
width: max-content;
37+
margin: auto;
38+
clip: auto;
39+
white-space: normal;
40+
font: normal normal 11px/1.5 ${get('fonts.normal')};
41+
-webkit-font-smoothing: subpixel-antialiased;
42+
color: var(--tooltip-fgColor, ${get('colors.fg.onEmphasis')});
43+
text-align: center;
44+
word-wrap: break-word;
45+
background: var(--tooltip-bgColor, ${get('colors.neutral.emphasisPlus')});
46+
border-radius: ${get('radii.2')};
47+
border: 0;
48+
opacity: 0;
49+
max-width: 250px;
50+
inset: auto;
51+
/* for scrollbar */
52+
overflow: visible;
53+
}
54+
/* class name in chrome is :popover-open */
55+
&[popover]:popover-open {
56+
display: block;
57+
}
58+
/* class name in firefox and safari is \:popover-open */
59+
&[popover].\\:popover-open {
60+
display: block;
61+
}
5462
55-
@media (forced-colors: active) {
56-
outline: 1px solid transparent;
57-
}
63+
@media (forced-colors: active) {
64+
outline: 1px solid transparent;
65+
}
5866
59-
// This is needed to keep the tooltip open when the user leaves the trigger element to hover tooltip
60-
&::after {
61-
position: absolute;
62-
display: block;
63-
right: 0;
64-
left: 0;
65-
height: var(--overlay-offset, 0.25rem);
66-
content: '';
67-
}
67+
// This is needed to keep the tooltip open when the user leaves the trigger element to hover tooltip
68+
&::after {
69+
position: absolute;
70+
display: block;
71+
right: 0;
72+
left: 0;
73+
height: var(--overlay-offset, 0.25rem);
74+
content: '';
75+
}
6876
69-
/* South, East, Southeast, Southwest after */
70-
&[data-direction='n']::after,
71-
&[data-direction='ne']::after,
72-
&[data-direction='nw']::after {
73-
top: 100%;
74-
}
75-
&[data-direction='s']::after,
76-
&[data-direction='se']::after,
77-
&[data-direction='sw']::after {
78-
bottom: 100%;
79-
}
77+
/* South, East, Southeast, Southwest after */
78+
&[data-direction='n']::after,
79+
&[data-direction='ne']::after,
80+
&[data-direction='nw']::after {
81+
top: 100%;
82+
}
83+
&[data-direction='s']::after,
84+
&[data-direction='se']::after,
85+
&[data-direction='sw']::after {
86+
bottom: 100%;
87+
}
8088
81-
&[data-direction='w']::after {
82-
position: absolute;
83-
display: block;
84-
height: 100%;
85-
width: 8px;
86-
content: '';
87-
bottom: 0;
88-
left: 100%;
89-
}
90-
/* East before and after */
91-
&[data-direction='e']::after {
92-
position: absolute;
93-
display: block;
94-
height: 100%;
95-
width: 8px;
96-
content: '';
97-
bottom: 0;
98-
right: 100%;
99-
margin-left: -8px;
100-
}
89+
&[data-direction='w']::after {
90+
position: absolute;
91+
display: block;
92+
height: 100%;
93+
width: 8px;
94+
content: '';
95+
bottom: 0;
96+
left: 100%;
97+
}
98+
/* East before and after */
99+
&[data-direction='e']::after {
100+
position: absolute;
101+
display: block;
102+
height: 100%;
103+
width: 8px;
104+
content: '';
105+
bottom: 0;
106+
right: 100%;
107+
margin-left: -8px;
108+
}
101109
102-
/* Animation definition */
103-
@keyframes tooltip-appear {
104-
from {
105-
opacity: 0;
110+
/* Animation definition */
111+
@keyframes tooltip-appear {
112+
from {
113+
opacity: 0;
114+
}
115+
to {
116+
opacity: 1;
117+
}
106118
}
107-
to {
108-
opacity: 1;
119+
/* Animation styles */
120+
&:popover-open,
121+
&:popover-open::before {
122+
${animationStyles}
109123
}
110-
}
111-
/* Animation styles */
112-
&:popover-open,
113-
&:popover-open::before {
114-
${animationStyles}
115-
}
116124
117-
/* Animation styles */
118-
&.\\:popover-open,
119-
&.\\:popover-open::before {
120-
${animationStyles}
121-
}
125+
/* Animation styles */
126+
&.\\:popover-open,
127+
&.\\:popover-open::before {
128+
${animationStyles}
129+
}
122130
123-
${sx};
124-
`
131+
${sx};
132+
`,
133+
)
125134

126135
export type TooltipDirection = 'nw' | 'n' | 'ne' | 'e' | 'se' | 's' | 'sw' | 'w'
127136
export type TooltipProps = React.PropsWithChildren<
128137
{
129138
direction?: TooltipDirection
130139
text: string
131140
type?: 'label' | 'description'
132-
} & SxProp &
133-
ComponentProps<typeof StyledTooltip>
134-
>
141+
} & SxProp
142+
> &
143+
React.HTMLAttributes<HTMLElement>
135144

136145
type TriggerPropsType = {
137146
'aria-describedby'?: string
@@ -187,11 +196,12 @@ const isInteractive = (element: HTMLElement) => {
187196
export const TooltipContext = React.createContext<{tooltipId?: string}>({})
188197

189198
export const Tooltip = React.forwardRef(
190-
({direction = 's', text, type = 'description', children, id, ...rest}: TooltipProps, forwardedRef) => {
199+
({direction = 's', text, type = 'description', children, id, className, ...rest}: TooltipProps, forwardedRef) => {
191200
const tooltipId = useId(id)
192201
const child = Children.only(children)
193202
const triggerRef = useProvidedRefOrCreate(forwardedRef as React.RefObject<HTMLElement>)
194203
const tooltipElRef = useRef<HTMLDivElement>(null)
204+
const enabled = useFeatureFlag(CSS_MODULE_FEATURE_FLAG)
195205

196206
const [calculatedDirection, setCalculatedDirection] = useState<TooltipDirection>(direction)
197207

@@ -355,6 +365,7 @@ export const Tooltip = React.forwardRef(
355365
},
356366
})}
357367
<StyledTooltip
368+
className={clsx(className, {[classes.Tooltip]: enabled})}
358369
ref={tooltipElRef}
359370
data-direction={calculatedDirection}
360371
{...rest}

0 commit comments

Comments
 (0)