diff --git a/.changeset/pink-windows-add.md b/.changeset/pink-windows-add.md new file mode 100644 index 00000000000..96bb3e319f4 --- /dev/null +++ b/.changeset/pink-windows-add.md @@ -0,0 +1,5 @@ +--- +"@primer/react": minor +--- + +Adds `hasBorder` prop to PageHeader to allow a bottom border diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-colorblind-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-colorblind-linux.png new file mode 100644 index 00000000000..36e53264c8a Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-dimmed-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-dimmed-linux.png new file mode 100644 index 00000000000..40e9d7f882e Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-dimmed-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-high-contrast-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-high-contrast-linux.png new file mode 100644 index 00000000000..9e147048a42 Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-linux.png new file mode 100644 index 00000000000..36e53264c8a Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-tritanopia-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-tritanopia-linux.png new file mode 100644 index 00000000000..36e53264c8a Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-dark-tritanopia-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-colorblind-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-colorblind-linux.png new file mode 100644 index 00000000000..7f4a3d0c02f Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-colorblind-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-high-contrast-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-high-contrast-linux.png new file mode 100644 index 00000000000..3847974402c Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-high-contrast-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-linux.png new file mode 100644 index 00000000000..7f4a3d0c02f Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-linux.png differ diff --git a/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-tritanopia-linux.png b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-tritanopia-linux.png new file mode 100644 index 00000000000..7f4a3d0c02f Binary files /dev/null and b/.playwright/snapshots/components/PageHeader.test.ts-snapshots/PageHeader-Has-Border-light-tritanopia-linux.png differ diff --git a/e2e/components/PageHeader.test.ts b/e2e/components/PageHeader.test.ts index c183cb247fe..f641d5f1af1 100644 --- a/e2e/components/PageHeader.test.ts +++ b/e2e/components/PageHeader.test.ts @@ -185,6 +185,24 @@ test.describe('PageHeader', () => { } }) + test.describe('Has Border', () => { + for (const theme of themes) { + test.describe(theme, () => { + test('default @vrt', async ({page}) => { + await visit(page, { + id: 'components-pageheader-features--has-bottom-border', + globals: { + colorScheme: theme, + }, + }) + + // Default state + expect(await page.screenshot()).toMatchSnapshot(`PageHeader.Has Border.${theme}.png`) + }) + }) + } + }) + test.describe('Has Large Title', () => { for (const theme of themes) { test.describe(theme, () => { diff --git a/packages/react/src/PageHeader/PageHeader.docs.json b/packages/react/src/PageHeader/PageHeader.docs.json index 5a599706f2b..e2d05a1cc5e 100644 --- a/packages/react/src/PageHeader/PageHeader.docs.json +++ b/packages/react/src/PageHeader/PageHeader.docs.json @@ -77,6 +77,11 @@ "name": "as", "type": "React.ElementType", "defaultValue": "\"div\"" + }, + { + "name": "hasBorder", + "type": "boolean", + "description": "Whether to render a border below the PageHeader. This border will NOT be rendered if the PageHeader has a `PageHeader.Navigation` child that is not hidden at the current breakpoint." } ], "subcomponents": [ diff --git a/packages/react/src/PageHeader/PageHeader.features.stories.tsx b/packages/react/src/PageHeader/PageHeader.features.stories.tsx index 5df0ce6caae..7c21f166b9b 100644 --- a/packages/react/src/PageHeader/PageHeader.features.stories.tsx +++ b/packages/react/src/PageHeader/PageHeader.features.stories.tsx @@ -266,4 +266,14 @@ export const WithActionsThatHaveResponsiveContent = () => ( ) +export const HasBottomBorder = () => ( + + + + Title + + + +) + export default meta diff --git a/packages/react/src/PageHeader/PageHeader.module.css b/packages/react/src/PageHeader/PageHeader.module.css index 1d84ab75981..55fa99714c5 100644 --- a/packages/react/src/PageHeader/PageHeader.module.css +++ b/packages/react/src/PageHeader/PageHeader.module.css @@ -58,6 +58,33 @@ --title-line-height: var(--custom-line-height, var(--text-title-lineHeight-medium, 1.6)); } + &[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-all]), + &[data-has-border='true']:not(:has([data-component='PH_Navigation'])) { + border-block-end: var(--borderWidth-thin) solid var(--borderColor-default); + padding-block-end: var(--base-size-8); + } + + @media screen and (max-width: 768px) { + &[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-narrow]) { + border-block-end: var(--borderWidth-thin) solid var(--borderColor-default); + padding-block-end: var(--base-size-8); + } + } + + @media screen and (min-width: 768px) { + &[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-regular]) { + border-block-end: var(--borderWidth-thin) solid var(--borderColor-default); + padding-block-end: var(--base-size-8); + } + } + + @media screen and (min-width: 1440px) { + &[data-has-border='true']:has([data-component='PH_Navigation'][data-hidden-wide]) { + border-block-end: var(--borderWidth-thin) solid var(--borderColor-default); + padding-block-end: var(--base-size-8); + } + } + & [data-component='PH_LeadingAction'], & [data-component='PH_TrailingAction'], & [data-component='PH_Actions'], diff --git a/packages/react/src/PageHeader/PageHeader.stories.tsx b/packages/react/src/PageHeader/PageHeader.stories.tsx index 7341e7d9ec2..65d7400bab8 100644 --- a/packages/react/src/PageHeader/PageHeader.stories.tsx +++ b/packages/react/src/PageHeader/PageHeader.stories.tsx @@ -59,6 +59,9 @@ const meta: Meta = { description: 'ContextArea is only visible on narrow viewports by default to provide user context of where they are at their journey.', }, + hasBorder: { + type: 'boolean', + }, ParentLink: { type: 'string', if: {arg: 'hasContextArea'}, @@ -189,7 +192,7 @@ export default meta export const Playground: StoryFn = args => ( - + >( - ({children, className, sx = {}, as = 'div', 'aria-label': ariaLabel, role}, forwardedRef) => { + ({children, className, sx = {}, as = 'div', 'aria-label': ariaLabel, role, hasBorder}, forwardedRef) => { const enabled = useFeatureFlag(CSS_MODULES_FEATURE_FLAG) const rootStyles = { @@ -116,6 +117,29 @@ const Root = React.forwardRef(forwardedRef as React.RefObject) @@ -176,6 +200,7 @@ const Root = React.forwardRef(rootStyles, sx)} aria-label={ariaLabel} role={role} @@ -754,6 +779,7 @@ const Navigation: React.FC> = ({ aria-label={as === 'nav' ? ariaLabel : undefined} aria-labelledby={as === 'nav' ? ariaLabelledBy : undefined} className={clsx(enabled && classes.Navigation, className)} + data-component="PH_Navigation" sx={ enabled ? sx @@ -773,7 +799,9 @@ const Navigation: React.FC> = ({ sx, ) } - {...getHiddenDataAttributes(enabled, hidden)} + // passing `true` always get the data attributes for the hidden prop, + // not just for CSS modules + {...getHiddenDataAttributes(true, hidden)} > {children}