Skip to content
Draft
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
8 changes: 7 additions & 1 deletion packages/react-core/src/components/Page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,13 @@ class Page extends Component<PageProps, PageState> {
)}
>
{skipToContent}
{variant === 'docked' ? <div className={css(styles.pageDock)}>{masthead}</div> : masthead}
{variant === 'docked' ? (
<div className={css(styles.pageDock)}>
<div className={css(styles.pageDockMain)}>{masthead}</div>
</div>
) : (
masthead
)}
{sidebar}
{notificationDrawer && (
<div className={css(styles.pageDrawer)}>
Expand Down
15 changes: 15 additions & 0 deletions packages/react-core/src/components/Page/PageGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
hasOverflowScroll?: boolean;
/** Adds an accessible name to the page group when the hasOverflowScroll prop is set to true. */
'aria-label'?: string;
/** Adds plain styling to the page group. */
isPlain?: boolean;
/** @beta Prevents the page group from automatically applying plain styling when glass theme is enabled. When both this and isPlain are true, isPlain takes precedence. */
isNoPlainOnGlass?: boolean;
}

export const PageGroup = ({
Expand All @@ -38,8 +42,17 @@
hasShadowBottom = false,
hasOverflowScroll = false,
'aria-label': ariaLabel,
isPlain = false,
isNoPlainOnGlass = false,
...props
}: PageGroupProps) => {
if (isPlain && isNoPlainOnGlass) {
// eslint-disable-next-line no-console
console.warn(
`PageGroup: When both isPlain and isNoPlainOnGlass are true, isPlain will take precedence and isNoPlainOnGlass will have no effect. It's recommended to pass only one prop according to the current theme.`
);
}

const { height, getVerticalBreakpoint } = useContext(PageContext);

useEffect(() => {
Expand All @@ -60,6 +73,8 @@
hasShadowTop && styles.modifiers.shadowTop,
hasShadowBottom && styles.modifiers.shadowBottom,
hasOverflowScroll && styles.modifiers.overflowScroll,
isPlain && styles.modifiers.plain,
isNoPlainOnGlass && styles.modifiers.noPlainOnGlass,

Check failure on line 77 in packages/react-core/src/components/Page/PageGroup.tsx

View workflow job for this annotation

GitHub Actions / Build, test & deploy

Property 'noPlainOnGlass' does not exist on type '{ dock: "pf-m-dock"; hamburger: "pf-m-hamburger"; expanded: "pf-m-expanded"; collapsed: "pf-m-collapsed"; vertical: "pf-m-vertical"; textExpanded: "pf-m-text-expanded"; pageInsets: "pf-m-page-insets"; ... 36 more ...; noPaddingOn_2xl: "pf-m-no-padding-on-2xl"; }'.

Check failure on line 77 in packages/react-core/src/components/Page/PageGroup.tsx

View workflow job for this annotation

GitHub Actions / Build

Property 'noPlainOnGlass' does not exist on type '{ dock: "pf-m-dock"; hamburger: "pf-m-hamburger"; expanded: "pf-m-expanded"; collapsed: "pf-m-collapsed"; vertical: "pf-m-vertical"; textExpanded: "pf-m-text-expanded"; pageInsets: "pf-m-page-insets"; ... 36 more ...; noPaddingOn_2xl: "pf-m-no-padding-on-2xl"; }'.
className
)}
{...(hasOverflowScroll && { tabIndex: 0, role: 'region', 'aria-label': ariaLabel })}
Expand Down
15 changes: 15 additions & 0 deletions packages/react-core/src/components/Page/PageSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
'aria-label'?: string;
/** Sets the base component to render. Defaults to section */
component?: keyof React.JSX.IntrinsicElements;
/** Adds plain styling to the page section. */
isPlain?: boolean;
/** @beta Prevents the page section from automatically applying plain styling when glass theme is enabled. When both this and isPlain are true, isPlain takes precedence. */
isNoPlainOnGlass?: boolean;
}

const variantType = {
Expand Down Expand Up @@ -98,8 +102,17 @@
'aria-label': ariaLabel,
component = 'section',
hasBodyWrapper = true,
isPlain = false,
isNoPlainOnGlass = false,
...props
}: PageSectionProps) => {
if (isPlain && isNoPlainOnGlass) {
// eslint-disable-next-line no-console
console.warn(
`PageSection: When both isPlain and isNoPlainOnGlass are true, isPlain will take precedence and isNoPlainOnGlass will have no effect. It's recommended to pass only one prop according to the current theme.`
);
}

const { height, getVerticalBreakpoint } = useContext(PageContext);

useEffect(() => {
Expand All @@ -126,6 +139,8 @@
hasShadowTop && styles.modifiers.shadowTop,
hasShadowBottom && styles.modifiers.shadowBottom,
hasOverflowScroll && styles.modifiers.overflowScroll,
isPlain && styles.modifiers.plain,
isNoPlainOnGlass && styles.modifiers.noPlainOnGlass,

Check failure on line 143 in packages/react-core/src/components/Page/PageSection.tsx

View workflow job for this annotation

GitHub Actions / Build, test & deploy

Property 'noPlainOnGlass' does not exist on type '{ dock: "pf-m-dock"; hamburger: "pf-m-hamburger"; expanded: "pf-m-expanded"; collapsed: "pf-m-collapsed"; vertical: "pf-m-vertical"; textExpanded: "pf-m-text-expanded"; pageInsets: "pf-m-page-insets"; ... 36 more ...; noPaddingOn_2xl: "pf-m-no-padding-on-2xl"; }'.

Check failure on line 143 in packages/react-core/src/components/Page/PageSection.tsx

View workflow job for this annotation

GitHub Actions / Build

Property 'noPlainOnGlass' does not exist on type '{ dock: "pf-m-dock"; hamburger: "pf-m-hamburger"; expanded: "pf-m-expanded"; collapsed: "pf-m-collapsed"; vertical: "pf-m-vertical"; textExpanded: "pf-m-text-expanded"; pageInsets: "pf-m-page-insets"; ... 36 more ...; noPaddingOn_2xl: "pf-m-no-padding-on-2xl"; }'.
className
)}
{...(hasOverflowScroll && { tabIndex: 0 })}
Expand Down
4 changes: 3 additions & 1 deletion packages/react-core/src/components/Page/PageSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ export const PageSidebar: React.FunctionComponent<PageSidebarProps> = ({
aria-hidden={!sidebarOpen}
{...props}
>
<PageSidebarContext.Provider value={{ isSidebarOpen: sidebarOpen }}>{children}</PageSidebarContext.Provider>
<PageSidebarContext.Provider value={{ isSidebarOpen: sidebarOpen }}>
<div className={css(styles.pageSidebarMain)}>{children}</div>
</PageSidebarContext.Provider>
</div>
);
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ exports[`PageSidebar should match snapshot (auto-generated) 1`] = `
class="pf-v6-c-page__sidebar ''"
id="page-sidebar"
>
<div>
ReactNode
<div
class="pf-v6-c-page__sidebar-main"
>
<div>
ReactNode
</div>
</div>
</div>
</DocumentFragment>
Expand Down
14 changes: 14 additions & 0 deletions packages/react-core/src/components/Page/__tests__/Page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,4 +406,18 @@ describe('Page', () => {
render(<Page data-testid="page"></Page>);
expect(screen.getByTestId('page')).not.toHaveClass(styles.modifiers.dock);
});

test(`Renders with ${styles.pageDockMain} wrapper when variant is docked`, () => {
render(<Page masthead={<>Masthead</>} data-testid="page"></Page>);

const pageDockMain = screen.getByText('Masthead').closest(`.${styles.pageDockMain}`);
expect(pageDockMain).not.toBeInTheDocument();
});

test(`Renders with ${styles.pageDockMain} wrapper when variant is docked`, () => {
render(<Page variant="docked" masthead={<>Masthead</>} data-testid="page"></Page>);

const pageDockMain = screen.getByText('Masthead').closest(`.${styles.pageDockMain}`);
expect(pageDockMain).toBeInTheDocument();
});
});
216 changes: 128 additions & 88 deletions packages/react-core/src/components/Page/__tests__/PageGroup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,92 +2,132 @@ import { render, screen } from '@testing-library/react';
import { PageGroup } from '../PageGroup';
import styles from '@patternfly/react-styles/css/components/Page/page';

describe('page group', () => {
test('Verify basic render', () => {
const { asFragment } = render(<PageGroup>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify top sticky', () => {
const { asFragment } = render(<PageGroup stickyOnBreakpoint={{ default: 'top' }}>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify bottom sticky', () => {
const { asFragment } = render(<PageGroup stickyOnBreakpoint={{ default: 'bottom' }}>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify top shadow', () => {
const { asFragment } = render(<PageGroup hasShadowTop>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify bottom shadow', () => {
const { asFragment } = render(<PageGroup hasShadowBottom>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify overflow scroll', () => {
const { asFragment } = render(<PageGroup hasOverflowScroll>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});

test('Renders without an aria-label by default', () => {
render(<PageGroup>test</PageGroup>);

expect(screen.getByText('test')).not.toHaveAccessibleName('Test label');
});

test('Renders with the passed aria-label applied', () => {
render(
<PageGroup aria-label="Test label" hasOverflowScroll>
test
</PageGroup>
);

expect(screen.getByText('test')).toHaveAccessibleName('Test label');
});

test('Does not log a warning in the console by default', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup>test</PageGroup>);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Does not log a warning in the console when an aria-label is included with hasOverflowScroll', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(
<PageGroup hasOverflowScroll aria-label="Test label">
test
</PageGroup>
);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Logs a warning in the console when an aria-label is not included with hasOverflowScroll', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup hasOverflowScroll>test</PageGroup>);

expect(consoleWarning).toHaveBeenCalled();
});

test(`Does not render with ${styles.modifiers.fill} or ${styles.modifiers.noFill} if isFilled is not passed`, () => {
render(<PageGroup>test</PageGroup>);

expect(screen.getByText('test')).not.toHaveClass(styles.modifiers.fill);
expect(screen.getByText('test')).not.toHaveClass(styles.modifiers.noFill);
});

test(`Renders with ${styles.modifiers.fill} if isFilled={true} is passed`, () => {
render(<PageGroup isFilled={true}>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.fill);
});

test(`Renders with ${styles.modifiers.noFill} if isFilled={false} is passed`, () => {
render(<PageGroup isFilled={false}>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.noFill);
});
test('Verify basic render', () => {
const { asFragment } = render(<PageGroup>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify top sticky', () => {
const { asFragment } = render(<PageGroup stickyOnBreakpoint={{ default: 'top' }}>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify bottom sticky', () => {
const { asFragment } = render(<PageGroup stickyOnBreakpoint={{ default: 'bottom' }}>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify top shadow', () => {
const { asFragment } = render(<PageGroup hasShadowTop>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify bottom shadow', () => {
const { asFragment } = render(<PageGroup hasShadowBottom>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});
test('Verify overflow scroll', () => {
const { asFragment } = render(<PageGroup hasOverflowScroll>test</PageGroup>);
expect(asFragment()).toMatchSnapshot();
});

test('Renders without an aria-label by default', () => {
render(<PageGroup>test</PageGroup>);

expect(screen.getByText('test')).not.toHaveAccessibleName('Test label');
});

test('Renders with the passed aria-label applied', () => {
render(
<PageGroup aria-label="Test label" hasOverflowScroll>
test
</PageGroup>
);

expect(screen.getByText('test')).toHaveAccessibleName('Test label');
});

test('Does not log a warning in the console by default', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup>test</PageGroup>);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Does not log a warning in the console when an aria-label is included with hasOverflowScroll', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(
<PageGroup hasOverflowScroll aria-label="Test label">
test
</PageGroup>
);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Logs a warning in the console when an aria-label is not included with hasOverflowScroll', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup hasOverflowScroll>test</PageGroup>);

expect(consoleWarning).toHaveBeenCalled();
});

test(`Does not render with ${styles.modifiers.fill} or ${styles.modifiers.noFill} if isFilled is not passed`, () => {
render(<PageGroup>test</PageGroup>);

expect(screen.getByText('test')).not.toHaveClass(styles.modifiers.fill);
expect(screen.getByText('test')).not.toHaveClass(styles.modifiers.noFill);
});

test(`Renders with ${styles.modifiers.fill} if isFilled={true} is passed`, () => {
render(<PageGroup isFilled={true}>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.fill);
});

test(`Renders with ${styles.modifiers.noFill} if isFilled={false} is passed`, () => {
render(<PageGroup isFilled={false}>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.noFill);
});

test(`Renders with ${styles.modifiers.plain} class when isPlain is true`, () => {
render(<PageGroup isPlain>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.plain);
});

test(`Renders with ${styles.modifiers.noPlainOnGlass} class when isPlain is true`, () => {
render(<PageGroup isNoPlainOnGlass>test</PageGroup>);

expect(screen.getByText('test')).toHaveClass(styles.modifiers.noPlainOnGlass);
});

test('Does not log a warning when only isPlain is passed', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup isPlain>test</PageGroup>);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Does not log a warning when only isNoPlainOnGlass is passed', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(<PageGroup isNoPlainOnGlass>test</PageGroup>);

expect(consoleWarning).not.toHaveBeenCalled();
});

test('Logs a warning when both isPlain and isNoPlainOnGlass are passed', () => {
const consoleWarning = jest.spyOn(console, 'warn').mockImplementation();

render(
<PageGroup isPlain isNoPlainOnGlass>
test
</PageGroup>
);

expect(consoleWarning).toHaveBeenCalledWith(
`PageGroup: When both isPlain and isNoPlainOnGlass are true, isPlain will take precedence and isNoPlainOnGlass will have no effect. It's recommended to pass only one prop according to the current theme.`
);
});
Loading
Loading