diff --git a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md
index 751fc253d8c..0340deeb364 100644
--- a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md
+++ b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md
@@ -22,1447 +22,14 @@ import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg';
### Basic
-```js isFullscreen
-import React from 'react';
-import {
- Avatar,
- Brand,
- Breadcrumb,
- BreadcrumbItem,
- Button,
- ButtonVariant,
- Divider,
- Dropdown,
- DropdownItem,
- DropdownList,
- EmptyState,
- EmptyStateActions,
- EmptyStateBody,
- EmptyStateIcon,
- EmptyStateHeader,
- EmptyStateFooter,
- EmptyStateVariant,
- MenuToggle,
- Nav,
- NavItem,
- NavList,
- NotificationBadge,
- NotificationDrawer,
- NotificationDrawerBody,
- NotificationDrawerHeader,
- NotificationDrawerList,
- NotificationDrawerListItem,
- NotificationDrawerListItemBody,
- NotificationDrawerListItemHeader,
- Page,
- PageSection,
- PageSectionVariants,
- PageSidebar,
- PageSidebarBody,
- SkipToContent,
- TextContent,
- Text,
- Title,
- PageToggleButton,
- Masthead,
- MastheadMain,
- MastheadToggle,
- MastheadContent,
- MastheadBrand,
- Toolbar,
- ToolbarItem,
- ToolbarGroup,
- ToolbarContent
-} from '@patternfly/react-core';
-import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
-import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
-import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
-import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
-import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
-import BarsIcon from '@patternfly/react-icons/dist/js/icons/bars-icon';
-import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg';
-import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
-import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg';
-
-class BasicNotificationDrawer extends React.Component {
- constructor(props) {
- super(props);
- this.drawerRef = React.createRef();
- this.state = {
- isDropdownOpen: false,
- isKebabDropdownOpen: false,
- activeItem: 0,
- isDrawerExpanded: false,
- isUnreadMap: {
- 'notification-1': true,
- 'notification-2': true
- },
- showNotifications: true,
- isActionsMenuOpen: {}
- };
- this.onDropdownToggle = () => {
- this.setState((prevState) => ({
- isDropdownOpen: !prevState.isDropdownOpen
- }));
- };
-
- this.onDropdownSelect = () => {
- this.setState({
- isDropdownOpen: false
- });
- };
-
- this.onKebabDropdownToggle = () => {
- this.setState((prevState) => ({
- isKebabDropdownOpen: !prevState.isKebabDropdownOpen
- }));
- };
-
- this.onKebabDropdownSelect = () => {
- this.setState({
- isKebabDropdownOpen: false
- });
- };
-
- this.onNavSelect = (_event, result) => {
- this.setState({
- activeItem: result.itemId
- });
- };
-
- this.onCloseNotificationDrawer = (_event) => {
- this.setState((prevState) => {
- return {
- isDrawerExpanded: !prevState.isDrawerExpanded
- };
- });
- };
-
- this.onToggle = (id) => {
- this.setState((prevState) => ({
- isActionsMenuOpen: { [id]: !prevState.isActionsMenuOpen[id] }
- }));
- };
+```ts file='./examples/NotificationDrawerBasic.tsx' isFullscreen
- this.onSelect = () => {
- this.setState({
- isActionsMenuOpen: {}
- });
- };
-
- this.onListItemClick = (id) => {
- this.setState((prevState) => {
- if (!prevState.isUnreadMap) return;
- prevState.isUnreadMap[id] = false;
- return {
- isUnreadMap: prevState.isUnreadMap
- };
- });
- };
-
- this.getNumberUnread = () => {
- const { isUnreadMap } = this.state;
- if (isUnreadMap === null) return 0;
- return Object.keys(isUnreadMap).reduce((count, id) => {
- return isUnreadMap[id] ? count + 1 : count;
- }, 0);
- };
-
- this.markAllRead = () => {
- this.setState({
- isUnreadMap: null
- });
- };
-
- this.showNotifications = (showNotifications) => {
- this.setState({
- isUnreadMap: null,
- showNotifications: showNotifications
- });
- };
-
- this.focusDrawer = (_event) => {
- const firstTabbableItem = this.drawerRef.current.querySelector('a, button');
- firstTabbableItem.focus();
- };
- }
-
- render() {
- const {
- isDropdownOpen,
- isKebabDropdownOpen,
- activeItem,
- res,
- isDrawerExpanded,
- isActionsMenuOpen,
- isUnreadMap,
- showNotifications
- } = this.state;
-
- const PageNav = (
-
- );
- const kebabDropdownItems = (
- <>
-
- Settings
-
-
- Help
-
- >
- );
- const userDropdownItems = (
- <>
- My profile
- User management
- Logout
- >
- );
- const headerToolbar = (
-
-
-
-
-
- this.onCloseNotificationDrawer(event)}
- aria-label="Notifications"
- isExpanded={isDrawerExpanded}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- this.setState({ isKebabDropdownOpen: isOpen })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
-
-
-
- )}
- >
- {kebabDropdownItems}
-
-
-
- this.setState({ isDropdownOpen: isOpen })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- }
- isFullHeight
- >
- John Smith
-
- )}
- >
- {userDropdownItems}
-
-
-
-
-
-
- );
-
- const Header = (
-
-
-
-
-
-
-
-
-
-
-
- {headerToolbar}
-
- );
- const Sidebar = (
-
- {PageNav}
-
- );
- const pageId = 'main-content-page-layout-default-nav';
- const PageSkipToContent = Skip to content;
-
- const PageBreadcrumb = (
-
- Section home
- Section title
- Section title
-
- Section landing
-
-
- );
-
- const drawerContent = 'Panel content';
-
- const notificationDrawerActions = (
- <>
-
- Mark all read
-
- this.showNotifications(false)}>
- Clear all
-
- this.showNotifications(true)}>
- Unclear last
-
- Settings
- >
- );
- const notificationDrawerDropdownItems = (
- <>
- ev.preventDefault()}
- >
- Link
-
- Action
-
-
- Disabled Link
-
- >
- );
- const notificationDrawer = (
-
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-0')}
- isExpanded={isActionsMenuOpen['toggle-id-0'] || false}
- >
-
-
- )}
- >
- {notificationDrawerActions}
-
-
-
- {showNotifications && (
-
- this.onListItemClick('notification-1')}
- isRead={isUnreadMap === null || !isUnreadMap['notification-1']}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-1')}
- isExpanded={isActionsMenuOpen['toggle-id-1'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is an info notification description.
-
-
- this.onListItemClick('notification-2')}
- isRead={isUnreadMap === null || !isUnreadMap['notification-2']}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-2')}
- isExpanded={isActionsMenuOpen['toggle-id-2'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a danger notification description. This is a long description to show how the title will wrap
- if it is long and wraps to multiple lines.
-
-
- this.onListItemClick('notification-3')}
- isRead={isUnreadMap === null || !isUnreadMap['notification-3']}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-3')}
- isExpanded={isActionsMenuOpen['toggle-id-3'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a warning notification description.
-
-
- this.onListItemClick('notification-4')}
- isRead={isUnreadMap === null || !isUnreadMap['notification-4']}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-4')}
- isExpanded={isActionsMenuOpen['toggle-id-4'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a success notification description.
-
-
-
- )}
- {!showNotifications && (
-
- }
- />
-
- There are currently no alerts. There may be silenced critical alerts however.
-
-
-
-
-
-
-
- )}
-
-
- );
-
- return (
-
- this.focusDrawer(event)}
- isNotificationDrawerExpanded={isDrawerExpanded}
- skipToContent={PageSkipToContent}
- breadcrumb={PageBreadcrumb}
- mainContainerId={pageId}
- >
-
-
- Main title
-
- Body text should be Overpass Regular at 16px. It should have leading of 24px because
- of its relative line height of 1.5.
-
-
-
-
- Panel section content
-
-
-
- );
- }
-}
```
### Grouped
When using the NotificationDrawerGroupList and related components, the function that is passed in to the `onNotificationDrawerExpand` prop on the Page component must also ensure the NotificationDrawer component only receives focus when it is initially opened. Otherwise any time a drawer group item is opened the NotificationDrawer component will receive focus, which would be unexpected behavior for users.
-```js isFullscreen
-import React from 'react';
-import {
- Avatar,
- Brand,
- Breadcrumb,
- BreadcrumbItem,
- Button,
- ButtonVariant,
- Divider,
- Dropdown,
- DropdownItem,
- DropdownList,
- EmptyState,
- EmptyStateActions,
- EmptyStateBody,
- EmptyStateIcon,
- EmptyStateHeader,
- EmptyStateFooter,
- EmptyStateVariant,
- MenuToggle,
- Nav,
- NavItem,
- NavList,
- NotificationBadge,
- NotificationDrawer,
- NotificationDrawerBody,
- NotificationDrawerGroup,
- NotificationDrawerGroupList,
- NotificationDrawerHeader,
- NotificationDrawerList,
- NotificationDrawerListItem,
- NotificationDrawerListItemBody,
- NotificationDrawerListItemHeader,
- Page,
- PageSection,
- PageSectionVariants,
- PageSidebar,
- PageSidebarBody,
- SkipToContent,
- Title,
- TextContent,
- Text,
- PageToggleButton,
- Masthead,
- MastheadMain,
- MastheadToggle,
- MastheadContent,
- MastheadBrand,
- Toolbar,
- ToolbarItem,
- ToolbarGroup,
- ToolbarContent
-} from '@patternfly/react-core';
-import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
-import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
-import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
-import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
-import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
-import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
-import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
-import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg';
-import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg';
-
-class GroupedNotificationDrawer extends React.Component {
- constructor(props) {
- super(props);
- this.drawerRef = React.createRef();
- this.state = {
- isDropdownOpen: false,
- isKebabDropdownOpen: false,
- activeItem: 0,
- isDrawerExpanded: false,
- firstDrawerGroupExpanded: false,
- secondDrawerGroupExpanded: true,
- thirdDrawerGroupExpanded: false,
- isActionsMenuOpen: {},
- showNotifications: true,
- isUnreadMap: {
- 'group-1': {
- 'notification-5': true,
- 'notification-6': true
- },
- 'group-2': {
- 'notification-9': true,
- 'notification-10': true
- },
- 'group-3': null
- }
- };
- this.onDropdownToggle = () => {
- this.setState((prevState) => ({
- isDropdownOpen: !prevState.isDropdownOpen
- }));
- };
-
- this.onDropdownSelect = () => {
- this.setState({
- isDropdownOpen: false
- });
- };
-
- this.onKebabDropdownToggle = () => {
- this.setState((prevState) => ({
- isKebabDropdownOpen: !prevState.isKebabDropdownOpen
- }));
- };
-
- this.onKebabDropdownSelect = () => {
- this.setState({
- isKebabDropdownOpen: false
- });
- };
-
- this.onNavSelect = (_event, result) => {
- this.setState({
- activeItem: result.itemId
- });
- };
-
- this.onCloseNotificationDrawer = (_event) => {
- this.setState((prevState) => {
- return {
- isDrawerExpanded: !prevState.isDrawerExpanded
- };
- });
- };
-
- this.onToggle = (id) => {
- this.setState((prevState) => ({
- isActionsMenuOpen: { [id]: !prevState.isActionsMenuOpen[id] }
- }));
- };
-
- this.onSelect = () => {
- this.setState({
- isActionsMenuOpen: {}
- });
- };
-
- this.onListItemClick = (groupId, id) => {
- this.setState((prevState) => {
- if (!prevState.isUnreadMap || !prevState.isUnreadMap[groupId]) return;
- console.log(prevState.isUnreadMap);
- prevState.isUnreadMap[groupId][id] = false;
- return {
- isUnreadMap: prevState.isUnreadMap
- };
- });
- };
-
- this.isUnread = (groupId, id) => {
- const { isUnreadMap } = this.state;
- return isUnreadMap && isUnreadMap[groupId] && isUnreadMap[groupId][id];
- };
-
- this.getNumberUnread = (groupId) => {
- const { isUnreadMap } = this.state;
- if (isUnreadMap === null) return 0;
-
- if (groupId) {
- if (isUnreadMap[groupId] === null) return 0;
-
- return Object.keys(isUnreadMap[groupId]).reduce((count, id) => {
- return isUnreadMap[groupId][id] ? count + 1 : count;
- }, 0);
- }
-
- return Object.keys(isUnreadMap).reduce((count, groupId) => {
- if (isUnreadMap[groupId] === null) return count;
-
- return Object.keys(isUnreadMap[groupId]).reduce((groupCount, id) => {
- return isUnreadMap[groupId][id] ? groupCount + 1 : groupCount;
- }, count);
- }, 0);
- };
-
- this.markAllRead = () => {
- this.setState({
- isUnreadMap: null
- });
- };
-
- this.showNotifications = (showNotifications) => {
- this.setState({
- isUnreadMap: null,
- showNotifications: showNotifications
- });
- };
-
- this.toggleFirstDrawer = (event, value) => {
- this.setState({
- firstDrawerGroupExpanded: value
- });
- };
-
- this.toggleSecondDrawer = (event, value) => {
- this.setState({
- secondDrawerGroupExpanded: value
- });
- };
-
- this.toggleThirdDrawer = (event, value) => {
- this.setState({
- thirdDrawerGroupExpanded: value
- });
- };
-
- this.focusDrawer = (_event) => {
- // Prevent the NotificationDrawer from receiving focus if a drawer group item is opened
- if (!document.activeElement.closest(`.${this.drawerRef.current.className}`)) {
- const firstTabbableItem = this.drawerRef.current.querySelector('a, button');
- firstTabbableItem.focus();
- }
- };
- }
-
- render() {
- const {
- isDropdownOpen,
- isKebabDropdownOpen,
- activeItem,
- res,
- isDrawerExpanded,
- isActionsMenuOpen,
- isUnreadMap,
- showNotifications,
- firstDrawerGroupExpanded,
- secondDrawerGroupExpanded,
- thirdDrawerGroupExpanded
- } = this.state;
-
- const PageNav = (
-
- );
- const kebabDropdownItems = (
- <>
-
- Settings
-
-
- Help
-
- >
- );
- const userDropdownItems = (
- <>
- My profile
- User management
- Logout
- >
- );
- const headerToolbar = (
-
-
-
-
-
- this.onCloseNotificationDrawer(event)}
- aria-label="Notifications"
- isExpanded={isDrawerExpanded}
- >
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- this.setState({ isKebabDropdownOpen: isOpen })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
-
-
-
- )}
- >
- {kebabDropdownItems}
-
-
-
- this.setState({ isDropdownOpen: isOpen })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- }
- isFullHeight
- >
- John Smith
-
- )}
- >
- {userDropdownItems}
-
-
-
-
-
-
- );
-
- const Header = (
-
-
-
-
-
-
-
-
-
-
-
- {headerToolbar}
-
- );
- const Sidebar = (
-
- {PageNav}
-
- );
- const pageId = 'main-content-page-layout-default-nav';
- const PageSkipToContent = Skip to content;
-
- const PageBreadcrumb = (
-
- Section home
- Section title
- Section title
-
- Section landing
-
-
- );
-
- const drawerContent = 'Panel content';
- const notificationDrawerActions = (
- <>
-
- Mark all read
-
- this.showNotifications(false)}>
- Clear all
-
- this.showNotifications(true)}>
- Unclear last
-
- Settings
- >
- );
- const notificationDrawerDropdownItems = (
- <>
- ev.preventDefault()}
- >
- Link
-
- Action
-
-
- Disabled Link
-
- >
- );
-
- const notificationDrawer = (
-
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-0')}
- isExpanded={isActionsMenuOpen['toggle-id-0'] || false}
- >
-
-
- )}
- >
- {notificationDrawerActions}
-
-
-
- {showNotifications && (
-
-
-
- this.onListItemClick('group-1', 'notification-5')}
- isRead={!this.isUnread('group-1', 'notification-5')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-5')}
- isExpanded={isActionsMenuOpen['toggle-id-5'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is an info notification description.
-
-
- this.onListItemClick('group-1', 'notification-6')}
- isRead={!this.isUnread('group-1', 'notification-6')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-6')}
- isExpanded={isActionsMenuOpen['toggle-id-6'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a danger notification description. This is a long description to show how the title will
- wrap if it is long and wraps to multiple lines.
-
-
- this.onListItemClick('group-1', 'notification-7')}
- isRead={!this.isUnread('group-1', 'notification-7')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-7')}
- isExpanded={isActionsMenuOpen['toggle-id-7'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a warning notification description.
-
-
- this.onListItemClick('group-1', 'notification-8')}
- isRead={!this.isUnread('group-1', 'notification-8')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-8')}
- isExpanded={isActionsMenuOpen['toggle-id-8'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a success notification description.
-
-
-
-
-
-
- this.onListItemClick('group-2', 'notification-9')}
- isRead={!this.isUnread('group-2', 'notification-9')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-9')}
- isExpanded={isActionsMenuOpen['toggle-id-9'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is an info notification description.
-
-
- this.onListItemClick('group-2', 'notification-10')}
- isRead={!this.isUnread('group-2', 'notification-10')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-10')}
- isExpanded={isActionsMenuOpen['toggle-id-10'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a danger notification description. This is a long description to show how the title will
- wrap if it is long and wraps to multiple lines.
-
-
- this.onListItemClick('group-2', 'notification-11')}
- isRead={!this.isUnread('group-2', 'notification-11')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-11')}
- isExpanded={isActionsMenuOpen['toggle-id-11'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a warning notification description.
-
-
- this.onListItemClick('group-2', 'notification-12')}
- isRead={!this.isUnread('group-2', 'notification-12')}
- >
-
- !isOpen && this.setState({ isActionsMenuOpen: {} })}
- popperProps={{ position: 'right' }}
- toggle={(toggleRef) => (
- this.onToggle('toggle-id-12')}
- isExpanded={isActionsMenuOpen['toggle-id-12'] || false}
- >
-
-
- )}
- >
- {notificationDrawerDropdownItems}
-
-
-
- This is a success notification description.
-
-
-
-
-
-
-
- }
- />
-
- There are currently no critical alerts firing. There may be firing alerts of other severities or
- silenced critical alerts however.
-
-
-
-
-
-
-
-
-
-
- )}
- {!showNotifications && (
-
- }
- />
-
- There are currently no alerts. There may be silenced critical alerts however.
-
-
-
-
-
-
-
- )}
-
-
- );
+```ts file='./examples/NotificationDrawerGrouped.tsx' isFullscreen
- return (
-
- this.focusDrawer(event)}
- skipToContent={PageSkipToContent}
- breadcrumb={PageBreadcrumb}
- mainContainerId={pageId}
- >
-
-
- Main title
-
- Body text should be Overpass Regular at 16px. It should have leading of 24px because
- of its relative line height of 1.5.
-
-
-
-
- Panel section content
-
-
-
- );
- }
-}
```
diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx
new file mode 100644
index 00000000000..721e40cdb1f
--- /dev/null
+++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx
@@ -0,0 +1,557 @@
+import React from 'react';
+import {
+ Avatar,
+ Brand,
+ Breadcrumb,
+ BreadcrumbItem,
+ Button,
+ ButtonVariant,
+ Divider,
+ Dropdown,
+ DropdownItem,
+ DropdownList,
+ EmptyState,
+ EmptyStateActions,
+ EmptyStateBody,
+ EmptyStateIcon,
+ EmptyStateHeader,
+ EmptyStateFooter,
+ EmptyStateVariant,
+ MenuToggle,
+ Nav,
+ NavItem,
+ NavList,
+ NotificationBadge,
+ NotificationDrawer,
+ NotificationDrawerBody,
+ NotificationDrawerHeader,
+ NotificationDrawerList,
+ NotificationDrawerListItem,
+ NotificationDrawerListItemBody,
+ NotificationDrawerListItemHeader,
+ Page,
+ PageSection,
+ PageSectionVariants,
+ PageSidebar,
+ PageSidebarBody,
+ SkipToContent,
+ TextContent,
+ Text,
+ PageToggleButton,
+ Masthead,
+ MastheadMain,
+ MastheadToggle,
+ MastheadContent,
+ MastheadBrand,
+ Toolbar,
+ ToolbarItem,
+ ToolbarGroup,
+ ToolbarContent
+} from '@patternfly/react-core';
+import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
+import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
+import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
+import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
+import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
+import BarsIcon from '@patternfly/react-icons/dist/js/icons/bars-icon';
+import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg';
+import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
+import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg';
+
+export const NotificationDrawerBasic: React.FunctionComponent = () => {
+ const drawerRef = React.useRef(null);
+
+ const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
+ const [isKebabDropdownOpen, setIsKebabDropdownOpen] = React.useState(false);
+ const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false);
+
+ interface UnreadMap {
+ [notificationId: string]: boolean;
+ }
+
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [isUnreadMap, setIsUnreadMap] = React.useState({
+ 'notification-1': true,
+ 'notification-2': true,
+ 'notification-3': false,
+ 'notification-4': false
+ });
+
+ const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true);
+
+ interface ActionsMenu {
+ [toggleId: string]: boolean;
+ }
+
+ const [isActionsMenuOpen, setIsActionsMenuOpen] = React.useState({});
+
+ const onNavSelect = (
+ _event: React.FormEvent,
+ selectedItem: {
+ groupId: number | string;
+ itemId: number | string;
+ to: string;
+ }
+ ) => setActiveItem(selectedItem.itemId);
+
+ const onDropdownToggle = () => setIsDropdownOpen((prevState) => !prevState);
+ const onDropdownSelect = () => setIsDropdownOpen(false);
+ const onKebabDropdownToggle = () => setIsKebabDropdownOpen((prevState) => !prevState);
+ const onKebabDropdownSelect = () => setIsKebabDropdownOpen(false);
+ const onCloseNotificationDrawer = (_event: any) => setIsDrawerExpanded((prevState) => !prevState);
+
+ const onToggle = (id: string) => {
+ setIsActionsMenuOpen({ [id]: !isActionsMenuOpen[id] });
+ };
+
+ const closeActionsMenu = () => setIsActionsMenuOpen({});
+
+ const onListItemClick = (id: string) => {
+ if (!isUnreadMap) {
+ return;
+ }
+ setIsUnreadMap({ ...isUnreadMap, [id]: false });
+ };
+
+ const getNumberUnread: () => number = () => {
+ if (!isUnreadMap) {
+ return 0;
+ }
+ return Object.values(isUnreadMap).reduce((count, value) => count + (value ? 1 : 0), 0);
+ };
+
+ const markAllRead = () => setIsUnreadMap(null);
+
+ const showNotifications = (showNotifications: boolean) => {
+ setIsUnreadMap(null);
+ setShouldShowNotifications(showNotifications);
+ };
+
+ const focusDrawer = (_event: any) => {
+ if (drawerRef.current === null) {
+ return;
+ }
+ const firstTabbableItem = drawerRef.current.querySelector('a, button') as
+ | HTMLAnchorElement
+ | HTMLButtonElement
+ | null;
+ firstTabbableItem?.focus();
+ };
+
+ const PageNav = (
+
+ );
+ const kebabDropdownItems = (
+ <>
+
+ Settings
+
+
+ Help
+
+ >
+ );
+ const userDropdownItems = (
+ <>
+ My profile
+ User management
+ Logout
+ >
+ );
+ const headerToolbar = (
+
+
+
+
+
+ onCloseNotificationDrawer(event)}
+ aria-label="Notifications"
+ isExpanded={isDrawerExpanded}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) => (
+
+
+
+ )}
+ >
+ {kebabDropdownItems}
+
+
+
+ ) => (
+ }
+ isFullHeight
+ >
+ John Smith
+
+ )}
+ >
+ {userDropdownItems}
+
+
+
+
+
+
+ );
+
+ const Header = (
+
+
+
+
+
+
+
+
+
+
+
+ {headerToolbar}
+
+ );
+ const Sidebar = (
+
+ {PageNav}
+
+ );
+ const pageId = 'main-content-page-layout-default-nav';
+ const PageSkipToContent = Skip to content;
+
+ const PageBreadcrumb = (
+
+ Section home
+ Section title
+ Section title
+
+ Section landing
+
+
+ );
+
+ const notificationDrawerActions = (
+ <>
+
+ Mark all read
+
+ showNotifications(false)}>
+ Clear all
+
+ showNotifications(true)}>
+ Unclear last
+
+ Settings
+ >
+ );
+ const notificationDrawerDropdownItems = (
+ <>
+ ev.preventDefault()}
+ >
+ Link
+
+ Action
+
+
+ Disabled Link
+
+ >
+ );
+ const notificationDrawer = (
+
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-0')}
+ isExpanded={isActionsMenuOpen['toggle-id-0'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerActions}
+
+
+
+ {shouldShowNotifications && (
+
+ onListItemClick('notification-1')}
+ isRead={isUnreadMap === null || !isUnreadMap['notification-1']}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-1')}
+ isExpanded={isActionsMenuOpen['toggle-id-1'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is an info notification description.
+
+
+ onListItemClick('notification-2')}
+ isRead={isUnreadMap === null || !isUnreadMap['notification-2']}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-2')}
+ isExpanded={isActionsMenuOpen['toggle-id-2'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a danger notification description. This is a long description to show how the title will wrap if
+ it is long and wraps to multiple lines.
+
+
+ onListItemClick('notification-3')}
+ isRead={isUnreadMap === null || !isUnreadMap['notification-3']}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-3')}
+ isExpanded={isActionsMenuOpen['toggle-id-3'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a warning notification description.
+
+
+ onListItemClick('notification-4')}
+ isRead={isUnreadMap === null || !isUnreadMap['notification-4']}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-4')}
+ isExpanded={isActionsMenuOpen['toggle-id-4'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a success notification description.
+
+
+
+ )}
+ {!shouldShowNotifications && (
+
+ }
+ />
+
+ There are currently no alerts. There may be silenced critical alerts however.
+
+
+
+
+
+
+
+ )}
+
+
+ );
+
+ return (
+
+ | KeyboardEvent | React.TransitionEvent
+ ) => focusDrawer(event)}
+ isNotificationDrawerExpanded={isDrawerExpanded}
+ skipToContent={PageSkipToContent}
+ breadcrumb={PageBreadcrumb}
+ mainContainerId={pageId}
+ >
+
+
+ Main title
+
+ Body text should be Overpass Regular at 16px. It should have leading of 24px because
+ of its relative line height of 1.5.
+
+
+
+ Panel section content
+
+
+ );
+};
diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx
new file mode 100644
index 00000000000..e1bf93e05c9
--- /dev/null
+++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx
@@ -0,0 +1,796 @@
+import React from 'react';
+import {
+ Avatar,
+ Brand,
+ Breadcrumb,
+ BreadcrumbItem,
+ Button,
+ ButtonVariant,
+ Divider,
+ Dropdown,
+ DropdownItem,
+ DropdownList,
+ EmptyState,
+ EmptyStateActions,
+ EmptyStateBody,
+ EmptyStateIcon,
+ EmptyStateHeader,
+ EmptyStateFooter,
+ EmptyStateVariant,
+ MenuToggle,
+ Nav,
+ NavItem,
+ NavList,
+ NotificationBadge,
+ NotificationDrawer,
+ NotificationDrawerBody,
+ NotificationDrawerGroup,
+ NotificationDrawerGroupList,
+ NotificationDrawerHeader,
+ NotificationDrawerList,
+ NotificationDrawerListItem,
+ NotificationDrawerListItemBody,
+ NotificationDrawerListItemHeader,
+ Page,
+ PageSection,
+ PageSectionVariants,
+ PageSidebar,
+ PageSidebarBody,
+ SkipToContent,
+ TextContent,
+ Text,
+ PageToggleButton,
+ Masthead,
+ MastheadMain,
+ MastheadToggle,
+ MastheadContent,
+ MastheadBrand,
+ Toolbar,
+ ToolbarItem,
+ ToolbarGroup,
+ ToolbarContent
+} from '@patternfly/react-core';
+import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
+import CogIcon from '@patternfly/react-icons/dist/esm/icons/cog-icon';
+import BarsIcon from '@patternfly/react-icons/dist/esm/icons/bars-icon';
+import HelpIcon from '@patternfly/react-icons/dist/esm/icons/help-icon';
+import SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon';
+import QuestionCircleIcon from '@patternfly/react-icons/dist/esm/icons/question-circle-icon';
+import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon';
+import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg';
+import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg';
+
+export const NotificationDrawerGrouped: React.FunctionComponent = () => {
+ const drawerRef = React.useRef(null);
+
+ const [isDropdownOpen, setIsDropdownOpen] = React.useState(false);
+ const [isKebabDropdownOpen, setIsKebabDropdownOpen] = React.useState(false);
+ const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false);
+ const [firstDrawerGroupExpanded, setFirstDrawerGroupExpanded] = React.useState(false);
+ const [secondDrawerGroupExpanded, setSecondDrawerGroupExpanded] = React.useState(true);
+ const [thirdDrawerGroupExpanded, setThirdDrawerGroupExpanded] = React.useState(false);
+
+ interface UnreadMap {
+ [groupName: string]: {
+ [notificationId: string]: boolean;
+ } | null;
+ }
+
+ const [activeItem, setActiveItem] = React.useState(0);
+ const [isUnreadMap, setIsUnreadMap] = React.useState({
+ 'group-1': {
+ 'notification-5': true,
+ 'notification-6': true
+ },
+ 'group-2': {
+ 'notification-9': true,
+ 'notification-10': true
+ },
+ 'group-3': null
+ });
+
+ const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true);
+
+ interface ActionsMenu {
+ [toggleId: string]: boolean;
+ }
+
+ const [isActionsMenuOpen, setIsActionsMenuOpen] = React.useState({});
+
+ const onNavSelect = (
+ _event: React.FormEvent,
+ selectedItem: {
+ groupId: number | string;
+ itemId: number | string;
+ to: string;
+ }
+ ) => setActiveItem(selectedItem.itemId);
+
+ const onDropdownToggle = () => setIsDropdownOpen((prevState) => !prevState);
+ const onDropdownSelect = () => setIsDropdownOpen(false);
+ const onKebabDropdownToggle = () => setIsKebabDropdownOpen((prevState) => !prevState);
+ const onKebabDropdownSelect = () => setIsKebabDropdownOpen(false);
+ const onCloseNotificationDrawer = (_event: any) => setIsDrawerExpanded((prevState) => !prevState);
+
+ const onToggle = (id: string) => {
+ setIsActionsMenuOpen({ [id]: !isActionsMenuOpen[id] });
+ };
+
+ const closeActionsMenu = () => setIsActionsMenuOpen({});
+
+ const onListItemClick = (groupId: string, id: string) => {
+ if (isUnreadMap === null) {
+ return;
+ }
+ if (!isUnreadMap[groupId]) {
+ setIsUnreadMap({ ...isUnreadMap, [groupId]: { [id]: false } });
+ } else {
+ setIsUnreadMap({ ...isUnreadMap, [groupId]: { ...isUnreadMap[groupId], [id]: false } });
+ }
+ };
+
+ const isUnread = (groupId: string, id: string) =>
+ isUnreadMap && isUnreadMap[groupId] !== null && isUnreadMap[groupId][id];
+
+ const getNumberUnread = (groupId: string | null) => {
+ if (!isUnreadMap) {
+ return 0;
+ }
+
+ if (groupId) {
+ const group = isUnreadMap[groupId];
+ if (!group) {
+ return 0;
+ }
+ return Object.values(group).reduce((count, value) => (value ? count + 1 : count), 0);
+ }
+
+ return Object.keys(isUnreadMap).reduce((count, groupId) => {
+ const group = isUnreadMap[groupId];
+ if (!group) {
+ return count;
+ }
+
+ return Object.values(group).reduce((groupCount, value) => (value ? groupCount + 1 : groupCount), count);
+ }, 0);
+ };
+
+ const markAllRead = () => setIsUnreadMap(null);
+
+ const showNotifications = (showNotifications: boolean) => {
+ setIsUnreadMap(null);
+ setShouldShowNotifications(showNotifications);
+ };
+
+ const toggleFirstDrawer = (_event: any, value: boolean | ((prevState: boolean) => boolean)) => {
+ setFirstDrawerGroupExpanded(value);
+ };
+
+ const toggleSecondDrawer = (_event: any, value: boolean | ((prevState: boolean) => boolean)) => {
+ setSecondDrawerGroupExpanded(value);
+ };
+
+ const toggleThirdDrawer = (_event: any, value: boolean | ((prevState: boolean) => boolean)) => {
+ setThirdDrawerGroupExpanded(value);
+ };
+
+ const focusDrawer = (_event: any) => {
+ if (drawerRef.current === null) {
+ return;
+ }
+ // Prevent the NotificationDrawer from receiving focus if a drawer group item is opened
+ if (!document.activeElement?.closest(`.${drawerRef.current.className}`)) {
+ const firstTabbableItem = drawerRef.current.querySelector('a, button') as
+ | HTMLAnchorElement
+ | HTMLButtonElement
+ | null;
+ firstTabbableItem?.focus();
+ }
+ };
+
+ const PageNav = (
+
+ );
+ const kebabDropdownItems = (
+ <>
+
+ Settings
+
+
+ Help
+
+ >
+ );
+ const userDropdownItems = (
+ <>
+ My profile
+ User management
+ Logout
+ >
+ );
+ const headerToolbar = (
+
+
+
+
+
+ onCloseNotificationDrawer(event)}
+ aria-label="Notifications"
+ isExpanded={isDrawerExpanded}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ) => (
+
+
+
+ )}
+ >
+ {kebabDropdownItems}
+
+
+
+ ) => (
+ }
+ isFullHeight
+ >
+ John Smith
+
+ )}
+ >
+ {userDropdownItems}
+
+
+
+
+
+
+ );
+
+ const Header = (
+
+
+
+
+
+
+
+
+
+
+
+ {headerToolbar}
+
+ );
+ const Sidebar = (
+
+ {PageNav}
+
+ );
+ const pageId = 'main-content-page-layout-default-nav';
+ const PageSkipToContent = Skip to content;
+
+ const PageBreadcrumb = (
+
+ Section home
+ Section title
+ Section title
+
+ Section landing
+
+
+ );
+
+ const notificationDrawerActions = (
+ <>
+
+ Mark all read
+
+ showNotifications(false)}>
+ Clear all
+
+ showNotifications(true)}>
+ Unclear last
+
+ Settings
+ >
+ );
+ const notificationDrawerDropdownItems = (
+ <>
+ ev.preventDefault()}
+ >
+ Link
+
+ Action
+
+
+ Disabled Link
+
+ >
+ );
+
+ const notificationDrawer = (
+
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-0')}
+ isExpanded={isActionsMenuOpen['toggle-id-0'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerActions}
+
+
+
+ {shouldShowNotifications && (
+
+
+
+ onListItemClick('group-1', 'notification-5')}
+ isRead={!isUnread('group-1', 'notification-5')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-5')}
+ isExpanded={isActionsMenuOpen['toggle-id-5'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is an info notification description.
+
+
+ onListItemClick('group-1', 'notification-6')}
+ isRead={!isUnread('group-1', 'notification-6')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-6')}
+ isExpanded={isActionsMenuOpen['toggle-id-6'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a danger notification description. This is a long description to show how the title will
+ wrap if it is long and wraps to multiple lines.
+
+
+ onListItemClick('group-1', 'notification-7')}
+ isRead={!isUnread('group-1', 'notification-7')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-7')}
+ isExpanded={isActionsMenuOpen['toggle-id-7'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a warning notification description.
+
+
+ onListItemClick('group-1', 'notification-8')}
+ isRead={!isUnread('group-1', 'notification-8')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-8')}
+ isExpanded={isActionsMenuOpen['toggle-id-8'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a success notification description.
+
+
+
+
+
+
+ onListItemClick('group-2', 'notification-9')}
+ isRead={!isUnread('group-2', 'notification-9')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-9')}
+ isExpanded={isActionsMenuOpen['toggle-id-9'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is an info notification description.
+
+
+ onListItemClick('group-2', 'notification-10')}
+ isRead={!isUnread('group-2', 'notification-10')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-10')}
+ isExpanded={isActionsMenuOpen['toggle-id-10'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a danger notification description. This is a long description to show how the title will
+ wrap if it is long and wraps to multiple lines.
+
+
+ onListItemClick('group-2', 'notification-11')}
+ isRead={!isUnread('group-2', 'notification-11')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-11')}
+ isExpanded={isActionsMenuOpen['toggle-id-11'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a warning notification description.
+
+
+ onListItemClick('group-2', 'notification-12')}
+ isRead={!isUnread('group-2', 'notification-12')}
+ >
+
+ !isOpen && closeActionsMenu()}
+ popperProps={{ position: 'right' }}
+ toggle={(toggleRef: React.RefObject) => (
+ onToggle('toggle-id-12')}
+ isExpanded={isActionsMenuOpen['toggle-id-12'] || false}
+ >
+
+
+ )}
+ >
+ {notificationDrawerDropdownItems}
+
+
+
+ This is a success notification description.
+
+
+
+
+
+
+
+ }
+ />
+
+ There are currently no critical alerts firing. There may be firing alerts of other severities or
+ silenced critical alerts however.
+
+
+
+
+
+
+
+
+
+
+ )}
+ {!shouldShowNotifications && (
+
+ }
+ />
+
+ There are currently no alerts. There may be silenced critical alerts however.
+
+
+
+
+
+
+
+ )}
+
+
+ );
+
+ return (
+
+ | KeyboardEvent | React.TransitionEvent
+ ) => focusDrawer(event)}
+ skipToContent={PageSkipToContent}
+ breadcrumb={PageBreadcrumb}
+ mainContainerId={pageId}
+ >
+
+
+ Main title
+
+ Body text should be Overpass Regular at 16px. It should have leading of 24px because
+ of its relative line height of 1.5.
+
+
+
+ Panel section content
+
+
+ );
+};