Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/react-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"tslib": "^2.8.1"
},
"devDependencies": {
"@patternfly/patternfly": "6.6.0-prerelease.39",
"@patternfly/patternfly": "6.6.0-prerelease.41",
"case-anything": "^3.1.2",
"css": "^3.0.0",
"fs-extra": "^11.3.3"
Expand Down
12 changes: 10 additions & 2 deletions packages/react-core/src/components/Drawer/Drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export interface DrawerProps extends React.HTMLProps<HTMLDivElement>, OUIAProps
isInline?: boolean;
/** @beta Indicates if the drawer will have pill styles */
isPill?: boolean;
/** @beta Positions the drawer as fixed to fill the viewport. Place the drawer after Page as a sibling. */
isViewport?: boolean;
/** Indicates if the drawer will always show both content and panel. */
isStatic?: boolean;
/** Position of the drawer panel. left and right are deprecated, use start and end instead. */
Expand All @@ -43,6 +45,7 @@ export interface DrawerContextProps {
drawerRef?: React.RefObject<HTMLDivElement | null>;
drawerContentRef?: React.RefObject<HTMLDivElement | null>;
isInline: boolean;
isViewport: boolean;
}

export const DrawerContext = createContext<Partial<DrawerContextProps>>({
Expand All @@ -52,7 +55,8 @@ export const DrawerContext = createContext<Partial<DrawerContextProps>>({
position: 'end',
drawerRef: null,
drawerContentRef: null,
isInline: false
isInline: false,
isViewport: false
});

export const Drawer: React.FunctionComponent<DrawerProps> = ({
Expand All @@ -61,6 +65,7 @@ export const Drawer: React.FunctionComponent<DrawerProps> = ({
isExpanded = false,
isInline = false,
isPill = false,
isViewport = false,
isStatic = false,
position = 'end',
onExpand = () => {},
Expand All @@ -73,13 +78,16 @@ export const Drawer: React.FunctionComponent<DrawerProps> = ({
const drawerContentRef = useRef<HTMLDivElement>(undefined);

return (
<DrawerContext.Provider value={{ isExpanded, isStatic, onExpand, position, drawerRef, drawerContentRef, isInline }}>
<DrawerContext.Provider
value={{ isExpanded, isStatic, onExpand, position, drawerRef, drawerContentRef, isInline, isViewport }}
>
<div
className={css(
styles.drawer,
isExpanded && styles.modifiers.expanded,
isInline && styles.modifiers.inline,
isPill && styles.modifiers.pill,
isViewport && styles.modifiers.viewport,
isStatic && styles.modifiers.static,
(position === 'left' || position === 'start') && styles.modifiers.panelLeft,
position === 'bottom' && styles.modifiers.panelBottom,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export enum DrawerContentColorVariant {
export interface DrawerContentProps extends React.HTMLProps<HTMLDivElement> {
/** Additional classes added to the Drawer. */
className?: string;
/** Content to be rendered in the drawer. */
/** Content to be rendered in the drawer. Can be omitted to leave the content container empty. */
children?: React.ReactNode;
/** Content rendered in the drawer panel. */
panelContent: React.ReactNode;
Expand Down
50 changes: 25 additions & 25 deletions packages/react-core/src/components/Drawer/DrawerPanelContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
const panel = useRef<HTMLDivElement>(undefined);
const splitterRef = useRef<HTMLDivElement>(undefined);
const [separatorValue, setSeparatorValue] = useState(0);
const { position, isExpanded, isStatic, onExpand, drawerRef, drawerContentRef, isInline } = useContext(DrawerContext);
const { position, isExpanded, isStatic, onExpand, drawerRef, drawerContentRef, isInline, isViewport } =
useContext(DrawerContext);
const hidden = isStatic ? false : !isExpanded;
const [isExpandedInternal, setIsExpandedInternal] = useState(!hidden);
const [isFocusTrapActive, setIsFocusTrapActive] = useState(false);
Expand All @@ -126,9 +127,22 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
}
}, [isStatic, isExpanded]);

const getSizeElement = () => {
if (isViewport) {
return drawerRef?.current ?? drawerContentRef?.current;
}
return drawerContentRef?.current ?? drawerRef?.current;
};

const calcValueNow = () => {
let splitterPos;
let drawerSize;
const sizeEl = getSizeElement();

if (!sizeEl || !panel.current || !splitterRef.current || !drawerRef?.current) {
return 0;
}

const isRTL = getLanguageDirection(panel.current) === 'rtl';

if (isInline && (position === 'end' || position === 'right')) {
Expand All @@ -149,37 +163,23 @@ export const DrawerPanelContent: React.FunctionComponent<DrawerPanelContentProps
}
} else if (position === 'end' || position === 'right') {
if (isRTL) {
splitterPos =
drawerContentRef.current.getBoundingClientRect().left - splitterRef.current.getBoundingClientRect().right;
drawerSize =
drawerContentRef.current.getBoundingClientRect().left -
drawerContentRef.current.getBoundingClientRect().right;
splitterPos = sizeEl.getBoundingClientRect().left - splitterRef.current.getBoundingClientRect().right;
drawerSize = sizeEl.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
} else {
splitterPos =
drawerContentRef.current.getBoundingClientRect().right - splitterRef.current.getBoundingClientRect().left;
drawerSize =
drawerContentRef.current.getBoundingClientRect().right -
drawerContentRef.current.getBoundingClientRect().left;
splitterPos = sizeEl.getBoundingClientRect().right - splitterRef.current.getBoundingClientRect().left;
drawerSize = sizeEl.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
}
} else if (position === 'start' || position === 'left') {
if (isRTL) {
splitterPos =
splitterRef.current.getBoundingClientRect().left - drawerContentRef.current.getBoundingClientRect().right;
drawerSize =
drawerContentRef.current.getBoundingClientRect().left -
drawerContentRef.current.getBoundingClientRect().right;
splitterPos = splitterRef.current.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
drawerSize = sizeEl.getBoundingClientRect().left - sizeEl.getBoundingClientRect().right;
} else {
splitterPos =
splitterRef.current.getBoundingClientRect().right - drawerContentRef.current.getBoundingClientRect().left;
drawerSize =
drawerContentRef.current.getBoundingClientRect().right -
drawerContentRef.current.getBoundingClientRect().left;
splitterPos = splitterRef.current.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
drawerSize = sizeEl.getBoundingClientRect().right - sizeEl.getBoundingClientRect().left;
}
} else if (position === 'bottom') {
splitterPos =
drawerContentRef.current.getBoundingClientRect().bottom - splitterRef.current.getBoundingClientRect().top;
drawerSize =
drawerContentRef.current.getBoundingClientRect().bottom - drawerContentRef.current.getBoundingClientRect().top;
splitterPos = sizeEl.getBoundingClientRect().bottom - splitterRef.current.getBoundingClientRect().top;
drawerSize = sizeEl.getBoundingClientRect().bottom - sizeEl.getBoundingClientRect().top;
}

const newSplitterPos = (splitterPos / drawerSize) * 100;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,3 +231,56 @@ test('Renders with ouiaSafe=false when specified', () => {
);
expect(screen.getByTestId('drawer')).toHaveAttribute('data-ouia-safe', 'false');
});

test(`Does not render with ${styles.modifiers.viewport} class by default`, () => {
render(
<Drawer data-testid="drawer">
<DrawerContent panelContent={<DrawerPanelContent>panel</DrawerPanelContent>}>
<DrawerContentBody>content</DrawerContentBody>
</DrawerContent>
</Drawer>
);

expect(screen.getByTestId('drawer')).not.toHaveClass(styles.modifiers.viewport);
});

test(`Renders with ${styles.modifiers.viewport} when isViewport is specified`, () => {
render(
<Drawer data-testid="drawer" isViewport>
<DrawerContent panelContent={<DrawerPanelContent>panel</DrawerPanelContent>}>
<DrawerContentBody>content</DrawerContentBody>
</DrawerContent>
</Drawer>
);

expect(screen.getByTestId('drawer')).toHaveClass(styles.modifiers.viewport);
});

test('Resizeable DrawerPanelContent without drawer content does not throw', async () => {
const consoleError = jest.spyOn(console, 'error').mockImplementation();

const panelContent = (
<DrawerPanelContent isResizable>
<DrawerHead>
<span>drawer-panel</span>
<DrawerActions>
<DrawerCloseButton />
</DrawerActions>
</DrawerHead>
</DrawerPanelContent>
);

const user = userEvent.setup();

render(
<Drawer isExpanded isViewport>
<DrawerContent panelContent={panelContent} />
</Drawer>
);

await user.tab();
await user.keyboard(`{${KeyTypes.ArrowLeft}}`);

expect(consoleError).not.toHaveBeenCalled();
consoleError.mockRestore();
});
10 changes: 9 additions & 1 deletion packages/react-core/src/components/Drawer/examples/Drawer.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ propComponents:
DrawerCloseButton,
DrawerPanelDescription,
DrawerPanelBody,
DrawerPanelFocusTrapObject,
DrawerPanelFocusTrapObject
]
section: components
---
Expand Down Expand Up @@ -156,3 +156,11 @@ To customize which element receives focus when the drawer panel expands, use the
```ts file="./DrawerPillInline.tsx"

```

### Viewport

Use `isViewport` to position the drawer as `fixed` so it fills the viewport. Place the drawer after `<Page>` as a sibling. Omit children of `<DrawerContent>` so the content container stays empty; the empty container is still required so the panel can overlay the page.

```ts file="./DrawerViewport.tsx" isFullscreen isBeta

```
112 changes: 112 additions & 0 deletions packages/react-core/src/components/Drawer/examples/DrawerViewport.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Fragment, useRef, useState } from 'react';
import {
Backdrop,
Button,
Drawer,
DrawerActions,
DrawerCloseButton,
DrawerContent,
DrawerHead,
DrawerPanelContent,
Masthead,
MastheadBrand,
MastheadContent,
MastheadLogo,
MastheadMain,
MastheadToggle,
Page,
PageSection,
PageSidebar,
PageSidebarBody,
PageToggleButton,
Toolbar,
ToolbarContent,
ToolbarItem
} from '@patternfly/react-core';

export const DrawerViewport: React.FunctionComponent = () => {
const [isExpanded, setIsExpanded] = useState(true);
const drawerRef = useRef<HTMLSpanElement>(null);

const onExpand = () => {
drawerRef.current && drawerRef.current.focus();
};

const onClick = () => {
setIsExpanded(!isExpanded);
};

const onCloseClick = () => {
setIsExpanded(false);
};

const onResize = (_event: MouseEvent | TouchEvent | React.KeyboardEvent, newWidth: number, id: string) => {
// eslint-disable-next-line no-console
console.log(`${id} has new width of: ${newWidth}`);
};

const headerToolbar = (
<Toolbar id="viewport-drawer-toolbar">
<ToolbarContent>
<ToolbarItem>
<Button aria-expanded={isExpanded} onClick={onClick}>
Toggle drawer
</Button>
</ToolbarItem>
</ToolbarContent>
</Toolbar>
);

const masthead = (
<Masthead>
<MastheadMain>
<MastheadToggle>
<PageToggleButton isHamburgerButton aria-label="Global navigation" id="viewport-drawer-nav-toggle" />
</MastheadToggle>
<MastheadBrand>
<MastheadLogo href="https://patternfly.org" target="_blank">
Logo
</MastheadLogo>
</MastheadBrand>
</MastheadMain>
<MastheadContent>{headerToolbar}</MastheadContent>
</Masthead>
);

const sidebar = (
<PageSidebar id="viewport-drawer-sidebar">
<PageSidebarBody>Navigation</PageSidebarBody>
</PageSidebar>
);

const panelContent = (
<DrawerPanelContent isResizable onResize={onResize} id="viewport-resize-panel" minSize="150px">
<DrawerHead>
<span tabIndex={isExpanded ? 0 : -1} ref={drawerRef}>
Drawer panel header
</span>
<DrawerActions>
<DrawerCloseButton onClick={onCloseClick} />
</DrawerActions>
</DrawerHead>
</DrawerPanelContent>
);

return (
<Fragment>
<Page isManagedSidebar masthead={masthead} sidebar={sidebar}>
<PageSection aria-labelledby="viewport-drawer-section">
<h2 id="viewport-drawer-section">Viewport drawer example</h2>
<p>
The viewport drawer is a sibling of Page. DrawerContent is left empty so the panel overlays the page without
affecting page layout or scrolling.
</p>
</PageSection>
</Page>
{isExpanded && <Backdrop onClick={onCloseClick}></Backdrop>}
<Drawer isExpanded={isExpanded} isViewport onExpand={onExpand}>
<DrawerContent panelContent={panelContent} />
</Drawer>
</Fragment>
);
};
2 changes: 1 addition & 1 deletion packages/react-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"test:a11y": "patternfly-a11y --config patternfly-a11y.config"
},
"dependencies": {
"@patternfly/patternfly": "6.6.0-prerelease.39",
"@patternfly/patternfly": "6.6.0-prerelease.41",
"@patternfly/react-charts": "workspace:^",
"@patternfly/react-code-editor": "workspace:^",
"@patternfly/react-core": "workspace:^",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-icons/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,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.6.0-prerelease.39",
"@patternfly/patternfly": "6.6.0-prerelease.41",
"@rhds/icons": "^2.3.1",
"fs-extra": "^11.3.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react-styles/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"clean": "rimraf dist css"
},
"devDependencies": {
"@patternfly/patternfly": "6.6.0-prerelease.39",
"@patternfly/patternfly": "6.6.0-prerelease.41",
"change-case": "^5.4.4",
"fs-extra": "^11.3.3"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/react-tokens/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
},
"devDependencies": {
"@adobe/css-tools": "^4.4.4",
"@patternfly/patternfly": "6.6.0-prerelease.39",
"@patternfly/patternfly": "6.6.0-prerelease.41",
"fs-extra": "^11.3.3"
}
}
Loading
Loading