Skip to content

Commit

Permalink
Merge branch 'main' of github.com:primer/react into refactor/update-b…
Browse files Browse the repository at this point in the history
…ranch-name-to-css-modules
  • Loading branch information
joshblack committed Oct 29, 2024
2 parents a4e085e + 31d9a05 commit 1fe209b
Show file tree
Hide file tree
Showing 37 changed files with 501 additions and 125 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-mails-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Convert Details to css module behind feature flag
5 changes: 5 additions & 0 deletions .changeset/old-plums-explode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": patch
---

fix(DataTable): export datatable utility types
5 changes: 5 additions & 0 deletions .changeset/shy-seahorses-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@primer/react': minor
---

AvatarStack: Adds keyboard support to `AvatarStack`
5 changes: 5 additions & 0 deletions .changeset/six-colts-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/react": minor
---

Remove CSS modules feature flag from Label component
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
79 changes: 46 additions & 33 deletions e2e/components/Details.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,56 @@ import {test, expect} from '@playwright/test'
import {visit} from '../test-helpers/storybook'
import {themes} from '../test-helpers/themes'

test.describe('Details', () => {
test.describe('Default', () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: 'components-details--default',
globals: {
colorScheme: theme,
},
})
const stories: Array<{title: string; id: string}> = [
{
title: 'Default',
id: 'components-details--default',
},
{
title: 'SX Prop Stress Test',
id: 'components-details-dev--sx-prop-stress-test',
},
]

// Default state - closed
expect(await page.screenshot()).toMatchSnapshot(`Details.Default.${theme}.png`)
// Click the summary to open
await page.getByText('See Details').click()
await page.getByText('This is some content').waitFor()
// Open state
expect(await page.screenshot()).toMatchSnapshot(`Details.Default.${theme}.open.png`)
})
test.describe('Details', () => {
for (const story of stories) {
test.describe(story.title, () => {
for (const theme of themes) {
test.describe(theme, () => {
test('default @vrt', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})

test('axe @aat', async ({page}) => {
await visit(page, {
id: 'components-details--default',
globals: {
colorScheme: theme,
},
// Default state - closed
expect(await page.screenshot()).toMatchSnapshot(`Details.${story.title}.${theme}.png`)
// Click the summary to open
await page.getByText('See Details').click()
await page.getByText('This is some content').waitFor()
// Open state
expect(await page.screenshot()).toMatchSnapshot(`Details.${story.title}.${theme}.open.png`)
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: false,

test('axe @aat', async ({page}) => {
await visit(page, {
id: story.id,
globals: {
colorScheme: theme,
},
})
await expect(page).toHaveNoViolations({
rules: {
'color-contrast': {
enabled: false,
},
},
},
})
})
})
})
}
})
}
})
}
})
45 changes: 41 additions & 4 deletions packages/react/src/AvatarStack/AvatarStack.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {clsx} from 'clsx'
import React from 'react'
import React, {useEffect, useRef, useState} from 'react'
import styled from 'styled-components'
import {get} from '../constants'
import Box from '../Box'
Expand All @@ -12,6 +12,8 @@ import {isResponsiveValue} from '../hooks/useResponsiveValue'
import {getBreakpointDeclarations} from '../utils/getBreakpointDeclarations'
import {defaultSxProp} from '../utils/defaultSxProp'
import type {WidthOnlyViewportRangeKeys} from '../utils/types/ViewportRangeKeys'
import {hasInteractiveNodes} from '../internal/utils/hasInteractiveNodes'
import getGlobalFocusStyles from '../internal/utils/getGlobalFocusStyles'

