Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions .changeset/silent-buses-knock.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@primer/react": minor
---

Add data-component attributes and associated tests for:

SplitPageLayout
Stack
StateLabel
SubNav
TabNav
Text
TextArea
ThemeProvider
37 changes: 29 additions & 8 deletions packages/react/src/PageLayout/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type PageLayoutProps = {
_slotsConfig?: Record<'header' | 'footer' | 'sidebar', React.ElementType>
className?: string
style?: React.CSSProperties
'data-component'?: string
}

// TODO: refs
Expand All @@ -79,6 +80,7 @@ const Root: React.FC<React.PropsWithChildren<PageLayoutProps>> = ({
className,
style,
_slotsConfig: slotsConfig,
'data-component': dataComponent = 'PageLayout',
}) => {
const paneRef = useRef<HTMLDivElement>(null)
const contentWrapperRef = useRef<HTMLDivElement>(null)
Expand All @@ -101,7 +103,13 @@ const Root: React.FC<React.PropsWithChildren<PageLayoutProps>> = ({

return (
<PageLayoutContext.Provider value={memoizedContextValue}>
<RootWrapper style={style} padding={padding} className={className} hasSidebar={!!slots.sidebar}>
<RootWrapper
style={style}
padding={padding}
className={className}
hasSidebar={!!slots.sidebar}
dataComponent={dataComponent}
>
{slots.sidebar}
<div ref={sidebarContentWrapperRef} className={classes.PageLayoutWrapper} data-width={containerWidth}>
{slots.header}
Expand All @@ -120,7 +128,10 @@ const RootWrapper = memo(
children,
className,
hasSidebar,
}: React.PropsWithChildren<Pick<PageLayoutProps, 'style' | 'padding' | 'className'> & {hasSidebar?: boolean}>) => {
dataComponent,
}: React.PropsWithChildren<
Pick<PageLayoutProps, 'style' | 'padding' | 'className'> & {hasSidebar?: boolean; dataComponent: string}
>) => {
return (
<div
style={
Expand All @@ -130,7 +141,7 @@ const RootWrapper = memo(
} as React.CSSProperties
}
className={clsx(classes.PageLayoutRoot, className)}
data-component="PageLayout"
data-component={dataComponent}
data-has-sidebar={hasSidebar || undefined}
>
{children}
Expand Down Expand Up @@ -549,6 +560,7 @@ export type PageLayoutHeaderProps = {
hidden?: boolean | ResponsiveValue<boolean>
className?: string
style?: React.CSSProperties
'data-component'?: string
}

const Header: FCWithSlotMarker<React.PropsWithChildren<PageLayoutHeaderProps>> = ({
Expand All @@ -561,6 +573,7 @@ const Header: FCWithSlotMarker<React.PropsWithChildren<PageLayoutHeaderProps>> =
children,
style,
className,
'data-component': dataComponent = 'PageLayout.Header',
}) => {
// Combine divider and dividerWhenNarrow for backwards compatibility
const dividerProp =
Expand All @@ -574,7 +587,7 @@ const Header: FCWithSlotMarker<React.PropsWithChildren<PageLayoutHeaderProps>> =
<header
aria-label={label}
aria-labelledby={labelledBy}
data-component="PageLayout.Header"
data-component={dataComponent}
{...getResponsiveAttributes('hidden', hidden)}
className={clsx(classes.Header, className)}
style={
Expand Down Expand Up @@ -632,6 +645,7 @@ export type PageLayoutContentProps = {
hidden?: boolean | ResponsiveValue<boolean>
className?: string
style?: React.CSSProperties
'data-component'?: string
}

// TODO: Account for pane width when centering content
Expand All @@ -645,6 +659,7 @@ const Content: FCWithSlotMarker<React.PropsWithChildren<PageLayoutContentProps>>
children,
className,
style,
'data-component': dataComponent = 'PageLayout.Content',
}) => {
const Component = as
const {contentWrapperRef} = React.useContext(PageLayoutContext)
Expand All @@ -654,7 +669,7 @@ const Content: FCWithSlotMarker<React.PropsWithChildren<PageLayoutContentProps>>
ref={contentWrapperRef}
aria-label={label}
aria-labelledby={labelledBy}
data-component="PageLayout.Content"
data-component={dataComponent}
style={style}
className={clsx(classes.ContentWrapper, className)}
{...getResponsiveAttributes('is-hidden', hidden)}
Expand Down Expand Up @@ -750,6 +765,7 @@ export type PageLayoutPaneBaseProps = {
id?: string
className?: string
style?: React.CSSProperties
'data-component'?: string
}

export type PageLayoutPaneProps = PageLayoutPaneBaseProps &
Expand Down Expand Up @@ -799,6 +815,7 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
id,
className,
style,
'data-component': dataComponent = 'PageLayout.Pane',
},
forwardRef,
) => {
Expand Down Expand Up @@ -902,7 +919,7 @@ const Pane = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLayout
{...labelProp}
{...(id && {id: paneId})}
className={classes.Pane}
data-component="PageLayout.Pane"
data-component={dataComponent}
data-resizable={resizable || undefined}
style={
{
Expand Down Expand Up @@ -1100,6 +1117,7 @@ export type PageLayoutSidebarBaseProps = {

className?: string
style?: React.CSSProperties
'data-component'?: string
}

export type PageLayoutSidebarProps = PageLayoutSidebarBaseProps &
Expand Down Expand Up @@ -1145,6 +1163,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLay
id,
className,
style,
'data-component': dataComponent = 'PageLayout.Sidebar',
},
forwardRef,
) => {
Expand Down Expand Up @@ -1237,7 +1256,7 @@ const Sidebar = React.forwardRef<HTMLDivElement, React.PropsWithChildren<PageLay
{...labelProp}
{...(id && {id: sidebarId})}
className={classes.Sidebar}
data-component="PageLayout.Sidebar"
data-component={dataComponent}
data-resizable={resizable || undefined}
style={
{
Expand Down Expand Up @@ -1313,6 +1332,7 @@ export type PageLayoutFooterProps = {
hidden?: boolean | ResponsiveValue<boolean>
className?: string
style?: React.CSSProperties
'data-component'?: string
}

const Footer: FCWithSlotMarker<React.PropsWithChildren<PageLayoutFooterProps>> = ({
Expand All @@ -1325,6 +1345,7 @@ const Footer: FCWithSlotMarker<React.PropsWithChildren<PageLayoutFooterProps>> =
children,
className,
style,
'data-component': dataComponent = 'PageLayout.Footer',
}) => {
// Combine divider and dividerWhenNarrow for backwards compatibility
const dividerProp =
Expand All @@ -1338,7 +1359,7 @@ const Footer: FCWithSlotMarker<React.PropsWithChildren<PageLayoutFooterProps>> =
<footer
aria-label={label}
aria-labelledby={labelledBy}
data-component="PageLayout.Footer"
data-component={dataComponent}
{...getResponsiveAttributes('hidden', hidden)}
className={clsx(classes.FooterWrapper, className)}
style={
Expand Down
19 changes: 19 additions & 0 deletions packages/react/src/SplitPageLayout/SplitPageLayout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,25 @@ import {implementsClassName} from '../utils/testing'
describe('SplitPageLayout', () => {
implementsClassName(SplitPageLayout)

it('renders data-component attributes for all components', () => {
const {container} = render(
<SplitPageLayout>
<SplitPageLayout.Header>Header</SplitPageLayout.Header>
<SplitPageLayout.Content>Content</SplitPageLayout.Content>
<SplitPageLayout.Pane>Pane</SplitPageLayout.Pane>
<SplitPageLayout.Sidebar>Sidebar</SplitPageLayout.Sidebar>
<SplitPageLayout.Footer>Footer</SplitPageLayout.Footer>
</SplitPageLayout>,
)

expect(container.firstChild).toHaveAttribute('data-component', 'SplitPageLayout')
expect(container.querySelector('[data-component="SplitPageLayout.Header"]')).toBeInTheDocument()
expect(container.querySelector('[data-component="SplitPageLayout.Content"]')).toBeInTheDocument()
expect(container.querySelector('[data-component="SplitPageLayout.Pane"]')).toBeInTheDocument()
expect(container.querySelector('[data-component="SplitPageLayout.Sidebar"]')).toBeInTheDocument()
expect(container.querySelector('[data-component="SplitPageLayout.Footer"]')).toBeInTheDocument()
})

it('renders Pane with a custom ID', () => {
const {getByText} = render(
<SplitPageLayout>
Expand Down
24 changes: 20 additions & 4 deletions packages/react/src/SplitPageLayout/SplitPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type {
PageLayoutSidebarProps,
} from '../PageLayout'
import {PageLayout} from '../PageLayout'
import type {WithSlotMarker} from '../utils/types'

// ----------------------------------------------------------------------------
// SplitPageLayout
Expand All @@ -16,6 +17,7 @@ export type SplitPageLayoutProps = {className?: string}
export const Root: React.FC<React.PropsWithChildren<SplitPageLayoutProps>> = props => {
return (
<PageLayout
data-component="SplitPageLayout"
containerWidth="full"
padding="none"
columnGap="none"
Expand Down Expand Up @@ -43,7 +45,7 @@ export const Header: React.FC<React.PropsWithChildren<SplitPageLayoutHeaderProps
...props
}) => {
// eslint-disable-next-line primer-react/direct-slot-children
return <PageLayout.Header padding={padding} divider={divider} {...props} />
return <PageLayout.Header data-component="SplitPageLayout.Header" padding={padding} divider={divider} {...props} />
}

Header.displayName = 'SplitPageLayout.Header'
Expand All @@ -58,7 +60,7 @@ export const Content: React.FC<React.PropsWithChildren<SplitPageLayoutContentPro
padding = 'normal',
...props
}) => {
return <PageLayout.Content width={width} padding={padding} {...props} />
return <PageLayout.Content data-component="SplitPageLayout.Content" width={width} padding={padding} {...props} />
}

Content.displayName = 'SplitPageLayout.Content'
Expand All @@ -77,6 +79,7 @@ export const Pane: React.FC<React.PropsWithChildren<SplitPageLayoutPaneProps>> =
}) => {
return (
<PageLayout.Pane
data-component="SplitPageLayout.Pane"
position={position}
sticky={sticky}
padding={padding}
Expand All @@ -98,7 +101,15 @@ export const Sidebar: React.FC<React.PropsWithChildren<SplitPageLayoutSidebarPro
divider = 'line',
...props
}) => {
return <PageLayout.Sidebar position={position} padding={padding} divider={divider} {...props} />
return (
<PageLayout.Sidebar
data-component="SplitPageLayout.Sidebar"
position={position}
padding={padding}
divider={divider}
{...props}
/>
)
}

Sidebar.displayName = 'SplitPageLayout.Sidebar'
Expand All @@ -114,13 +125,18 @@ export const Footer: React.FC<React.PropsWithChildren<SplitPageLayoutFooterProps
...props
}) => {
// eslint-disable-next-line primer-react/direct-slot-children
return <PageLayout.Footer padding={padding} divider={divider} {...props} />
return <PageLayout.Footer data-component="SplitPageLayout.Footer" padding={padding} divider={divider} {...props} />
}

Footer.displayName = 'SplitPageLayout.Footer'

// ----------------------------------------------------------------------------
// Export
;(Header as WithSlotMarker<typeof Header>).__SLOT__ = PageLayout.Header.__SLOT__
;(Content as WithSlotMarker<typeof Content>).__SLOT__ = PageLayout.Content.__SLOT__
;(Pane as WithSlotMarker<typeof Pane>).__SLOT__ = PageLayout.Pane.__SLOT__
;(Sidebar as WithSlotMarker<typeof Sidebar>).__SLOT__ = PageLayout.Sidebar.__SLOT__
;(Footer as WithSlotMarker<typeof Footer>).__SLOT__ = PageLayout.Footer.__SLOT__

export const SplitPageLayout = Object.assign(Root, {
Header,
Expand Down
2 changes: 2 additions & 0 deletions packages/react/src/Stack/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ const Stack = forwardRef(
<Component
ref={forwardedRef}
{...rest}
data-component="Stack"
className={clsx(className, classes.Stack)}
{...getResponsiveAttributes('gap', gap)}
{...getResponsiveAttributes('direction', direction)}
Expand Down Expand Up @@ -144,6 +145,7 @@ const StackItem = forwardRef(({as: Component = 'div', children, grow, shrink, cl
<Component
ref={forwardedRef}
{...rest}
data-component="StackItem"
className={clsx(className, classes.StackItem)}
{...getResponsiveAttributes('grow', grow)}
{...getResponsiveAttributes('shrink', shrink)}
Expand Down
5 changes: 5 additions & 0 deletions packages/react/src/Stack/__tests__/Stack.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import classes from '../Stack.module.css'
describe('Stack', () => {
implementsClassName(Stack, classes.Stack)

it('renders data-component attribute', () => {
render(<Stack data-testid="stack" />)
expect(screen.getByTestId('stack')).toHaveAttribute('data-component', 'Stack')
})

it('should support rendering content through `children`', () => {
render(
<Stack>
Expand Down
9 changes: 9 additions & 0 deletions packages/react/src/Stack/__tests__/StackItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ import classes from '../Stack.module.css'
describe('StackItem', () => {
implementsClassName(StackItem, classes.StackItem)

it('renders data-component attribute', () => {
render(
<Stack>
<StackItem data-testid="stack-item">Content</StackItem>
</Stack>,
)
expect(screen.getByTestId('stack-item')).toHaveAttribute('data-component', 'StackItem')
})

it('should render its children', () => {
render(
<Stack>
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/StateLabel/StateLabel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ const StateLabel = forwardRef<HTMLSpanElement, StateLabelProps>(
{...rest}
ref={ref}
className={clsx(classes.StateLabel, className)}
data-component="StateLabel"
data-size={inferredSize}
data-status={status}
>
Expand Down
9 changes: 9 additions & 0 deletions packages/react/src/StateLabel/__tests__/StateLabel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ import classes from '../StateLabel.module.css'
describe('StateLabel', () => {
implementsClassName(props => <StateLabel {...props} status="issueOpened" />, classes.StateLabel)

it('renders data-component attribute', () => {
expect(HTMLRender(<StateLabel status="issueOpened" />).container.firstChild).toHaveAttribute(
'data-component',
'StateLabel',
)
})

it('respects the status prop', () => {
expect(HTMLRender(<StateLabel status="issueOpened" />).container).toMatchSnapshot()
expect(HTMLRender(<StateLabel status="issueClosed" />).container).toMatchSnapshot()
Expand Down Expand Up @@ -64,11 +71,13 @@ describe('StateLabel', () => {
expect(screenArchived.getByText('Archived')).toBeInTheDocument() // text
screenArchived.unmount()
})

it('renders open status without an icon', () => {
const screen = HTMLRender(<StateLabel status="open">Open</StateLabel>)
expect(screen.queryByRole('img')).not.toBeInTheDocument() // svg
expect(screen.getByText('Open')).toBeInTheDocument() // text
})

it('renders closed status without an icon', () => {
const screen = HTMLRender(<StateLabel status="closed">Closed</StateLabel>)
expect(screen.queryByRole('img')).not.toBeInTheDocument() // svg
Expand Down
Loading
Loading