From 4ba98fdec29a00a5b3b6b6439073c10971e97552 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 10 Nov 2025 09:41:37 -0500 Subject: [PATCH 1/7] feat(Hero): added component --- packages/react-core/package.json | 2 +- .../src/components/Compass/CompassHero.tsx | 81 +--------- .../Compass/__tests__/CompassHero.test.tsx | 126 +-------------- .../__snapshots__/CompassHero.test.tsx.snap | 11 +- .../Compass/examples/CompassDemo.tsx | 8 +- .../react-core/src/components/Hero/Hero.tsx | 97 ++++++++++++ .../src/components/Hero/HeroBody.tsx | 19 +++ .../components/Hero/__tests__/Hero.test.tsx | 149 ++++++++++++++++++ .../Hero/__tests__/HeroBody.test.tsx | 39 +++++ .../__snapshots__/Hero.test.tsx.snap | 22 +++ .../__snapshots__/HeroBody.test.tsx.snap | 11 ++ .../src/components/Hero/examples/Hero.md | 17 ++ .../components/Hero/examples/HeroBasic.tsx | 7 + .../react-core/src/components/Hero/index.ts | 2 + packages/react-core/src/components/index.ts | 1 + packages/react-docs/package.json | 2 +- packages/react-icons/package.json | 2 +- packages/react-styles/package.json | 2 +- packages/react-tokens/package.json | 2 +- yarn.lock | 18 +-- 20 files changed, 399 insertions(+), 219 deletions(-) create mode 100644 packages/react-core/src/components/Hero/Hero.tsx create mode 100644 packages/react-core/src/components/Hero/HeroBody.tsx create mode 100644 packages/react-core/src/components/Hero/__tests__/Hero.test.tsx create mode 100644 packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx create mode 100644 packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap create mode 100644 packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap create mode 100644 packages/react-core/src/components/Hero/examples/Hero.md create mode 100644 packages/react-core/src/components/Hero/examples/HeroBasic.tsx create mode 100644 packages/react-core/src/components/Hero/index.ts diff --git a/packages/react-core/package.json b/packages/react-core/package.json index 18fe134ecce..dafd1a225a1 100644 --- a/packages/react-core/package.json +++ b/packages/react-core/package.json @@ -54,7 +54,7 @@ "tslib": "^2.8.1" }, "devDependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.19", + "@patternfly/patternfly": "6.5.0-prerelease.21", "case-anything": "^3.1.2", "css": "^3.0.0", "fs-extra": "^11.3.0" diff --git a/packages/react-core/src/components/Compass/CompassHero.tsx b/packages/react-core/src/components/Compass/CompassHero.tsx index 0987c25277e..381ef35f69a 100644 --- a/packages/react-core/src/components/Compass/CompassHero.tsx +++ b/packages/react-core/src/components/Compass/CompassHero.tsx @@ -1,87 +1,18 @@ import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -import compassHeroBackgroundImageLight from '@patternfly/react-tokens/dist/esm/c_compass__hero_BackgroundImage_light'; -import compassHeroBackgroundImageDark from '@patternfly/react-tokens/dist/esm/c_compass__hero_BackgroundImage_dark'; -import compassHeroGradientStop1Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_1_light'; -import compassHeroGradientStop2Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_2_light'; -import compassHeroGradientStop3Light from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_3_light'; -import compassHeroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_1_dark'; -import compassHeroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_2_dark'; -import compassHeroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_compass__hero_gradient_stop_3_dark'; - +/** A wrapper component to pass a PatternFly Hero component into. */ interface CompassHeroProps extends Omit, 'content'> { /** Content of the hero */ children?: React.ReactNode; /** Additional classes added to the hero */ className?: string; - /** Light theme background image path of the hero */ - backgroundSrcLight?: string; - /** Dark theme background image path of the hero */ - backgroundSrcDark?: string; - /** Light theme gradient of the hero */ - gradientLight?: { - stop1?: string; - stop2?: string; - stop3?: string; - }; - /** Dark theme gradient of the hero */ - gradientDark?: { - stop1?: string; - stop2?: string; - stop3?: string; - }; } -export const CompassHero: React.FunctionComponent = ({ - className, - children, - backgroundSrcLight, - backgroundSrcDark, - gradientLight, - gradientDark, - ...props -}) => { - const backgroundImageStyles: { [key: string]: string } = {}; - if (backgroundSrcLight) { - backgroundImageStyles[compassHeroBackgroundImageLight.name] = `url(${backgroundSrcLight})`; - } - if (backgroundSrcDark) { - backgroundImageStyles[compassHeroBackgroundImageDark.name] = `url(${backgroundSrcDark})`; - } - - if (gradientLight) { - if (gradientLight.stop1) { - backgroundImageStyles[compassHeroGradientStop1Light.name] = gradientLight.stop1; - } - if (gradientLight.stop2) { - backgroundImageStyles[compassHeroGradientStop2Light.name] = gradientLight.stop2; - } - if (gradientLight.stop3) { - backgroundImageStyles[compassHeroGradientStop3Light.name] = gradientLight.stop3; - } - } - if (gradientDark) { - if (gradientDark.stop1) { - backgroundImageStyles[compassHeroGradientStop1Dark.name] = gradientDark.stop1; - } - if (gradientDark.stop2) { - backgroundImageStyles[compassHeroGradientStop2Dark.name] = gradientDark.stop2; - } - if (gradientDark.stop3) { - backgroundImageStyles[compassHeroGradientStop3Dark.name] = gradientDark.stop3; - } - } - - return ( -
-
{children}
-
- ); -}; +export const CompassHero: React.FunctionComponent = ({ className, children, ...props }) => ( +
+ {children} +
+); CompassHero.displayName = 'CompassHero'; diff --git a/packages/react-core/src/components/Compass/__tests__/CompassHero.test.tsx b/packages/react-core/src/components/Compass/__tests__/CompassHero.test.tsx index f68ed214b79..c46f8605e73 100644 --- a/packages/react-core/src/components/Compass/__tests__/CompassHero.test.tsx +++ b/packages/react-core/src/components/Compass/__tests__/CompassHero.test.tsx @@ -16,135 +16,23 @@ test('Renders with children', () => { expect(screen.getByText('Test content')).toBeVisible(); }); -test('Renders with custom class name when className prop is provided', () => { - render(Test); - expect(screen.getByText('Test').parentElement).toHaveClass('custom-class'); -}); - -test(`Renders with default ${styles.compassPanel} and ${styles.compassHero} classes on the hero and ${styles.compassHeroBody} class on the hero body`, () => { +test(`Renders with ${styles.compass}__hero class by defaulty`, () => { render(Test); - const heroBodyElement = screen.getByText('Test'); - expect(heroBodyElement).toHaveClass(styles.compassHeroBody); - - const heroElement = heroBodyElement.parentElement; - expect(heroElement).toHaveClass(styles.compassPanel); - expect(heroElement).toHaveClass(styles.compassHero); -}); - -test('Renders with light background image style when backgroundSrcLight is provided', () => { - const backgroundSrc = 'light-bg.jpg'; - render(Test); - expect(screen.getByText('Test').parentElement).toHaveStyle( - `--pf-v6-c-compass__hero--BackgroundImage--light: url(${backgroundSrc})` - ); -}); - -test('Renders with dark background image style when backgroundSrcDark is provided', () => { - const backgroundSrc = 'dark-bg.jpg'; - render(Test); - expect(screen.getByText('Test').parentElement).toHaveStyle( - `--pf-v6-c-compass__hero--BackgroundImage--dark: url(${backgroundSrc})` - ); -}); - -test('Renders with both light and dark background image styles when both are provided', () => { - const lightSrc = 'light-bg.jpg'; - const darkSrc = 'dark-bg.jpg'; - render( - - Test - - ); - const heroElement = screen.getByText('Test').parentElement; - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--BackgroundImage--light: url(${lightSrc})`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--BackgroundImage--dark: url(${darkSrc})`); -}); - -test('Renders with light gradient styles when gradientLight is provided', () => { - const gradient = { - stop1: '#ff0000', - stop2: '#00ff00', - stop3: '#0000ff' - }; - render(Test); - const heroElement = screen.getByText('Test').parentElement; - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--light: ${gradient.stop1}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-2--light: ${gradient.stop2}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-3--light: ${gradient.stop3}`); -}); -test('Renders with dark gradient styles when gradientDark is provided', () => { - const gradient = { - stop1: '#ff0000', - stop2: '#00ff00', - stop3: '#0000ff' - }; - render(Test); - const heroElement = screen.getByText('Test').parentElement; - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--dark: ${gradient.stop1}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-2--dark: ${gradient.stop2}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-3--dark: ${gradient.stop3}`); -}); - -test('Renders with both light and dark gradient styles when both are provided', () => { - const lightGradient = { - stop1: '#ff0000', - stop2: '#00ff00', - stop3: '#0000ff' - }; - const darkGradient = { - stop1: '#000000', - stop2: '#ffffff', - stop3: '#808080' - }; - render( - - Test - - ); - const heroElement = screen.getByText('Test').parentElement; - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--light: ${lightGradient.stop1}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--dark: ${darkGradient.stop1}`); + expect(screen.getByText('Test')).toHaveClass(`${styles.compass}__hero`, { exact: true }); }); -test('Renders with both background images and gradient styles when both are provided', () => { - const lightSrc = 'light-bg.jpg'; - const darkSrc = 'dark-bg.jpg'; - const lightGradient = { stop1: '#ff0000' }; - const darkGradient = { stop1: '#000000' }; - - render( - - Test - - ); - const heroElement = screen.getByText('Test').parentElement; - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--BackgroundImage--light: url(${lightSrc})`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--BackgroundImage--dark: url(${darkSrc})`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--light: ${lightGradient.stop1}`); - expect(heroElement).toHaveStyle(`--pf-v6-c-compass__hero--gradient--stop-1--dark: ${darkGradient.stop1}`); +test('Renders with custom class name when className prop is provided', () => { + render(Test); + expect(screen.getByText('Test')).toHaveClass('custom-class'); }); test('Renders with additional props spread to the component', () => { render(Test); - expect(screen.getByText('Test').parentElement).toHaveAccessibleName('Test label'); + expect(screen.getByText('Test')).toHaveAccessibleName('Test label'); }); test('Matches the snapshot', () => { - const { asFragment } = render( - -
Hero content
-
- ); + const { asFragment } = render(Hero content); expect(asFragment()).toMatchSnapshot(); }); diff --git a/packages/react-core/src/components/Compass/__tests__/__snapshots__/CompassHero.test.tsx.snap b/packages/react-core/src/components/Compass/__tests__/__snapshots__/CompassHero.test.tsx.snap index f130da9f4dc..da16b2c6089 100644 --- a/packages/react-core/src/components/Compass/__tests__/__snapshots__/CompassHero.test.tsx.snap +++ b/packages/react-core/src/components/Compass/__tests__/__snapshots__/CompassHero.test.tsx.snap @@ -3,16 +3,9 @@ exports[`Matches the snapshot 1`] = `
-
-
- Hero content -
-
+ Hero content
`; diff --git a/packages/react-core/src/components/Compass/examples/CompassDemo.tsx b/packages/react-core/src/components/Compass/examples/CompassDemo.tsx index 735cbdf0e3f..b81ad1afea7 100644 --- a/packages/react-core/src/components/Compass/examples/CompassDemo.tsx +++ b/packages/react-core/src/components/Compass/examples/CompassDemo.tsx @@ -7,6 +7,8 @@ import { CompassMainHeader, CompassPanel, CompassMessageBar, + Hero, + HeroBody, Tabs, TabsComponent, Tab, @@ -119,8 +121,10 @@ export const CompassBasic: React.FunctionComponent = () => { const sidebarStartContent = sidebarContent; const mainContent = ( <> - -
Hero
+ + + Hero + Content title} /> diff --git a/packages/react-core/src/components/Hero/Hero.tsx b/packages/react-core/src/components/Hero/Hero.tsx new file mode 100644 index 00000000000..f3ead6d0016 --- /dev/null +++ b/packages/react-core/src/components/Hero/Hero.tsx @@ -0,0 +1,97 @@ +import styles from '@patternfly/react-styles/css/components/Hero/hero'; +import { css } from '@patternfly/react-styles'; +import heroBackgroundImageLight from '@patternfly/react-tokens/dist/esm/c_hero_BackgroundImage_light'; +import heroBackgroundImageDark from '@patternfly/react-tokens/dist/esm/c_hero_BackgroundImage_dark'; +import heroGradientStop1Light from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_1_light'; +import heroGradientStop2Light from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_2_light'; +import heroGradientStop3Light from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_3_light'; +import heroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_1_dark'; +import heroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_2_dark'; +import heroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_3_dark'; + +export interface GradientStops { + stop1?: string; + stop2?: string; + stop3?: string; +} + +/** The main Hero component that allowws adjusting of its background images and gradients in different color modes (such as light and dark). */ + +export interface HeroProps extends Omit, 'content'> { + /** Content of the hero */ + children?: React.ReactNode; + /** Additional classes added to the hero */ + className?: string; + /** Light theme background image path of the hero */ + backgroundSrcLight?: string; + /** Dark theme background image path of the hero */ + backgroundSrcDark?: string; + /** Light theme gradient of the hero, taking any valid CSS color values for each stop property. */ + gradientLight?: { + stop1?: string; + stop2?: string; + stop3?: string; + }; + /** Dark theme gradient of the hero, taking any valid CSS color values for each stop property. */ + gradientDark?: { + stop1?: string; + stop2?: string; + stop3?: string; + }; + /** Flag indicating whether glass styles are removed from the hero when a glass theme is applied. */ + hasNoGlass?: boolean; +} + +export const Hero: React.FunctionComponent = ({ + className, + children, + backgroundSrcLight, + backgroundSrcDark, + gradientLight, + gradientDark, + hasNoGlass = false, + ...props +}) => { + const backgroundImageStyles: { [key: string]: string } = {}; + if (backgroundSrcLight) { + backgroundImageStyles[heroBackgroundImageLight.name] = `url(${backgroundSrcLight})`; + } + if (backgroundSrcDark) { + backgroundImageStyles[heroBackgroundImageDark.name] = `url(${backgroundSrcDark})`; + } + + if (gradientLight) { + if (gradientLight.stop1) { + backgroundImageStyles[heroGradientStop1Light.name] = gradientLight.stop1; + } + if (gradientLight.stop2) { + backgroundImageStyles[heroGradientStop2Light.name] = gradientLight.stop2; + } + if (gradientLight.stop3) { + backgroundImageStyles[heroGradientStop3Light.name] = gradientLight.stop3; + } + } + if (gradientDark) { + if (gradientDark.stop1) { + backgroundImageStyles[heroGradientStop1Dark.name] = gradientDark.stop1; + } + if (gradientDark.stop2) { + backgroundImageStyles[heroGradientStop2Dark.name] = gradientDark.stop2; + } + if (gradientDark.stop3) { + backgroundImageStyles[heroGradientStop3Dark.name] = gradientDark.stop3; + } + } + + return ( +
+ {children} +
+ ); +}; + +Hero.displayName = 'Hero'; diff --git a/packages/react-core/src/components/Hero/HeroBody.tsx b/packages/react-core/src/components/Hero/HeroBody.tsx new file mode 100644 index 00000000000..2a71ea108c7 --- /dev/null +++ b/packages/react-core/src/components/Hero/HeroBody.tsx @@ -0,0 +1,19 @@ +import styles from '@patternfly/react-styles/css/components/Hero/hero'; +import { css } from '@patternfly/react-styles'; + +/** An optional wrapper component for the body content of a Hero that can be used to help control any text content from overlapping a Hero background image. */ + +export interface HeroBodyProps extends Omit, 'content'> { + /** Content of the hero */ + children?: React.ReactNode; + /** Additional classes added to the hero */ + className?: string; +} + +export const HeroBody: React.FunctionComponent = ({ className, children, ...props }) => ( +
+ {children} +
+); + +HeroBody.displayName = 'HeroBody'; diff --git a/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx b/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx new file mode 100644 index 00000000000..55c806b3010 --- /dev/null +++ b/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx @@ -0,0 +1,149 @@ +import { render, screen } from '@testing-library/react'; +import { Hero } from '../Hero'; +import styles from '@patternfly/react-styles/css/components/Hero/hero'; + +test('Renders without children', () => { + render( +
+ +
+ ); + expect(screen.getByTestId('test-hero').firstChild).toBeVisible(); +}); + +test('Renders with children', () => { + render(Test content); + expect(screen.getByText('Test content')).toBeVisible(); +}); + +test(`Renders with ${styles.hero} class by defaulty`, () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass(`${styles.hero}`, { exact: true }); +}); + +test('Renders with custom class name when className prop is provided', () => { + render(Test); + expect(screen.getByText('Test')).toHaveClass('custom-class'); +}); + +test('Renders with additional props spread to the component', () => { + render(Test); + expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id'); +}); + +test('Renders with light background image style when backgroundSrcLight is provided', () => { + const backgroundSrc = 'light-bg.jpg'; + render(Test); + expect(screen.getByText('Test')).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${backgroundSrc})`); +}); + +test('Renders with dark background image style when backgroundSrcDark is provided', () => { + const backgroundSrc = 'dark-bg.jpg'; + render(Test); + expect(screen.getByText('Test')).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${backgroundSrc})`); +}); + +test('Renders with both light and dark background image styles when both are provided', () => { + const lightSrc = 'light-bg.jpg'; + const darkSrc = 'dark-bg.jpg'; + render( + + Test + + ); + const heroElement = screen.getByText('Test'); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${lightSrc})`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${darkSrc})`); +}); + +test('Renders with light gradient styles when gradientLight is provided', () => { + const gradient = { + stop1: '#ff0000', + stop2: '#00ff00', + stop3: '#0000ff' + }; + render(Test); + const heroElement = screen.getByText('Test'); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${gradient.stop1}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-2--light: ${gradient.stop2}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-3--light: ${gradient.stop3}`); +}); + +test('Renders with dark gradient styles when gradientDark is provided', () => { + const gradient = { + stop1: '#ff0000', + stop2: '#00ff00', + stop3: '#0000ff' + }; + render(Test); + const heroElement = screen.getByText('Test'); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${gradient.stop1}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-2--dark: ${gradient.stop2}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-3--dark: ${gradient.stop3}`); +}); + +test('Renders with both light and dark gradient styles when both are provided', () => { + const lightGradient = { + stop1: '#ff0000', + stop2: '#00ff00', + stop3: '#0000ff' + }; + const darkGradient = { + stop1: '#000000', + stop2: '#ffffff', + stop3: '#808080' + }; + render( + + Test + + ); + const heroElement = screen.getByText('Test'); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${lightGradient.stop1}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${darkGradient.stop1}`); +}); + +test('Renders with both background images and gradient styles when both are provided', () => { + const lightSrc = 'light-bg.jpg'; + const darkSrc = 'dark-bg.jpg'; + const lightGradient = { stop1: '#ff0000' }; + const darkGradient = { stop1: '#000000' }; + + render( + + Test + + ); + const heroElement = screen.getByText('Test'); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${lightSrc})`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${darkSrc})`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${lightGradient.stop1}`); + expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${darkGradient.stop1}`); +}); + +test('Matches the snapshot without backgroundSrc and gradient props', () => { + const { asFragment } = render(Hero content); + + expect(asFragment()).toMatchSnapshot(); +}); + +test('Matches the snapshot with backgroundSrc and gradient props', () => { + const { asFragment } = render( + + Hero content + + ); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx b/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx new file mode 100644 index 00000000000..d3c6bccea0b --- /dev/null +++ b/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx @@ -0,0 +1,39 @@ +import { render, screen } from '@testing-library/react'; +import { HeroBody } from '../HeroBody'; +import styles from '@patternfly/react-styles/css/components/Hero/hero'; + +test('Renders without children', () => { + render( +
+ +
+ ); + expect(screen.getByTestId('test-hero').firstChild).toBeVisible(); +}); + +test('Renders with children', () => { + render(Test content); + expect(screen.getByText('Test content')).toBeVisible(); +}); + +test(`Renders with ${styles.heroBody} class by defaulty`, () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass(`${styles.heroBody}`, { exact: true }); +}); + +test('Renders with custom class name when className prop is provided', () => { + render(Test); + expect(screen.getByText('Test')).toHaveClass('custom-class'); +}); + +test('Renders with additional props spread to the component', () => { + render(Test); + expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id'); +}); + +test('Matches snapshot', () => { + const { asFragment } = render(Hero body content); + + expect(asFragment()).toMatchSnapshot(); +}); diff --git a/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap new file mode 100644 index 00000000000..944afb39c8c --- /dev/null +++ b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matches the snapshot with backgroundSrc and gradiant props 1`] = ` + +
+ Hero content +
+
+`; + +exports[`Matches the snapshot without backgroundSrc and gradiant props 1`] = ` + +
+ Hero content +
+
+`; diff --git a/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap b/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap new file mode 100644 index 00000000000..d5674cda832 --- /dev/null +++ b/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Matches snapshot 1`] = ` + +
+ Hero body content +
+
+`; diff --git a/packages/react-core/src/components/Hero/examples/Hero.md b/packages/react-core/src/components/Hero/examples/Hero.md new file mode 100644 index 00000000000..2b2fb448aea --- /dev/null +++ b/packages/react-core/src/components/Hero/examples/Hero.md @@ -0,0 +1,17 @@ +--- +id: Hero +section: components +cssPrefix: pf-v6-c-hero +beta: true +propComponents: ['Hero', 'HeroBody'] +--- + +## Examples + +### Basic + +When + +```ts file="HeroBasic.tsx" + +``` diff --git a/packages/react-core/src/components/Hero/examples/HeroBasic.tsx b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx new file mode 100644 index 00000000000..52276c12a32 --- /dev/null +++ b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx @@ -0,0 +1,7 @@ +import { Hero, HeroBody } from '@patternfly/react-core'; + +export const HeroBasic: React.FunctionComponent = () => ( + + The (super)hero content. + +); diff --git a/packages/react-core/src/components/Hero/index.ts b/packages/react-core/src/components/Hero/index.ts new file mode 100644 index 00000000000..6e222eb6011 --- /dev/null +++ b/packages/react-core/src/components/Hero/index.ts @@ -0,0 +1,2 @@ +export * from './Hero'; +export * from './HeroBody'; diff --git a/packages/react-core/src/components/index.ts b/packages/react-core/src/components/index.ts index 75ee15d8669..9ba1b78ec46 100644 --- a/packages/react-core/src/components/index.ts +++ b/packages/react-core/src/components/index.ts @@ -32,6 +32,7 @@ export * from './FileUpload'; export * from './Form'; export * from './FormSelect'; export * from './HelperText'; +export * from './Hero'; export * from './Hint'; export * from './Icon'; export * from './InputGroup'; diff --git a/packages/react-docs/package.json b/packages/react-docs/package.json index 42d38b92727..a29e982f87b 100644 --- a/packages/react-docs/package.json +++ b/packages/react-docs/package.json @@ -23,7 +23,7 @@ "test:a11y": "patternfly-a11y --config patternfly-a11y.config" }, "dependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.19", + "@patternfly/patternfly": "6.5.0-prerelease.21", "@patternfly/react-charts": "workspace:^", "@patternfly/react-code-editor": "workspace:^", "@patternfly/react-core": "workspace:^", diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json index 3a8262e1e9d..2728cadc260 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -33,7 +33,7 @@ "@fortawesome/free-brands-svg-icons": "^5.15.4", "@fortawesome/free-regular-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4", - "@patternfly/patternfly": "6.5.0-prerelease.19", + "@patternfly/patternfly": "6.5.0-prerelease.21", "fs-extra": "^11.3.0", "tslib": "^2.8.1" }, diff --git a/packages/react-styles/package.json b/packages/react-styles/package.json index dcf24bcdcc5..d2342094cd0 100644 --- a/packages/react-styles/package.json +++ b/packages/react-styles/package.json @@ -19,7 +19,7 @@ "clean": "rimraf dist css" }, "devDependencies": { - "@patternfly/patternfly": "6.5.0-prerelease.19", + "@patternfly/patternfly": "6.5.0-prerelease.21", "change-case": "^5.4.4", "fs-extra": "^11.3.0" }, diff --git a/packages/react-tokens/package.json b/packages/react-tokens/package.json index 504ab80fe60..40653027c9a 100644 --- a/packages/react-tokens/package.json +++ b/packages/react-tokens/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@adobe/css-tools": "^4.4.4", - "@patternfly/patternfly": "6.5.0-prerelease.19", + "@patternfly/patternfly": "6.5.0-prerelease.21", "fs-extra": "^11.3.0" } } diff --git a/yarn.lock b/yarn.lock index 5be9657a36d..abeb14925ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4516,10 +4516,10 @@ __metadata: languageName: node linkType: hard -"@patternfly/patternfly@npm:6.5.0-prerelease.19": - version: 6.5.0-prerelease.19 - resolution: "@patternfly/patternfly@npm:6.5.0-prerelease.19" - checksum: 10c0/581e18b078e146ad72553f8fb1177ffe84e7bfbeb5920f8e7ce7da22dd9c9334fee850cf7e8fe274a8dee72a9b1608aad5cbcc5679b52275970e2801669f56ab +"@patternfly/patternfly@npm:6.5.0-prerelease.21": + version: 6.5.0-prerelease.21 + resolution: "@patternfly/patternfly@npm:6.5.0-prerelease.21" + checksum: 10c0/2de6f81ae974c005d9d38041f1bb93732567aa37fdb5e56e8547a117b126c3649e2c5133d64eb956964cb19ccb28f39f881de41b98232dc2ee83f8f5e125bfaf languageName: node linkType: hard @@ -4617,7 +4617,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-core@workspace:packages/react-core" dependencies: - "@patternfly/patternfly": "npm:6.5.0-prerelease.19" + "@patternfly/patternfly": "npm:6.5.0-prerelease.21" "@patternfly/react-icons": "workspace:^" "@patternfly/react-styles": "workspace:^" "@patternfly/react-tokens": "workspace:^" @@ -4638,7 +4638,7 @@ __metadata: resolution: "@patternfly/react-docs@workspace:packages/react-docs" dependencies: "@patternfly/documentation-framework": "npm:^6.28.9" - "@patternfly/patternfly": "npm:6.5.0-prerelease.19" + "@patternfly/patternfly": "npm:6.5.0-prerelease.21" "@patternfly/patternfly-a11y": "npm:5.1.0" "@patternfly/react-charts": "workspace:^" "@patternfly/react-code-editor": "workspace:^" @@ -4678,7 +4678,7 @@ __metadata: "@fortawesome/free-brands-svg-icons": "npm:^5.15.4" "@fortawesome/free-regular-svg-icons": "npm:^5.15.4" "@fortawesome/free-solid-svg-icons": "npm:^5.15.4" - "@patternfly/patternfly": "npm:6.5.0-prerelease.19" + "@patternfly/patternfly": "npm:6.5.0-prerelease.21" fs-extra: "npm:^11.3.0" tslib: "npm:^2.8.1" peerDependencies: @@ -4763,7 +4763,7 @@ __metadata: version: 0.0.0-use.local resolution: "@patternfly/react-styles@workspace:packages/react-styles" dependencies: - "@patternfly/patternfly": "npm:6.5.0-prerelease.19" + "@patternfly/patternfly": "npm:6.5.0-prerelease.21" change-case: "npm:^5.4.4" fs-extra: "npm:^11.3.0" languageName: unknown @@ -4805,7 +4805,7 @@ __metadata: resolution: "@patternfly/react-tokens@workspace:packages/react-tokens" dependencies: "@adobe/css-tools": "npm:^4.4.4" - "@patternfly/patternfly": "npm:6.5.0-prerelease.19" + "@patternfly/patternfly": "npm:6.5.0-prerelease.21" fs-extra: "npm:^11.3.0" languageName: unknown linkType: soft From 7d570bce65b2e958ca163e4cedcd6aa4692c3171 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 10 Nov 2025 09:49:18 -0500 Subject: [PATCH 2/7] Added example description --- packages/react-core/src/components/Hero/Hero.tsx | 6 ------ packages/react-core/src/components/Hero/examples/Hero.md | 4 +++- .../react-core/src/components/Hero/examples/HeroBasic.tsx | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/react-core/src/components/Hero/Hero.tsx b/packages/react-core/src/components/Hero/Hero.tsx index f3ead6d0016..de41628188b 100644 --- a/packages/react-core/src/components/Hero/Hero.tsx +++ b/packages/react-core/src/components/Hero/Hero.tsx @@ -9,12 +9,6 @@ import heroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_hero_grad import heroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_2_dark'; import heroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_3_dark'; -export interface GradientStops { - stop1?: string; - stop2?: string; - stop3?: string; -} - /** The main Hero component that allowws adjusting of its background images and gradients in different color modes (such as light and dark). */ export interface HeroProps extends Omit, 'content'> { diff --git a/packages/react-core/src/components/Hero/examples/Hero.md b/packages/react-core/src/components/Hero/examples/Hero.md index 2b2fb448aea..b668ea6ffa9 100644 --- a/packages/react-core/src/components/Hero/examples/Hero.md +++ b/packages/react-core/src/components/Hero/examples/Hero.md @@ -10,7 +10,9 @@ propComponents: ['Hero', 'HeroBody'] ### Basic -When +When using a `` with a background image, you can use the `` to prevent text content from overlapping the background image, which helps ensure the text is more easily readable. Keep in mind that you should still check the color contrast when using the `gradientLight` or `gradientDark` properties. + +You can omit the `` for finer control over the text placement, or if no background image is applied to the ``. ```ts file="HeroBasic.tsx" diff --git a/packages/react-core/src/components/Hero/examples/HeroBasic.tsx b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx index 52276c12a32..61cb755a06c 100644 --- a/packages/react-core/src/components/Hero/examples/HeroBasic.tsx +++ b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx @@ -1,7 +1,7 @@ import { Hero, HeroBody } from '@patternfly/react-core'; export const HeroBasic: React.FunctionComponent = () => ( - - The (super)hero content. + + Basic hero content ); From 13e703372f4e14c0fff0e749e5924673d76d347b Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 10 Nov 2025 10:09:44 -0500 Subject: [PATCH 3/7] Added snapshots --- .../Hero/__tests__/__snapshots__/Hero.test.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap index 944afb39c8c..8c45539a80c 100644 --- a/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap +++ b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Matches the snapshot with backgroundSrc and gradiant props 1`] = ` +exports[`Matches the snapshot with backgroundSrc and gradient props 1`] = `
`; -exports[`Matches the snapshot without backgroundSrc and gradiant props 1`] = ` +exports[`Matches the snapshot without backgroundSrc and gradient props 1`] = `
Date: Tue, 11 Nov 2025 09:56:45 -0500 Subject: [PATCH 4/7] Austin and Erin feedback --- .../react-core/src/components/Hero/Hero.tsx | 38 +++++++++---------- .../src/components/Hero/examples/Hero.md | 4 +- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/packages/react-core/src/components/Hero/Hero.tsx b/packages/react-core/src/components/Hero/Hero.tsx index de41628188b..ce9385398ad 100644 --- a/packages/react-core/src/components/Hero/Hero.tsx +++ b/packages/react-core/src/components/Hero/Hero.tsx @@ -9,7 +9,7 @@ import heroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_hero_grad import heroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_2_dark'; import heroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_3_dark'; -/** The main Hero component that allowws adjusting of its background images and gradients in different color modes (such as light and dark). */ +/** The main Hero component that allows adjusting of its background images and gradients in different color modes (such as light and dark). */ export interface HeroProps extends Omit, 'content'> { /** Content of the hero */ @@ -54,27 +54,23 @@ export const Hero: React.FunctionComponent = ({ backgroundImageStyles[heroBackgroundImageDark.name] = `url(${backgroundSrcDark})`; } - if (gradientLight) { - if (gradientLight.stop1) { - backgroundImageStyles[heroGradientStop1Light.name] = gradientLight.stop1; - } - if (gradientLight.stop2) { - backgroundImageStyles[heroGradientStop2Light.name] = gradientLight.stop2; - } - if (gradientLight.stop3) { - backgroundImageStyles[heroGradientStop3Light.name] = gradientLight.stop3; - } + if (gradientLight?.stop1) { + backgroundImageStyles[heroGradientStop1Light.name] = gradientLight.stop1; } - if (gradientDark) { - if (gradientDark.stop1) { - backgroundImageStyles[heroGradientStop1Dark.name] = gradientDark.stop1; - } - if (gradientDark.stop2) { - backgroundImageStyles[heroGradientStop2Dark.name] = gradientDark.stop2; - } - if (gradientDark.stop3) { - backgroundImageStyles[heroGradientStop3Dark.name] = gradientDark.stop3; - } + if (gradientLight?.stop2) { + backgroundImageStyles[heroGradientStop2Light.name] = gradientLight.stop2; + } + if (gradientLight?.stop3) { + backgroundImageStyles[heroGradientStop3Light.name] = gradientLight.stop3; + } + if (gradientDark?.stop1) { + backgroundImageStyles[heroGradientStop1Dark.name] = gradientDark.stop1; + } + if (gradientDark?.stop2) { + backgroundImageStyles[heroGradientStop2Dark.name] = gradientDark.stop2; + } + if (gradientDark?.stop3) { + backgroundImageStyles[heroGradientStop3Dark.name] = gradientDark.stop3; } return ( diff --git a/packages/react-core/src/components/Hero/examples/Hero.md b/packages/react-core/src/components/Hero/examples/Hero.md index b668ea6ffa9..4070a7686b3 100644 --- a/packages/react-core/src/components/Hero/examples/Hero.md +++ b/packages/react-core/src/components/Hero/examples/Hero.md @@ -10,9 +10,9 @@ propComponents: ['Hero', 'HeroBody'] ### Basic -When using a `` with a background image, you can use the `` to prevent text content from overlapping the background image, which helps ensure the text is more easily readable. Keep in mind that you should still check the color contrast when using the `gradientLight` or `gradientDark` properties. +To ensure readability and prevent text from overlapping with the background image, use the `` component. The `` is optional and can be omitted if there's no background image or if you'd like finer control over text placement. -You can omit the `` for finer control over the text placement, or if no background image is applied to the ``. +When using `gradientLight` or `gradientDark`, regardless of the presence of a ``, check the color contrast to ensure proper accessibility. ```ts file="HeroBasic.tsx" From 5ffe512ca59a192b089a9ab61386dc61e297873f Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Tue, 11 Nov 2025 11:58:24 -0500 Subject: [PATCH 5/7] Updated to bake-in HeroBody in main Hero component --- .../react-core/src/components/Hero/Hero.tsx | 37 +++++++++---- .../src/components/Hero/HeroBody.tsx | 19 ------- .../components/Hero/__tests__/Hero.test.tsx | 52 ++++++++++++++----- .../Hero/__tests__/HeroBody.test.tsx | 39 -------------- .../__snapshots__/Hero.test.tsx.snap | 12 ++++- .../__snapshots__/HeroBody.test.tsx.snap | 11 ---- .../src/components/Hero/examples/Hero.md | 6 +-- .../components/Hero/examples/HeroBasic.tsx | 8 +-- .../react-core/src/components/Hero/index.ts | 1 - 9 files changed, 80 insertions(+), 105 deletions(-) delete mode 100644 packages/react-core/src/components/Hero/HeroBody.tsx delete mode 100644 packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx delete mode 100644 packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap diff --git a/packages/react-core/src/components/Hero/Hero.tsx b/packages/react-core/src/components/Hero/Hero.tsx index ce9385398ad..feec306183e 100644 --- a/packages/react-core/src/components/Hero/Hero.tsx +++ b/packages/react-core/src/components/Hero/Hero.tsx @@ -8,6 +8,8 @@ import heroGradientStop3Light from '@patternfly/react-tokens/dist/esm/c_hero_gra import heroGradientStop1Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_1_dark'; import heroGradientStop2Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_2_dark'; import heroGradientStop3Dark from '@patternfly/react-tokens/dist/esm/c_hero_gradient_stop_3_dark'; +import heroBodyWidth from '@patternfly/react-tokens/dist/esm/c_hero__body_Width'; +import heroBodyMaxWidth from '@patternfly/react-tokens/dist/esm/c_hero__body_MaxWidth'; /** The main Hero component that allows adjusting of its background images and gradients in different color modes (such as light and dark). */ @@ -34,6 +36,10 @@ export interface HeroProps extends Omit, 'conten }; /** Flag indicating whether glass styles are removed from the hero when a glass theme is applied. */ hasNoGlass?: boolean; + /** Modifies the width of the hero body. */ + bodyWidth?: string; + /** Modifies the max-width of the hero body. */ + bodyMaxWidth?: string; } export const Hero: React.FunctionComponent = ({ @@ -44,42 +50,51 @@ export const Hero: React.FunctionComponent = ({ gradientLight, gradientDark, hasNoGlass = false, + bodyWidth, + bodyMaxWidth, ...props }) => { - const backgroundImageStyles: { [key: string]: string } = {}; + const customStyles: { [key: string]: string } = {}; if (backgroundSrcLight) { - backgroundImageStyles[heroBackgroundImageLight.name] = `url(${backgroundSrcLight})`; + customStyles[heroBackgroundImageLight.name] = `url(${backgroundSrcLight})`; } if (backgroundSrcDark) { - backgroundImageStyles[heroBackgroundImageDark.name] = `url(${backgroundSrcDark})`; + customStyles[heroBackgroundImageDark.name] = `url(${backgroundSrcDark})`; } if (gradientLight?.stop1) { - backgroundImageStyles[heroGradientStop1Light.name] = gradientLight.stop1; + customStyles[heroGradientStop1Light.name] = gradientLight.stop1; } if (gradientLight?.stop2) { - backgroundImageStyles[heroGradientStop2Light.name] = gradientLight.stop2; + customStyles[heroGradientStop2Light.name] = gradientLight.stop2; } if (gradientLight?.stop3) { - backgroundImageStyles[heroGradientStop3Light.name] = gradientLight.stop3; + customStyles[heroGradientStop3Light.name] = gradientLight.stop3; } if (gradientDark?.stop1) { - backgroundImageStyles[heroGradientStop1Dark.name] = gradientDark.stop1; + customStyles[heroGradientStop1Dark.name] = gradientDark.stop1; } if (gradientDark?.stop2) { - backgroundImageStyles[heroGradientStop2Dark.name] = gradientDark.stop2; + customStyles[heroGradientStop2Dark.name] = gradientDark.stop2; } if (gradientDark?.stop3) { - backgroundImageStyles[heroGradientStop3Dark.name] = gradientDark.stop3; + customStyles[heroGradientStop3Dark.name] = gradientDark.stop3; + } + + if (bodyWidth) { + customStyles[heroBodyWidth.name] = bodyWidth; + } + if (bodyMaxWidth) { + customStyles[heroBodyMaxWidth.name] = bodyMaxWidth; } return (
- {children} +
{children}
); }; diff --git a/packages/react-core/src/components/Hero/HeroBody.tsx b/packages/react-core/src/components/Hero/HeroBody.tsx deleted file mode 100644 index 2a71ea108c7..00000000000 --- a/packages/react-core/src/components/Hero/HeroBody.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import styles from '@patternfly/react-styles/css/components/Hero/hero'; -import { css } from '@patternfly/react-styles'; - -/** An optional wrapper component for the body content of a Hero that can be used to help control any text content from overlapping a Hero background image. */ - -export interface HeroBodyProps extends Omit, 'content'> { - /** Content of the hero */ - children?: React.ReactNode; - /** Additional classes added to the hero */ - className?: string; -} - -export const HeroBody: React.FunctionComponent = ({ className, children, ...props }) => ( -
- {children} -
-); - -HeroBody.displayName = 'HeroBody'; diff --git a/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx b/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx index 55c806b3010..ee290be3aa8 100644 --- a/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx +++ b/packages/react-core/src/components/Hero/__tests__/Hero.test.tsx @@ -16,32 +16,42 @@ test('Renders with children', () => { expect(screen.getByText('Test content')).toBeVisible(); }); -test(`Renders with ${styles.hero} class by defaulty`, () => { +test(`Renders with ${styles.hero} class on wrapper by defaulty`, () => { render(Test); - expect(screen.getByText('Test')).toHaveClass(`${styles.hero}`, { exact: true }); + expect(screen.getByText('Test').parentElement).toHaveClass(`${styles.hero}`, { exact: true }); }); -test('Renders with custom class name when className prop is provided', () => { +test('Renders with custom class name on wrapper when className prop is provided', () => { render(Test); - expect(screen.getByText('Test')).toHaveClass('custom-class'); + expect(screen.getByText('Test').parentElement).toHaveClass('custom-class'); }); -test('Renders with additional props spread to the component', () => { +test(`Renders with class ${styles.heroBody} on content element`, () => { + render(Test); + + expect(screen.getByText('Test')).toHaveClass(`${styles.heroBody}`, { exact: true }); +}); + +test('Renders with additional props spread to the wrapper component', () => { render(Test); - expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id'); + expect(screen.getByText('Test').parentElement).toHaveAttribute('id', 'custom-id'); }); test('Renders with light background image style when backgroundSrcLight is provided', () => { const backgroundSrc = 'light-bg.jpg'; render(Test); - expect(screen.getByText('Test')).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${backgroundSrc})`); + expect(screen.getByText('Test').parentElement).toHaveStyle( + `--pf-v6-c-hero--BackgroundImage--light: url(${backgroundSrc})` + ); }); test('Renders with dark background image style when backgroundSrcDark is provided', () => { const backgroundSrc = 'dark-bg.jpg'; render(Test); - expect(screen.getByText('Test')).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${backgroundSrc})`); + expect(screen.getByText('Test').parentElement).toHaveStyle( + `--pf-v6-c-hero--BackgroundImage--dark: url(${backgroundSrc})` + ); }); test('Renders with both light and dark background image styles when both are provided', () => { @@ -52,7 +62,7 @@ test('Renders with both light and dark background image styles when both are pro Test
); - const heroElement = screen.getByText('Test'); + const heroElement = screen.getByText('Test').parentElement; expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${lightSrc})`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${darkSrc})`); }); @@ -64,7 +74,7 @@ test('Renders with light gradient styles when gradientLight is provided', () => stop3: '#0000ff' }; render(Test); - const heroElement = screen.getByText('Test'); + const heroElement = screen.getByText('Test').parentElement; expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${gradient.stop1}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-2--light: ${gradient.stop2}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-3--light: ${gradient.stop3}`); @@ -77,7 +87,7 @@ test('Renders with dark gradient styles when gradientDark is provided', () => { stop3: '#0000ff' }; render(Test); - const heroElement = screen.getByText('Test'); + const heroElement = screen.getByText('Test').parentElement; expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${gradient.stop1}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-2--dark: ${gradient.stop2}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-3--dark: ${gradient.stop3}`); @@ -99,7 +109,7 @@ test('Renders with both light and dark gradient styles when both are provided', Test
); - const heroElement = screen.getByText('Test'); + const heroElement = screen.getByText('Test').parentElement; expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${lightGradient.stop1}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${darkGradient.stop1}`); }); @@ -120,13 +130,29 @@ test('Renders with both background images and gradient styles when both are prov Test ); - const heroElement = screen.getByText('Test'); + const heroElement = screen.getByText('Test').parentElement; expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--light: url(${lightSrc})`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--BackgroundImage--dark: url(${darkSrc})`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--light: ${lightGradient.stop1}`); expect(heroElement).toHaveStyle(`--pf-v6-c-hero--gradient--stop-1--dark: ${darkGradient.stop1}`); }); +test('Renders with inline width style when bodyWidth is passed', () => { + const bodyWidth = '100px'; + + render(Test); + const heroElement = screen.getByText('Test').parentElement; + expect(heroElement).toHaveStyle(`--pf-v6-c-hero__body--Width: ${bodyWidth}`); +}); + +test('Renders with inline max-width style when bodyMaxWidth is passed', () => { + const bodyMaxWidth = '100px'; + + render(Test); + const heroElement = screen.getByText('Test').parentElement; + expect(heroElement).toHaveStyle(`--pf-v6-c-hero__body--MaxWidth: ${bodyMaxWidth}`); +}); + test('Matches the snapshot without backgroundSrc and gradient props', () => { const { asFragment } = render(Hero content); diff --git a/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx b/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx deleted file mode 100644 index d3c6bccea0b..00000000000 --- a/packages/react-core/src/components/Hero/__tests__/HeroBody.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { HeroBody } from '../HeroBody'; -import styles from '@patternfly/react-styles/css/components/Hero/hero'; - -test('Renders without children', () => { - render( -
- -
- ); - expect(screen.getByTestId('test-hero').firstChild).toBeVisible(); -}); - -test('Renders with children', () => { - render(Test content); - expect(screen.getByText('Test content')).toBeVisible(); -}); - -test(`Renders with ${styles.heroBody} class by defaulty`, () => { - render(Test); - - expect(screen.getByText('Test')).toHaveClass(`${styles.heroBody}`, { exact: true }); -}); - -test('Renders with custom class name when className prop is provided', () => { - render(Test); - expect(screen.getByText('Test')).toHaveClass('custom-class'); -}); - -test('Renders with additional props spread to the component', () => { - render(Test); - expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id'); -}); - -test('Matches snapshot', () => { - const { asFragment } = render(Hero body content); - - expect(asFragment()).toMatchSnapshot(); -}); diff --git a/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap index 8c45539a80c..ad439bf2870 100644 --- a/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap +++ b/packages/react-core/src/components/Hero/__tests__/__snapshots__/Hero.test.tsx.snap @@ -6,7 +6,11 @@ exports[`Matches the snapshot with backgroundSrc and gradient props 1`] = ` class="pf-v6-c-hero" style="--pf-v6-c-hero--BackgroundImage--light: url(light.jpg); --pf-v6-c-hero--BackgroundImage--dark: url(dark.jpg); --pf-v6-c-hero--gradient--stop-1--light: #ff0000; --pf-v6-c-hero--gradient--stop-2--light: #00ff00; --pf-v6-c-hero--gradient--stop-3--light: #0000ff; --pf-v6-c-hero--gradient--stop-1--dark: #000000; --pf-v6-c-hero--gradient--stop-2--dark: #ffffff; --pf-v6-c-hero--gradient--stop-3--dark: #808080;" > - Hero content +
+ Hero content +
`; @@ -16,7 +20,11 @@ exports[`Matches the snapshot without backgroundSrc and gradient props 1`] = `
- Hero content +
+ Hero content +
`; diff --git a/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap b/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap deleted file mode 100644 index d5674cda832..00000000000 --- a/packages/react-core/src/components/Hero/__tests__/__snapshots__/HeroBody.test.tsx.snap +++ /dev/null @@ -1,11 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Matches snapshot 1`] = ` - -
- Hero body content -
-
-`; diff --git a/packages/react-core/src/components/Hero/examples/Hero.md b/packages/react-core/src/components/Hero/examples/Hero.md index 4070a7686b3..d51fae0e298 100644 --- a/packages/react-core/src/components/Hero/examples/Hero.md +++ b/packages/react-core/src/components/Hero/examples/Hero.md @@ -3,16 +3,16 @@ id: Hero section: components cssPrefix: pf-v6-c-hero beta: true -propComponents: ['Hero', 'HeroBody'] +propComponents: ['Hero'] --- ## Examples ### Basic -To ensure readability and prevent text from overlapping with the background image, use the `` component. The `` is optional and can be omitted if there's no background image or if you'd like finer control over text placement. +The `bodyWidth` and `bodyMaxWidth` properties can be used for finer control over the placement of text content within the ``, such as if you omit a background image. Be mindful of adjusting these properties when a background image is still present, as you may need to ensure the contrast of text is sufficient should it overlap the image. -When using `gradientLight` or `gradientDark`, regardless of the presence of a ``, check the color contrast to ensure proper accessibility. +When using `gradientLight` or `gradientDark`, check the color contrast to ensure proper accessibility. ```ts file="HeroBasic.tsx" diff --git a/packages/react-core/src/components/Hero/examples/HeroBasic.tsx b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx index 61cb755a06c..0f428d4edd4 100644 --- a/packages/react-core/src/components/Hero/examples/HeroBasic.tsx +++ b/packages/react-core/src/components/Hero/examples/HeroBasic.tsx @@ -1,7 +1,3 @@ -import { Hero, HeroBody } from '@patternfly/react-core'; +import { Hero } from '@patternfly/react-core'; -export const HeroBasic: React.FunctionComponent = () => ( - - Basic hero content - -); +export const HeroBasic: React.FunctionComponent = () => Basic hero content; diff --git a/packages/react-core/src/components/Hero/index.ts b/packages/react-core/src/components/Hero/index.ts index 6e222eb6011..729bca95fb2 100644 --- a/packages/react-core/src/components/Hero/index.ts +++ b/packages/react-core/src/components/Hero/index.ts @@ -1,2 +1 @@ export * from './Hero'; -export * from './HeroBody'; From af884b4b844e0fdff0a2a11ef4b822999001ed1e Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Wed, 12 Nov 2025 08:16:24 -0500 Subject: [PATCH 6/7] Removed HeroBody instances, example verbiage updated --- .../src/components/Compass/examples/CompassDemo.tsx | 5 +---- packages/react-core/src/components/Hero/examples/Hero.md | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/react-core/src/components/Compass/examples/CompassDemo.tsx b/packages/react-core/src/components/Compass/examples/CompassDemo.tsx index b81ad1afea7..f2c08e72180 100644 --- a/packages/react-core/src/components/Compass/examples/CompassDemo.tsx +++ b/packages/react-core/src/components/Compass/examples/CompassDemo.tsx @@ -8,7 +8,6 @@ import { CompassPanel, CompassMessageBar, Hero, - HeroBody, Tabs, TabsComponent, Tab, @@ -122,9 +121,7 @@ export const CompassBasic: React.FunctionComponent = () => { const mainContent = ( <> - - Hero - + Hero Content title} /> diff --git a/packages/react-core/src/components/Hero/examples/Hero.md b/packages/react-core/src/components/Hero/examples/Hero.md index d51fae0e298..5539d1e0057 100644 --- a/packages/react-core/src/components/Hero/examples/Hero.md +++ b/packages/react-core/src/components/Hero/examples/Hero.md @@ -10,9 +10,9 @@ propComponents: ['Hero'] ### Basic -The `bodyWidth` and `bodyMaxWidth` properties can be used for finer control over the placement of text content within the ``, such as if you omit a background image. Be mindful of adjusting these properties when a background image is still present, as you may need to ensure the contrast of text is sufficient should it overlap the image. +If you need finer control over the placement of text content within the ``, such as when you omit a background image, adjust the `bodyWidth` and `bodyMaxWidth` properties. Be mindful of using these properties when a background image is still present and ensure there is sufficient contrast between text and any part of the image that it overlaps. -When using `gradientLight` or `gradientDark`, check the color contrast to ensure proper accessibility. +When using `gradientLight` or `gradientDark` to apply gradient backgrounds, check the color contrast to ensure proper accessibility. ```ts file="HeroBasic.tsx" From d281ae6a996adb28cf61b1b354106a7d56ee0e50 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Wed, 12 Nov 2025 11:53:09 -0500 Subject: [PATCH 7/7] Exported all compass interfaces --- packages/react-core/src/components/Compass/CompassContent.tsx | 2 +- packages/react-core/src/components/Compass/CompassHeader.tsx | 2 +- packages/react-core/src/components/Compass/CompassHero.tsx | 2 +- .../react-core/src/components/Compass/CompassMainHeader.tsx | 2 +- .../react-core/src/components/Compass/CompassMessageBar.tsx | 2 +- packages/react-core/src/components/Compass/CompassPanel.tsx | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-core/src/components/Compass/CompassContent.tsx b/packages/react-core/src/components/Compass/CompassContent.tsx index 09b3dc1cb19..cfd9a74219d 100644 --- a/packages/react-core/src/components/Compass/CompassContent.tsx +++ b/packages/react-core/src/components/Compass/CompassContent.tsx @@ -2,7 +2,7 @@ import { Drawer, DrawerContent, DrawerProps } from '../Drawer'; import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -interface CompassContentProps extends React.HTMLProps { +export interface CompassContentProps extends React.HTMLProps { /** Content of the main compass area. Typically one or more CompassPanel components. */ children: React.ReactNode; /** Additional classes added to the CompassContent */ diff --git a/packages/react-core/src/components/Compass/CompassHeader.tsx b/packages/react-core/src/components/Compass/CompassHeader.tsx index c249c1d8858..0d2896c2982 100644 --- a/packages/react-core/src/components/Compass/CompassHeader.tsx +++ b/packages/react-core/src/components/Compass/CompassHeader.tsx @@ -1,7 +1,7 @@ import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -interface CompassHeaderProps { +export interface CompassHeaderProps { /** Content of the logo area */ logo?: React.ReactNode; /** Content of the navigation area */ diff --git a/packages/react-core/src/components/Compass/CompassHero.tsx b/packages/react-core/src/components/Compass/CompassHero.tsx index 381ef35f69a..6b28eb4af7f 100644 --- a/packages/react-core/src/components/Compass/CompassHero.tsx +++ b/packages/react-core/src/components/Compass/CompassHero.tsx @@ -2,7 +2,7 @@ import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; /** A wrapper component to pass a PatternFly Hero component into. */ -interface CompassHeroProps extends Omit, 'content'> { +export interface CompassHeroProps extends Omit, 'content'> { /** Content of the hero */ children?: React.ReactNode; /** Additional classes added to the hero */ diff --git a/packages/react-core/src/components/Compass/CompassMainHeader.tsx b/packages/react-core/src/components/Compass/CompassMainHeader.tsx index 409f7b2e454..cb3b6528d1d 100644 --- a/packages/react-core/src/components/Compass/CompassMainHeader.tsx +++ b/packages/react-core/src/components/Compass/CompassMainHeader.tsx @@ -3,7 +3,7 @@ import { CompassPanel } from './CompassPanel'; import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -interface CompassMainHeaderProps extends Omit, 'title'> { +export interface CompassMainHeaderProps extends Omit, 'title'> { /** Additional classes added to the main header */ className?: string; /** Styled title. If title or toolbar is provided, the children will be ignored. */ diff --git a/packages/react-core/src/components/Compass/CompassMessageBar.tsx b/packages/react-core/src/components/Compass/CompassMessageBar.tsx index 4e9a737aa37..257a9b422dc 100644 --- a/packages/react-core/src/components/Compass/CompassMessageBar.tsx +++ b/packages/react-core/src/components/Compass/CompassMessageBar.tsx @@ -1,7 +1,7 @@ import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -interface CompassMessageBarProps extends React.HTMLProps { +export interface CompassMessageBarProps extends React.HTMLProps { /** Content of the message bar. Typically a @patternfly/chatbot MessageBar component. */ children?: React.ReactNode; /** Additional classes added to the message bar */ diff --git a/packages/react-core/src/components/Compass/CompassPanel.tsx b/packages/react-core/src/components/Compass/CompassPanel.tsx index 6b8096a51e6..bb62112f237 100644 --- a/packages/react-core/src/components/Compass/CompassPanel.tsx +++ b/packages/react-core/src/components/Compass/CompassPanel.tsx @@ -1,7 +1,7 @@ import styles from '@patternfly/react-styles/css/components/Compass/compass'; import { css } from '@patternfly/react-styles'; -interface CompassPanelProps extends React.HTMLProps { +export interface CompassPanelProps extends React.HTMLProps { /** Content of the panel. */ children: React.ReactNode; /** Additional classes added to the panel. */