diff --git a/.changeset/few-zebras-drive.md b/.changeset/few-zebras-drive.md new file mode 100644 index 000000000000..bd7917d6de5c --- /dev/null +++ b/.changeset/few-zebras-drive.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Convert SkeletonBox to CSS modules behind the feature flag diff --git a/packages/react/src/experimental/Skeleton/SkeletonBox.module.css b/packages/react/src/experimental/Skeleton/SkeletonBox.module.css new file mode 100644 index 000000000000..08edcbd97508 --- /dev/null +++ b/packages/react/src/experimental/Skeleton/SkeletonBox.module.css @@ -0,0 +1,30 @@ +@keyframes shimmer { + from { + mask-position: 200%; + } + + to { + mask-position: 0%; + } +} + +.SkeletonBox { + display: block; + height: 1rem; + background-color: var(--bgColor-muted); + border-radius: var(--borderRadius-small); + animation: shimmer; + + @media (prefers-reduced-motion: no-preference) { + mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%); + mask-size: 200%; + animation: shimmer; + animation-duration: 1s; + animation-iteration-count: infinite; + } + + @media (forced-colors: active) { + outline: 1px solid transparent; + outline-offset: -1px; + } +} diff --git a/packages/react/src/experimental/Skeleton/SkeletonBox.tsx b/packages/react/src/experimental/Skeleton/SkeletonBox.tsx index a5b2a0af4650..7df654aa354c 100644 --- a/packages/react/src/experimental/Skeleton/SkeletonBox.tsx +++ b/packages/react/src/experimental/Skeleton/SkeletonBox.tsx @@ -1,40 +1,80 @@ -import type React from 'react' +import React from 'react' import styled, {keyframes} from 'styled-components' -import sx, {type SxProp} from '../../sx' +import sx, {merge, type SxProp} from '../../sx' import {get} from '../../constants' +import {type CSSProperties, type HTMLProps} from 'react' +import {toggleStyledComponent} from '../../internal/utils/toggleStyledComponent' +import {clsx} from 'clsx' +import classes from './SkeletonBox.module.css' +import {useFeatureFlag} from '../../FeatureFlags' type SkeletonBoxProps = { /** Height of the skeleton "box". Accepts any valid CSS `height` value. */ - height?: React.CSSProperties['height'] + height?: CSSProperties['height'] /** Width of the skeleton "box". Accepts any valid CSS `width` value. */ - width?: React.CSSProperties['width'] -} & SxProp + width?: CSSProperties['width'] + /** The className of the skeleton box */ + className?: string +} & SxProp & + HTMLProps const shimmer = keyframes` from { mask-position: 200%; } to { mask-position: 0%; } ` +const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team' -export const SkeletonBox = styled.div` - animation: ${shimmer}; - display: block; - background-color: var(--bgColor-muted, ${get('colors.canvas.subtle')}); - border-radius: 3px; - height: ${props => props.height || '1rem'}; - width: ${props => props.width}; - - @media (prefers-reduced-motion: no-preference) { - mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%); - mask-size: 200%; +const StyledSkeletonBox = toggleStyledComponent( + CSS_MODULES_FEATURE_FLAG, + 'div', + styled.div` animation: ${shimmer}; - animation-duration: 1s; - animation-iteration-count: infinite; - } + display: block; + background-color: var(--bgColor-muted, ${get('colors.canvas.subtle')}); + border-radius: 3px; + height: ${props => props.height || '1rem'}; + width: ${props => props.width}; - @media (forced-colors: active) { - outline: 1px solid transparent; - outline-offset: -1px; - } + @media (prefers-reduced-motion: no-preference) { + mask-image: linear-gradient(75deg, #000 30%, rgba(0, 0, 0, 0.65) 80%); + mask-size: 200%; + animation: ${shimmer}; + animation-duration: 1s; + animation-iteration-count: infinite; + } - ${sx}; -` + @media (forced-colors: active) { + outline: 1px solid transparent; + outline-offset: -1px; + } + + ${sx}; + `, +) + +export const SkeletonBox = React.forwardRef(function SkeletonBox( + {height, width, className, style, ...props}, + ref, +) { + const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) + return ( + + ) +}) diff --git a/packages/react/src/experimental/Skeleton/__tests__/SkeletonBox.test.tsx b/packages/react/src/experimental/Skeleton/__tests__/SkeletonBox.test.tsx new file mode 100644 index 000000000000..a22e2ef2be50 --- /dev/null +++ b/packages/react/src/experimental/Skeleton/__tests__/SkeletonBox.test.tsx @@ -0,0 +1,25 @@ +import {render} from '@testing-library/react' +import React from 'react' +import {FeatureFlags} from '../../../FeatureFlags' +import {SkeletonBox} from '../SkeletonBox' + +describe('SkeletonBox', () => { + it('should support `className` on the outermost element', () => { + const Element = () => + const FeatureFlagElement = () => { + return ( + + + + ) + } + expect(render().container.firstChild).toHaveClass('test-class-name') + expect(render().container.firstChild).toHaveClass('test-class-name') + }) +})