Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Page): updated logic to allow section grouping #10650

Merged
merged 10 commits into from
Jul 15, 2024
Merged
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
32 changes: 11 additions & 21 deletions packages/react-core/src/components/Page/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { css } from '@patternfly/react-styles';
import globalBreakpointXl from '@patternfly/react-tokens/dist/esm/global_breakpoint_xl';
import { debounce, canUseDOM } from '../../helpers/util';
import { Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent } from '../Drawer';
import { PageBreadcrumbProps } from './PageBreadcrumb';
import { PageBreadcrumb, PageBreadcrumbProps } from './PageBreadcrumb';
import { PageGroup, PageGroupProps } from './PageGroup';
import { getResizeObserver } from '../../helpers/resizeObserver';
import { formatBreakpointMods, getBreakpoint, getVerticalBreakpoint } from '../../helpers/util';
import { getBreakpoint, getVerticalBreakpoint } from '../../helpers/util';
import { PageContextProvider } from './PageContext';
import { PageBody } from './PageBody';

export enum PageLayouts {
vertical = 'vertical',
Expand Down Expand Up @@ -263,32 +264,21 @@ class Page extends React.Component<PageProps, PageState> {
};

let nav = null;
if (horizontalSubnav && isHorizontalSubnavWidthLimited) {
if (horizontalSubnav) {
nav = (
<div className={css(styles.pageMainNav, styles.modifiers.limitWidth)}>
<div className={css(styles.pageMainBody)}>{horizontalSubnav}</div>
<div className={css(styles.pageMainSubnav, isHorizontalSubnavWidthLimited && styles.modifiers.limitWidth)}>
<PageBody>{horizontalSubnav}</PageBody>
</div>
);
} else if (horizontalSubnav) {
nav = <div className={css(styles.pageMainNav)}>{horizontalSubnav}</div>;
}

const crumb = breadcrumb ? (
thatblindgeye marked this conversation as resolved.
Show resolved Hide resolved
<section
className={css(
styles.pageMainBreadcrumb,
isBreadcrumbWidthLimited && styles.modifiers.limitWidth,
formatBreakpointMods(
breadcrumbProps?.stickyOnBreakpoint,
styles,
'sticky-',
getVerticalBreakpoint(height),
true
)
)}
<PageBreadcrumb
stickyOnBreakpoint={breadcrumbProps?.stickyOnBreakpoint}
isWidthLimited={isBreadcrumbWidthLimited}
>
{isBreadcrumbWidthLimited ? <div className={css(styles.pageMainBody)}>{breadcrumb}</div> : breadcrumb}
</section>
<PageBody>{breadcrumb}</PageBody>
</PageBreadcrumb>
) : null;

const isGrouped = isHorizontalSubnavGrouped || isBreadcrumbGrouped || additionalGroupedContent;
Expand Down
18 changes: 18 additions & 0 deletions packages/react-core/src/components/Page/PageBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import styles from '@patternfly/react-styles/css/components/Page/page';
import { css } from '@patternfly/react-styles';

export interface PageBodyProps extends React.HTMLProps<HTMLDivElement> {
/** Content rendered inside the section */
children?: React.ReactNode;
/** Additional classes added to the section */
className?: string;
}

export const PageBody: React.FunctionComponent<PageBodyProps> = ({ className, children, ...props }: PageBodyProps) => (
<div {...props} className={css(styles.pageMainBody, className)}>
{children}
</div>
);

PageBody.displayName = 'PageBody';
9 changes: 7 additions & 2 deletions packages/react-core/src/components/Page/PageBreadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { css } from '@patternfly/react-styles';
import styles from '@patternfly/react-styles/css/components/Page/page';
import { formatBreakpointMods, Mods } from '../../helpers/util';
import { PageContext } from './PageContext';
import { PageBody } from './PageBody';

export interface PageBreadcrumbProps extends React.HTMLProps<HTMLElement> {
/** Additional classes to apply to the PageBreadcrumb */
Expand All @@ -26,6 +27,10 @@ export interface PageBreadcrumbProps extends React.HTMLProps<HTMLElement> {
hasShadowBottom?: boolean;
/** Flag indicating if the PageBreadcrumb has a scrolling overflow */
hasOverflowScroll?: boolean;
/** @beta Flag indicating whether children passed to the component should be wrapped by a PageBody.
* Set this to false in order to pass multiple, custom PageBody's as children.
*/
hasBodyWrapper?: boolean;
/** Adds an accessible name to the breadcrumb section. Required when the hasOverflowScroll prop is set to true. */
'aria-label'?: string;
}
Expand All @@ -39,6 +44,7 @@ export const PageBreadcrumb = ({
hasShadowBottom = false,
hasOverflowScroll = false,
'aria-label': ariaLabel,
hasBodyWrapper = true,
...props
}: PageBreadcrumbProps) => {
const { height, getVerticalBreakpoint } = React.useContext(PageContext);
Expand All @@ -65,8 +71,7 @@ export const PageBreadcrumb = ({
aria-label={ariaLabel}
{...props}
>
{isWidthLimited && <div className={css(styles.pageMainBody)}>{children}</div>}
{!isWidthLimited && children}
{hasBodyWrapper ? <PageBody>{children}</PageBody> : children}
</section>
);
};
Expand Down
72 changes: 0 additions & 72 deletions packages/react-core/src/components/Page/PageNavigation.tsx

This file was deleted.

13 changes: 8 additions & 5 deletions packages/react-core/src/components/Page/PageSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styles from '@patternfly/react-styles/css/components/Page/page';
import { css } from '@patternfly/react-styles';
import { formatBreakpointMods } from '../../helpers/util';
import { PageContext } from './PageContext';
import { PageBody } from './PageBody';

export enum PageSectionVariants {
default = 'default',
Expand All @@ -11,7 +12,6 @@ export enum PageSectionVariants {

export enum PageSectionTypes {
default = 'default',
nav = 'nav',
subNav = 'subnav',
breadcrumb = 'breadcrumb',
tabs = 'tabs',
Expand All @@ -26,7 +26,7 @@ export interface PageSectionProps extends React.HTMLProps<HTMLDivElement> {
/** Section background color variant. This will only apply when the type prop has the "default" value. */
variant?: 'default' | 'secondary';
/** Section type variant */
type?: 'default' | 'nav' | 'subnav' | 'breadcrumb' | 'tabs' | 'wizard';
type?: 'default' | 'subnav' | 'breadcrumb' | 'tabs' | 'wizard';
/** Enables the page section to fill the available vertical space */
isFilled?: boolean;
/** Limits the width of the section */
Expand Down Expand Up @@ -57,6 +57,10 @@ export interface PageSectionProps extends React.HTMLProps<HTMLDivElement> {
hasShadowBottom?: boolean;
/** Flag indicating if the PageSection has a scrolling overflow */
hasOverflowScroll?: boolean;
/** @beta Flag indicating whether children passed to the component should be wrapped by a PageBody.
* Set this to false in order to pass multiple, custom PageBody's as children.
*/
hasBodyWrapper?: boolean;
/** Adds an accessible name to the page section. Required when the hasOverflowScroll prop is set to true.
* This prop should also be passed in if a heading is not being used to describe the content of the page section.
*/
Expand All @@ -67,7 +71,6 @@ export interface PageSectionProps extends React.HTMLProps<HTMLDivElement> {

const variantType = {
[PageSectionTypes.default]: styles.pageMainSection,
[PageSectionTypes.nav]: styles.pageMainNav,
[PageSectionTypes.subNav]: styles.pageMainSubnav,
[PageSectionTypes.breadcrumb]: styles.pageMainBreadcrumb,
[PageSectionTypes.tabs]: styles.pageMainTabs,
Expand All @@ -94,6 +97,7 @@ export const PageSection: React.FunctionComponent<PageSectionProps> = ({
hasOverflowScroll = false,
'aria-label': ariaLabel,
component = 'section',
hasBodyWrapper = true,
...props
}: PageSectionProps) => {
const { height, getVerticalBreakpoint } = React.useContext(PageContext);
Expand Down Expand Up @@ -127,8 +131,7 @@ export const PageSection: React.FunctionComponent<PageSectionProps> = ({
{...(hasOverflowScroll && { tabIndex: 0 })}
aria-label={ariaLabel}
>
{isWidthLimited && <div className={css(styles.pageMainBody)}>{children}</div>}
{!isWidthLimited && children}
{hasBodyWrapper ? <PageBody>{children}</PageBody> : children}
</Component>
);
};
Expand Down
52 changes: 11 additions & 41 deletions packages/react-core/src/components/Page/__tests__/Page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { Breadcrumb, BreadcrumbItem } from '../../Breadcrumb';
import { Nav, NavList, NavItem } from '../../Nav';
import { SkipToContent } from '../../SkipToContent';
import { PageBreadcrumb } from '../PageBreadcrumb';
import { PageNavigation } from '../PageNavigation';
import { PageGroup } from '../PageGroup';
import { Masthead } from '../../Masthead';

Expand Down Expand Up @@ -228,33 +227,6 @@ describe('Page', () => {
expect(asFragment()).toMatchSnapshot();
});

test('Check page to verify nav is created - PageNavigation syntax', () => {
const masthead = <Masthead />;
const Sidebar = <PageSidebar isSidebarOpen />;

const { asFragment } = render(
<Page {...props} masthead={masthead} sidebar={Sidebar}>
<PageNavigation>
<Nav aria-label="Nav" variant="horizontal-subnav">
<NavList>
<NavItem itemId={0} isActive>
System Panel
</NavItem>
<NavItem itemId={1}>Policy</NavItem>
<NavItem itemId={2}>Authentication</NavItem>
<NavItem itemId={3}>Network Services</NavItem>
<NavItem itemId={4}>Server</NavItem>
</NavList>
</Nav>
</PageNavigation>
<PageSection variant="default">Section with default background</PageSection>
</Page>
);

expect(screen.getByRole('main')).not.toHaveAttribute('id');
expect(asFragment()).toMatchSnapshot();
});

test('Check page to verify grouped nav and breadcrumb - new components syntax', () => {
const masthead = <Masthead />;
const Sidebar = <PageSidebar isSidebarOpen />;
Expand All @@ -272,19 +244,17 @@ describe('Page', () => {
</BreadcrumbItem>
</Breadcrumb>
</PageBreadcrumb>
<PageNavigation>
<Nav aria-label="Nav" variant="horizontal-subnav">
<NavList>
<NavItem itemId={0} isActive>
System Panel
</NavItem>
<NavItem itemId={1}>Policy</NavItem>
<NavItem itemId={2}>Authentication</NavItem>
<NavItem itemId={3}>Network Services</NavItem>
<NavItem itemId={4}>Server</NavItem>
</NavList>
</Nav>
</PageNavigation>
<Nav aria-label="Nav" variant="horizontal-subnav">
<NavList>
<NavItem itemId={0} isActive>
System Panel
</NavItem>
<NavItem itemId={1}>Policy</NavItem>
<NavItem itemId={2}>Authentication</NavItem>
<NavItem itemId={3}>Network Services</NavItem>
<NavItem itemId={4}>Server</NavItem>
</NavList>
</Nav>
</PageGroup>
<PageSection variant="default">Section with default background</PageSection>
</Page>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import * as React from 'react';
import { render, screen } from '@testing-library/react';
import { PageBody } from '../PageBody';
import styles from '@patternfly/react-styles/css/components/Page/page';

test('Renders without children', () => {
render(<PageBody data-testid="page-main-body" />);

expect(screen.getByTestId('page-main-body')).toBeVisible();
});
test('Renders children', () => {
render(<PageBody>Test</PageBody>);

expect(screen.getByText('Test')).toBeVisible();
});
test(`Renders with class ${styles.pageMainBody} by default`, () => {
render(<PageBody>Test</PageBody>);

expect(screen.getByText('Test')).toHaveClass(styles.pageMainBody);
});
test(`Renders with custom classes when className is passed`, () => {
render(<PageBody className="custom-class">Test</PageBody>);

expect(screen.getByText('Test')).toHaveClass('custom-class');
});
test(`Renders with spread props`, () => {
render(<PageBody id="custom-id">Test</PageBody>);

expect(screen.getByText('Test')).toHaveAttribute('id', 'custom-id');
});
test('Matches snapshot', () => {
const { asFragment } = render(<PageBody>Test</PageBody>);
expect(asFragment()).toMatchSnapshot();
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { render, screen } from '@testing-library/react';
import { PageBreadcrumb } from '../PageBreadcrumb';
import styles from '@patternfly/react-styles/css/components/Page/page';

describe('page breadcrumb', () => {
test('Verify basic render', () => {
Expand Down Expand Up @@ -32,6 +33,7 @@ describe('page breadcrumb', () => {
expect(asFragment()).toMatchSnapshot();
});

// Old snapshot tests end here. The following tests can be kept if Page test suites need a revamp
test('Renders without an aria-label by default', () => {
render(<PageBreadcrumb>test</PageBreadcrumb>);

Expand All @@ -41,7 +43,7 @@ describe('page breadcrumb', () => {
test('Renders with the passed aria-label applied', () => {
render(<PageBreadcrumb aria-label="Test label">test</PageBreadcrumb>);

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

test('Does not log a warning in the console by default', () => {
Expand Down Expand Up @@ -71,4 +73,15 @@ describe('page breadcrumb', () => {

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

test('Renders with PageBody wrapper by default', () => {
render(<PageBreadcrumb>test</PageBreadcrumb>);

expect(screen.getByText('test')).toHaveClass(styles.pageMainBody);
});
test('Does not render with PageBody wrapper when hasBodyWrapper is false', () => {
render(<PageBreadcrumb hasBodyWrapper={false}>test</PageBreadcrumb>);

expect(screen.getByText('test')).not.toHaveClass(styles.pageMainBody);
});
});
Loading
Loading