Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Token css modules #5237

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c0624b6
VRT dev story
randall-krauskopf Nov 5, 2024
d1f704e
test(vrt): update snapshots
randall-krauskopf Nov 5, 2024
739c19a
update snapshot comparison
randall-krauskopf Nov 5, 2024
32e9a5d
Merge branch 'token-vrt-css' of https://github.com/primer/react into …
randall-krauskopf Nov 5, 2024
b29a75f
test(vrt): update snapshots
randall-krauskopf Nov 5, 2024
cd318a2
migrate _TokenTextContainer
randall-krauskopf Nov 5, 2024
0f1eca7
Merge branch 'main' into token-css-modules
randall-krauskopf Nov 5, 2024
8429062
fix typing issue
randall-krauskopf Nov 5, 2024
581c40d
lint fix
randall-krauskopf Nov 6, 2024
d9b2ebb
RemoveTokenButton migration
randall-krauskopf Nov 6, 2024
acc914e
lint
randall-krauskopf Nov 6, 2024
02edd34
push WIP commit for review
randall-krauskopf Nov 6, 2024
00dac0a
migrate avatar token story
randall-krauskopf Nov 7, 2024
88220cf
use data- attributes
randall-krauskopf Nov 7, 2024
ef42f09
style updates
randall-krauskopf Nov 7, 2024
23a9368
migrate token base
randall-krauskopf Nov 7, 2024
59c41e9
use data attributes
randall-krauskopf Nov 7, 2024
646f774
format
randall-krauskopf Nov 7, 2024
edd2a48
Merge branch 'token-css-modules' of https://github.com/primer/react i…
randall-krauskopf Nov 7, 2024
7a78069
format
randall-krauskopf Nov 7, 2024
e85516f
add lint
randall-krauskopf Nov 7, 2024
2845db7
add missing data attribute
randall-krauskopf Nov 8, 2024
2b50028
remove comment
randall-krauskopf Nov 8, 2024
790a00d
migrate the top level token component
randall-krauskopf Nov 8, 2024
9cc92bf
Merge branch 'main' into token-css-modules
randall-krauskopf Nov 8, 2024
85a0d46
Migrate container and remove button to CSS modules
randall-krauskopf Nov 8, 2024
1f0afd8
fix format
randall-krauskopf Nov 8, 2024
67d0c7d
update to use vars
randall-krauskopf Nov 8, 2024
c53d37d
update related snapshots
randall-krauskopf Nov 8, 2024
06198d2
Merge branch 'token-css-modules-1' into token-css-modules
randall-krauskopf Nov 8, 2024
a880e31
format
randall-krauskopf Nov 8, 2024
3ef6c01
lint and snapshot updates
randall-krauskopf Nov 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions packages/react/src/Token/AvatarToken.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
:root {
--spacing: calc(var(--base-size-4) * 2);
}

.AvatarContainer {
display: block;
}

/* TODO: Remove this once the avatar component is converted to CSS modules */
.Avatar {
width: 100% !important;
height: 100% !important;
}

.Token {
padding-left: var(--base-size-4);
}

.AvatarContainer[data-size='small'] {
width: calc(16px - var(--spacing));
height: calc(16px - var(--spacing));
}

.AvatarContainer[data-size='medium'] {
width: calc(20px - var(--spacing));
height: calc(20px - var(--spacing));
}

.AvatarContainer[data-size='large'] {
width: calc(24px - var(--spacing));
height: calc(24px - var(--spacing));
}

.AvatarContainer[data-size='xlarge'] {
width: calc(32px - var(--spacing));
height: calc(32px - var(--spacing));
}
40 changes: 24 additions & 16 deletions packages/react/src/Token/AvatarToken.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,55 @@
import React, {forwardRef} from 'react'
import styled from 'styled-components'
import {clsx} from 'clsx'
import {get} from '../constants'
import type {TokenBaseProps, TokenSizeKeys} from './TokenBase'
import {defaultTokenSize, tokenSizes} from './TokenBase'
import Token from './Token'
import Avatar from '../Avatar'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {useFeatureFlag} from '../FeatureFlags'
import classes from './AvatarToken.module.css'

// TODO: update props to only accept 'large' and 'xlarge' on the next breaking change
export interface AvatarTokenProps extends TokenBaseProps {
avatarSrc: string
}

