Skip to content

Commit 536d7ab

Browse files
feat(Token): Migrate to CSS modules behind feature flag Pt 1 (#5251)
* Migrate container and remove button to CSS modules * fix format * update to use vars * update related snapshots * small refactor * remove dead code --------- Co-authored-by: Hussam Ghazzi <[email protected]>
1 parent a2efba0 commit 536d7ab

File tree

6 files changed

+600
-86
lines changed

6 files changed

+600
-86
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
.TokenButton {
2+
display: inline-flex;
3+
padding: 0;
4+
margin-left: var(--base-size-4);
5+
font-family: inherit;
6+
color: currentColor;
7+
text-decoration: none;
8+
cursor: pointer;
9+
user-select: none;
10+
background-color: transparent;
11+
border: 0;
12+
border-radius: var(--borderRadius-full);
13+
justify-content: center;
14+
align-items: center;
15+
appearance: none;
16+
align-self: baseline;
17+
}
18+
19+
.TokenButton[data-size='small'] {
20+
width: var(--base-size-16);
21+
height: var(--base-size-16);
22+
}
23+
24+
.TokenButton[data-size='medium'] {
25+
width: 20px;
26+
height: 20px;
27+
}
28+
29+
.TokenButton[data-size='large'] {
30+
width: var(--base-size-24);
31+
height: var(--base-size-24);
32+
margin-left: var(--base-size-8);
33+
}
34+
35+
.TokenButton[data-size='xlarge'] {
36+
width: var(--base-size-32);
37+
height: var(--base-size-32);
38+
margin-left: var(--base-size-8);
39+
}
40+
41+
.TokenButton:hover,
42+
.TokenButton:focus {
43+
/* TODO: choose a better functional color variable for this */
44+
background-color: var(--bgColor-neutral-muted);
45+
}
46+
47+
.TokenButton:active {
48+
/* TODO: choose a better functional color variable for this */
49+
background-color: var(--bgColor-neutral-muted);
50+
}

packages/react/src/Token/_RemoveTokenButton.tsx

Lines changed: 69 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@ import React from 'react'
22
import {XIcon} from '@primer/octicons-react'
33
import styled, {css} from 'styled-components'
44
import {variant} from 'styled-system'
5+
import {clsx} from 'clsx'
56
import {get} from '../constants'
6-
import type {SxProp} from '../sx'
7-
import sx from '../sx'
8-
import type {ComponentProps} from '../utils/types'
7+
import sx, {type SxProp} from '../sx'
98
import type {TokenSizeKeys} from './TokenBase'
109
import {tokenSizes, defaultTokenSize} from './TokenBase'
10+
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
11+
import {useFeatureFlag} from '../FeatureFlags'
1112

12-
interface TokenButtonProps {
13+
import classes from './_RemoveTokenButton.module.css'
14+
15+
interface TokenButtonProps extends SxProp {
1316
borderOffset?: number
1417
size?: TokenSizeKeys
1518
isParentInteractive?: boolean
1619
}
1720

21+
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'
22+
1823
const variants = variant<{height: string; width: string}, TokenSizeKeys>({
1924
prop: 'size',
2025
variants: {
@@ -39,66 +44,84 @@ const variants = variant<{height: string; width: string}, TokenSizeKeys>({
3944

4045
const getTokenButtonIconSize = (size?: TokenSizeKeys) => parseInt(tokenSizes[size || defaultTokenSize], 10) * 0.75
4146

42-
const StyledTokenButton = styled.span<TokenButtonProps & SxProp>`
43-
background-color: transparent;
44-
font-family: inherit;
45-
color: currentColor;
46-
cursor: pointer;
47-
display: inline-flex;
48-
justify-content: center;
49-
align-items: center;
50-
user-select: none;
51-
appearance: none;
52-
text-decoration: none;
53-
padding: 0;
54-
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
55-
align-self: baseline;
56-
border: 0;
57-
border-radius: 999px;
47+
const StyledTokenButton = toggleStyledComponent(
48+
CSS_MODULES_FEATURE_FLAG,
49+
'span',
50+
styled.span<TokenButtonProps>`
51+
background-color: transparent;
52+
font-family: inherit;
53+
color: currentColor;
54+
cursor: pointer;
55+
display: inline-flex;
56+
justify-content: center;
57+
align-items: center;
58+
user-select: none;
59+
appearance: none;
60+
text-decoration: none;
61+
padding: 0;
62+
transform: ${props => `translate(${props.borderOffset}px, -${props.borderOffset}px)`};
63+
align-self: baseline;
64+
border: 0;
65+
border-radius: 999px;
66+
67+
${props => {
68+
switch (props.size) {
69+
case 'large':
70+
case 'xlarge':
71+
return css`
72+
margin-left: ${get('space.2')};
73+
`
74+
default:
75+
return css`
76+
margin-left: ${get('space.1')};
77+
`
78+
}
79+
}}
5880
59-
${props => {
60-
switch (props.size) {
61-
case 'large':
62-
case 'xlarge':
63-
return css`
64-
margin-left: ${get('space.2')};
65-
`
66-
default:
67-
return css`
68-
margin-left: ${get('space.1')};
69-
`
81+
&:hover,
82+
&:focus {
83+
// TODO: choose a better functional color variable for this
84+
background-color: ${get('colors.neutral.muted')};
7085
}
71-
}}
7286
73-
&:hover,
74-
&:focus {
75-
// TODO: choose a better functional color variable for this
76-
background-color: ${get('colors.neutral.muted')};
77-
}
87+
&:active {
88+
// TODO: choose a better functional color variable for this
89+
background-color: ${get('colors.neutral.subtle')};
90+
}
7891
79-
&:active {
80-
// TODO: choose a better functional color variable for this
81-
background-color: ${get('colors.neutral.subtle')};
82-
}
92+
${variants}
93+
${sx}
94+
`,
95+
)
8396

84-
${variants}
85-
${sx}
86-
`
97+
type RemoveTokenButtonProps = TokenButtonProps & Omit<React.HTMLProps<HTMLSpanElement | HTMLButtonElement>, 'size'>
8798

88-
const RemoveTokenButton: React.FC<React.PropsWithChildren<ComponentProps<typeof StyledTokenButton>>> = ({
99+
const RemoveTokenButton = ({
89100
'aria-label': ariaLabel,
90101
isParentInteractive,
91102
size = defaultTokenSize,
103+
className,
92104
...rest
93-
}) => {
105+
}: React.PropsWithChildren<RemoveTokenButtonProps>) => {
94106
delete rest.children
95107

108+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
109+
96110
return (
97111
<StyledTokenButton
98112
as={isParentInteractive ? 'span' : 'button'}
99113
tabIndex={isParentInteractive ? -1 : undefined}
100114
aria-label={!isParentInteractive ? 'Remove token' : ariaLabel}
101115
size={size}
116+
data-size={size}
117+
className={clsx(enabled && classes.TokenButton, className)}
118+
style={
119+
enabled
120+
? {
121+
transform: `translate(${rest.borderOffset}px, -${rest.borderOffset}px)`,
122+
}
123+
: {}
124+
}
102125
{...rest}
103126
>
104127
<XIcon size={getTokenButtonIconSize(size)} />
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
.TokenTextContainer {
2+
width: auto;
3+
min-width: 0;
4+
padding: 0;
5+
margin: 0;
6+
overflow: hidden;
7+
font: inherit;
8+
/* stylelint-disable-next-line primer/typography */
9+
line-height: normal;
10+
color: inherit;
11+
12+
/* reset anchor styles */
13+
color: currentColor;
14+
text-decoration: none;
15+
text-overflow: ellipsis;
16+
white-space: nowrap;
17+
18+
/* reset button styles, make the cursor a pointer, and add line-height */
19+
background: transparent;
20+
border: none;
21+
flex-grow: 1;
22+
-webkit-font-smoothing: inherit;
23+
-moz-osx-font-smoothing: inherit;
24+
appearance: none;
25+
}
26+
27+
/* Position psuedo-element above text content, but below the
28+
remove button.
29+
This ensures the <a> or <button> receives the click no
30+
matter where on the token the user clicks. */
31+
.TokenTextContainer:is(a, button, [tabIndex='0']) {
32+
cursor: pointer;
33+
}
34+
35+
/* Position psuedo-element above text content, but below the
36+
remove button.
37+
This ensures the <a> or <button> receives the click no
38+
matter where on the token the user clicks. */
39+
.TokenTextContainer:is(a, button, [tabIndex='0'])::after {
40+
position: absolute;
41+
top: 0;
42+
right: 0;
43+
bottom: 0;
44+
left: 0;
45+
content: '';
46+
}
Lines changed: 61 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,67 @@
11
import styled from 'styled-components'
22
import type {TokenBaseProps} from './TokenBase'
3+
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
4+
import React from 'react'
5+
import classes from './_TokenTextContainer.module.css'
6+
import {useFeatureFlag} from '../FeatureFlags'
7+
import {clsx} from 'clsx'
38

4-
const TokenTextContainer = styled('span')<Partial<TokenBaseProps>>`
5-
flex-grow: 1;
6-
min-width: 0;
7-
overflow: hidden;
8-
text-overflow: ellipsis;
9-
white-space: nowrap;
10-
11-
// reset button styles, make the cursor a pointer, and add line-height
12-
background: transparent;
13-
border: none;
14-
color: inherit;
15-
font: inherit;
16-
margin: 0;
17-
padding: 0;
18-
width: auto;
19-
-webkit-font-smoothing: inherit;
20-
-moz-osx-font-smoothing: inherit;
21-
-webkit-appearance: none;
22-
line-height: normal;
23-
24-
// reset anchor styles
25-
color: currentColor;
26-
text-decoration: none;
27-
28-
// Position psuedo-element above text content, but below the
29-
// remove button.
30-
// This ensures the <a> or <button> receives the click no
31-
// matter where on the token the user clicks.
32-
&:is(a, button, [tabIndex='0']) {
33-
cursor: pointer;
34-
35-
&:after {
36-
content: '';
37-
position: absolute;
38-
left: 0;
39-
top: 0;
40-
right: 0;
41-
bottom: 0;
9+
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'
10+
11+
const StyledTokenTextContainer = toggleStyledComponent(
12+
CSS_MODULES_FEATURE_FLAG,
13+
'span',
14+
styled('span')`
15+
flex-grow: 1;
16+
min-width: 0;
17+
overflow: hidden;
18+
text-overflow: ellipsis;
19+
white-space: nowrap;
20+
21+
// reset button styles, make the cursor a pointer, and add line-height
22+
background: transparent;
23+
border: none;
24+
color: inherit;
25+
font: inherit;
26+
margin: 0;
27+
padding: 0;
28+
width: auto;
29+
-webkit-font-smoothing: inherit;
30+
-moz-osx-font-smoothing: inherit;
31+
-webkit-appearance: none;
32+
line-height: normal;
33+
34+
// reset anchor styles
35+
color: currentColor;
36+
text-decoration: none;
37+
38+
// Position psuedo-element above text content, but below the
39+
// remove button.
40+
// This ensures the <a> or <button> receives the click no
41+
// matter where on the token the user clicks.
42+
&:is(a, button, [tabIndex='0']) {
43+
cursor: pointer;
44+
45+
&:after {
46+
content: '';
47+
position: absolute;
48+
left: 0;
49+
top: 0;
50+
right: 0;
51+
bottom: 0;
52+
}
4253
}
43-
}
44-
`
54+
`,
55+
)
56+
57+
const TokenTextContainer = ({children, ...props}: React.PropsWithChildren<Partial<TokenBaseProps>>) => {
58+
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
59+
60+
return (
61+
<StyledTokenTextContainer className={clsx(enabled && classes.TokenTextContainer)} {...props}>
62+
{children}
63+
</StyledTokenTextContainer>
64+
)
65+
}
4566

4667
export default TokenTextContainer

0 commit comments

Comments
 (0)