type StyledAvatarStackWrapperProps = {
count?: number
Expand All @@ -30,6 +32,8 @@ const AvatarStackWrapper = styled.span<StyledAvatarStackWrapperProps>`
.pc-AvatarStackBody {
display: flex;
position: absolute;
${getGlobalFocusStyles('1px')}
}
.pc-AvatarItem {
Expand Down Expand Up @@ -130,7 +134,8 @@ const AvatarStackWrapper = styled.span<StyledAvatarStackWrapperProps>`
.pc-AvatarStackBody {
flex-direction: row-reverse;
&:not(.pc-AvatarStack--disableExpand):hover {
&:not(.pc-AvatarStack--disableExpand):hover,
&:not(.pc-AvatarStack--disableExpand):focus-within {
.pc-AvatarItem {
margin-right: ${get('space.1')}!important;
margin-left: 0 !important;
Expand All @@ -143,7 +148,8 @@ const AvatarStackWrapper = styled.span<StyledAvatarStackWrapperProps>`
}
}
.pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):hover {
.pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):hover,
.pc-AvatarStackBody:not(.pc-AvatarStack--disableExpand):focus-within {
width: auto;
.pc-AvatarItem {
Expand All @@ -157,6 +163,8 @@ const AvatarStackWrapper = styled.span<StyledAvatarStackWrapperProps>`
visibility 0.2s ease-in-out,
box-shadow 0.1s ease-in-out;
${getGlobalFocusStyles('1px')}
&:first-child {
margin-left: 0;
}
Expand Down Expand Up @@ -195,6 +203,9 @@ const AvatarStack = ({
className,
sx: sxProp = defaultSxProp,
}: AvatarStackProps) => {
const [hasInteractiveChildren, setHasInteractiveChildren] = useState<boolean | undefined>(false)
const stackContainer = useRef<HTMLDivElement>(null)

const count = React.Children.count(children)
const wrapperClassNames = clsx(
{
Expand Down Expand Up @@ -249,6 +260,25 @@ const AvatarStack = ({
)
}

useEffect(() => {
if (stackContainer.current) {
const interactiveChildren = () => {
setHasInteractiveChildren(hasInteractiveNodes(stackContainer.current))
}

const observer = new MutationObserver(interactiveChildren)

observer.observe(stackContainer.current, {childList: true})

// Call on initial render, then call it again only if there's a mutation
interactiveChildren()

return () => {
observer.disconnect()
}
}
}, [])

const getResponsiveAvatarSizeStyles = () => {
// if there is no size set on the AvatarStack, use the `size` props of the Avatar children to set the `--avatar-stack-size` CSS variable
if (!size) {
Expand Down Expand Up @@ -279,7 +309,14 @@ const AvatarStack = ({

return (
<AvatarStackWrapper count={count} className={wrapperClassNames} sx={avatarStackSx}>
<Box className={bodyClassNames}> {transformChildren(children)}</Box>
<Box
className={bodyClassNames}
tabIndex={!hasInteractiveChildren && !disableExpand ? 0 : undefined}
ref={stackContainer}
>
{' '}
{transformChildren(children)}
</Box>
</AvatarStackWrapper>
)
}
Expand Down
4 changes: 3 additions & 1 deletion packages/react/src/DataTable/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,6 @@ export type {
TableSkeletonProps,
} from './Table'
export {createColumnHelper} from './column'
export type {Column} from './column'
export type {Column, CellAlignment, ColumnWidth} from './column'
export type {UniqueRow} from './row'
export type {ObjectPaths} from './utils'
27 changes: 27 additions & 0 deletions packages/react/src/Details/Details.dev.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'
import type {StoryFn, Meta} from '@storybook/react'
import Details from './Details'
import {Button} from '../Button'
import useDetails from '../hooks/useDetails'

export default {
title: 'Components/Details/Dev',
component: Details,
} as Meta<typeof Details>

export const SxPropStressTest: StoryFn<typeof Details> = () => {
const {getDetailsProps} = useDetails({closeOnOutsideClick: true})
return (
<Details
{...getDetailsProps()}
sx={{
backgroundColor: 'accent.emphasis',
color: 'accent.fg',
p: 4,
}}
>
<Button as="summary">See Details</Button>
This is some content
</Details>
)
}
7 changes: 7 additions & 0 deletions packages/react/src/Details/Details.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.Details > summary {
list-style: none;
}

.Details > summary::-webkit-details-marker {
display: none;
}
43 changes: 32 additions & 11 deletions packages/react/src/Details/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
import React, {type ComponentPropsWithoutRef, type ReactElement} from 'react'
import styled from 'styled-components'
import type {SxProp} from '../sx'
import sx from '../sx'
import type {ComponentProps} from '../utils/types'
import {toggleStyledComponent} from '../internal/utils/toggleStyledComponent'
import {useFeatureFlag} from '../FeatureFlags'
import {clsx} from 'clsx'
import classes from './Details.module.css'

const Details = styled.details<SxProp>`
& > summary {
list-style: none;
}
& > summary::-webkit-details-marker {
display: none;
}
const CSS_MODULES_FEATURE_FLAG = 'primer_react_css_modules_team'

${sx};
`
const StyledDetails = toggleStyledComponent(
CSS_MODULES_FEATURE_FLAG,
'details',
styled.details<SxProp>`
& > summary {
list-style: none;
}
& > summary::-webkit-details-marker {
display: none;
}
${sx};
`,
)

const Details = React.forwardRef<HTMLDetailsElement, DetailsProps>(
({className, children, ...rest}, ref): ReactElement => {
const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG)
return (
<StyledDetails className={clsx(className, {[classes.Details]: enabled})} {...rest} ref={ref}>
{children}
</StyledDetails>
)
},
)

Details.displayName = 'Details'

export type DetailsProps = ComponentProps<typeof Details>
export type DetailsProps = ComponentPropsWithoutRef<'details'> & SxProp
export default Details
62 changes: 13 additions & 49 deletions packages/react/src/Label/Label.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import {clsx} from 'clsx'
import {useFeatureFlag} from '../FeatureFlags'
import Box from '../Box'
import classes from './Label.module.css'
import React from 'react'
import styled from 'styled-components'
import {variant} from 'styled-system'
import {get} from '../constants'
import type {BetterSystemStyleObject, SxProp} from '../sx'
import sx from '../sx'
import type {ForwardRefComponent as PolymorphicForwardRefComponent} from '../utils/polymorphic'

export type LabelProps = {
Expand Down Expand Up @@ -72,52 +67,21 @@ export const variants: Record<LabelColorOptions, BetterSystemStyleObject> = {
},
}

const sizes: Record<LabelSizeKeys, BetterSystemStyleObject> = {
small: {
height: 'var(--base-size-20, 20px)',
padding: '0 var(--base-size-6, 6px)',
},
large: {
height: 'var(--base-size-24, 24px)',
padding: '0 var(--base-size-8, 8px)',
},
}

const StyledLabel = styled.span<LabelProps>`
align-items: center;
background-color: transparent;
border-width: 1px;
border-radius: 999px;
border-style: solid;
display: inline-flex;
font-weight: ${get('fontWeights.semibold')};
font-size: ${get('fontSizes.0')};
line-height: 1;
white-space: nowrap;
${variant({variants})};
${variant({prop: 'size', variants: sizes})};
${sx};
`

const Label = React.forwardRef(function Label({as, size = 'small', variant = 'default', className, ...rest}, ref) {
const enabled = useFeatureFlag('primer_react_css_modules_ga')
if (enabled) {
const Component = as || 'span'
if (rest.sx) {
return (
<Box
as={Component}
className={clsx(className, classes.Label)}
data-size={size}
data-variant={variant}
ref={ref}
{...rest}
/>
)
}
return <Component className={clsx(className, classes.Label)} data-size={size} data-variant={variant} {...rest} />
const Component = as || 'span'
if (rest.sx) {
return (
<Box
as={Component}
className={clsx(className, classes.Label)}
data-size={size}
data-variant={variant}
ref={ref}
{...rest}
/>
)
}
return <StyledLabel as={as} className={className} size={size} variant={variant} ref={ref} {...rest} />
return <Component className={clsx(className, classes.Label)} data-size={size} data-variant={variant} {...rest} />
}) as PolymorphicForwardRefComponent<'span', LabelProps>

export default Label
Loading

0 comments on commit 1fe209b

Please sign in to comment.