const AvatarContainer = styled.span<{avatarSize: TokenSizeKeys}>`
// 'space.1' is used because to match space from the left of the token to the left of the avatar
// '* 2' is done to account for the top and bottom
--spacing: calc(${get('space.1')} * 2);
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

display: block;
height: ${props => `calc(${tokenSizes[props.avatarSize]} - var(--spacing))`};
width: ${props => `calc(${tokenSizes[props.avatarSize]} - var(--spacing))`};
`
const AvatarContainer = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'span',
styled.span<{avatarSize: TokenSizeKeys}>`
// 'space.1' is used because to match space from the left of the token to the left of the avatar
// '* 2' is done to account for the top and bottom
--spacing: calc(${get('space.1')} * 2);

display: block;
height: ${props => `calc(${tokenSizes[props.avatarSize]} - var(--spacing))`};
width: ${props => `calc(${tokenSizes[props.avatarSize]} - var(--spacing))`};
`,
)

const AvatarToken = forwardRef(({avatarSrc, id, size = defaultTokenSize, ...rest}, forwardedRef) => {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

return (
<Token
leadingVisual={() => (
<AvatarContainer avatarSize={size}>
<AvatarContainer avatarSize={size} className={clsx(enabled && classes.AvatarContainer)} data-size={size}>
<Avatar
src={avatarSrc}
size={parseInt(tokenSizes[size], 10)}
sx={{
width: '100%',
height: '100%',
}}
className={classes.Avatar}
style={!enabled ? {height: '100%', width: '100%'} : {}}
/>
</AvatarContainer>
)}
size={size}
id={id?.toString()}
sx={{
paddingLeft: get('space.1'),
}}
className={classes.Token}
{...rest}
ref={forwardedRef}
/>
Expand Down
4 changes: 4 additions & 0 deletions packages/react/src/Token/IssueLabelToken.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.RemoveButton[data-has-multiple-action-targets='true'] {
position: relative;
z-index: 1;
}
13 changes: 4 additions & 9 deletions packages/react/src/Token/IssueLabelToken.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {parseToHsla, parseToRgba} from 'color2k'
import {useTheme} from '../ThemeProvider'
import TokenTextContainer from './_TokenTextContainer'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import classes from './IssueLabelToken.module.css'

export interface IssueLabelTokenProps extends TokenBaseProps {
/**
Expand Down Expand Up @@ -140,7 +141,7 @@ const IssueLabelToken = forwardRef((props, forwardedRef) => {
isSelected={isSelected}
text={text}
size={size}
sx={labelStyles}
style={labelStyles}
{...(!hasMultipleActionTargets ? interactiveTokenProps : {})}
{...rest}
ref={forwardedRef}
Expand All @@ -153,14 +154,8 @@ const IssueLabelToken = forwardRef((props, forwardedRef) => {
size={size}
aria-hidden={hasMultipleActionTargets ? 'true' : 'false'}
isParentInteractive={isTokenInteractive(props)}
sx={
hasMultipleActionTargets
? {
position: 'relative',
zIndex: '1',
}
: {}
}
data-has-multiple-action-targets={hasMultipleActionTargets}
className={classes.RemoveButton}
/>
) : null}
</TokenBase>
Expand Down
21 changes: 21 additions & 0 deletions packages/react/src/Token/Token.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.Token {
max-width: 100%;
color: var(--fgColor-muted);
background-color: var(--bgColor-neutral-muted);
border-color: var(--borderColor-muted);
border-style: 'solid';
}

.Token[data-interactive='true']:hover {
color: var(--fgColor-default);
background-color: var(--bgColor-neutral-muted);
box-shadow: var(--shadow-resting-medium);
}

.Token[data-is-selected='true'] {
color: var(--fgColor-default);
}

.Token[data-is-remove-btn='true'] {
padding-right: 0;
}
39 changes: 15 additions & 24 deletions packages/react/src/Token/Token.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import type {MouseEventHandler} from 'react'
import React, {forwardRef} from 'react'
import Box from '../Box'
import type {BetterSystemStyleObject, SxProp} from '../sx'
import {merge} from '../sx'
import type {SxProp} from '../sx'
import {defaultSxProp} from '../utils/defaultSxProp'
import type {TokenBaseProps} from './TokenBase'
import TokenBase, {defaultTokenSize, isTokenInteractive} from './TokenBase'
import RemoveTokenButton from './_RemoveTokenButton'
import TokenTextContainer from './_TokenTextContainer'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'
import VisuallyHidden from '../_VisuallyHidden'
import {useFeatureFlag} from '../FeatureFlags'

import classes from './Token.module.css'
import {clsx} from 'clsx'

// Omitting onResize and onResizeCapture because seems like React 18 types includes these menthod in the expansion but React 17 doesn't.
// TODO: This is a temporary solution until we figure out why these methods are causing type errors.
Expand All @@ -20,6 +23,8 @@ export interface TokenProps extends TokenBaseProps, SxProp {
leadingVisual?: React.ElementType
}

const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

const tokenBorderWidthPx = 1

const LeadingVisualContainer: React.FC<React.PropsWithChildren<Pick<TokenBaseProps, 'size'>>> = ({children, size}) => (
Expand All @@ -35,6 +40,8 @@ const LeadingVisualContainer: React.FC<React.PropsWithChildren<Pick<TokenBasePro
)

const Token = forwardRef((props, forwardedRef) => {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)

const {
as,
onRemove,
Expand All @@ -45,7 +52,8 @@ const Token = forwardRef((props, forwardedRef) => {
hideRemoveButton,
href,
onClick,
sx: sxProp = defaultSxProp,
sx = defaultSxProp,
className,
...rest
} = props
const hasMultipleActionTargets = isTokenInteractive(props) && Boolean(onRemove) && !hideRemoveButton
Expand All @@ -58,38 +66,21 @@ const Token = forwardRef((props, forwardedRef) => {
href,
onClick,
}
const sx = merge<BetterSystemStyleObject>(
{
backgroundColor: 'neutral.subtle',
borderColor: props.isSelected ? 'fg.default' : 'border.subtle',
borderStyle: 'solid',
borderWidth: `${tokenBorderWidthPx}px`,
color: props.isSelected ? 'fg.default' : 'fg.muted',
maxWidth: '100%',
paddingRight: !(hideRemoveButton || !onRemove) ? 0 : undefined,
...(isTokenInteractive(props)
? {
'&:hover': {
backgroundColor: 'neutral.muted',
boxShadow: 'shadow.medium',
color: 'fg.default',
},
}
: {}),
},
sxProp,
)

return (
<TokenBase
onRemove={onRemove}
id={id?.toString()}
className={clsx(enabled && className, classes.Token)}
text={text}
size={size}
sx={sx}
data-is-selected={props.isSelected}
data-is-remove-btn={!(hideRemoveButton || !onRemove)}
{...(!hasMultipleActionTargets ? interactiveTokenProps : {})}
{...rest}
ref={forwardedRef}
style={{borderWidth: `${tokenBorderWidthPx}px`}}
>
{LeadingVisual ? (
<LeadingVisualContainer size={size}>
Expand Down
60 changes: 60 additions & 0 deletions packages/react/src/Token/TokenBase.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
.TokenBase {
position: relative;
display: inline-flex;
font-family: inherit;
font-weight: var(--base-text-weight-semibold);
text-decoration: none;
white-space: nowrap;
border-radius: var(--borderRadius-full);
align-items: center;
}

.TokenBase[data-cursor-is-interactive='true'] {
cursor: pointer;
}

.TokenBase[data-cursor-is-interactive='false'] {
cursor: auto;
}

.TokenBase[data-size='small'] {
width: auto;
height: 16px;
padding-right: var(--base-size-4);
padding-left: var(--base-size-4);
font-size: var(--text-body-size-small);
/* stylelint-disable-next-line primer/typography */
line-height: 16px;
}

.TokenBase[data-size='medium'] {
width: auto;
height: 20px;
padding-right: var(--base-size-8);
padding-left: var(--base-size-8);
font-size: var(--text-body-size-small);
/* stylelint-disable-next-line primer/typography */
line-height: 20px;
}

.TokenBase[data-size='large'] {
width: auto;
height: 24px;
padding-right: var(--base-size-8);
padding-left: var(--base-size-8);
font-size: var(--text-body-size-small);
/* stylelint-disable-next-line primer/typography */
line-height: 24px;
}

.TokenBase[data-size='xlarge'] {
width: auto;
height: 32px;
padding-top: 0;
padding-right: var(--base-size-16);
padding-bottom: 0;
padding-left: var(--base-size-16);
font-size: var(--text-body-size-medium);
/* stylelint-disable-next-line primer/typography */
line-height: 32px;
}
Loading
Loading