From 8d54c8bc3f18d539c151c1e98f85108fa6ac1cee Mon Sep 17 00:00:00 2001 From: adamviktora Date: Mon, 14 Aug 2023 16:41:16 +0200 Subject: [PATCH 1/6] chore(NotificationDrawer): basic drawer demo conversion --- .../NotificationDrawer/NotificationDrawer.md | 597 +----------------- .../examples/NotificationDrawerBasic.tsx | 579 +++++++++++++++++ 2 files changed, 580 insertions(+), 596 deletions(-) create mode 100644 packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx diff --git a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md index 871e20c3e1a..fc091cecf96 100644 --- a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md +++ b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md @@ -22,603 +22,8 @@ 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/Avatar/examples/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] } - })); - }; - - 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} - > - - - - - - - - - - - - - - - - - - - - ); - - 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'; +```js file='./examples/NotificationDrawerBasic.tsx' isFullscreen - 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 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..0ad4c9bdc77 --- /dev/null +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx @@ -0,0 +1,579 @@ +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/Avatar/examples/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 BasicNotificationDrawer: 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 { + ['notification-1']?: boolean | undefined; + ['notification-2']?: boolean | undefined; + ['notification-3']?: boolean | undefined; + ['notification-4']?: boolean | undefined; + } + + 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 { + ['toggle-id-0']: boolean; + ['toggle-id-1']: boolean; + ['toggle-id-2']: boolean; + ['toggle-id-3']: boolean; + ['toggle-id-4']: boolean; + } + + const [isActionsMenuOpen, setIsActionsMenuOpen] = React.useState({ + 'toggle-id-0': false, + 'toggle-id-1': false, + 'toggle-id-2': false, + 'toggle-id-3': false, + 'toggle-id-4': false + }); + + 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: 'toggle-id-0' | 'toggle-id-1' | 'toggle-id-2' | 'toggle-id-3' | 'toggle-id-4') => { + setIsActionsMenuOpen((prevState) => ({ + ...prevState, + [id]: !prevState[id] + })); + }; + + const closeActionMenu = () => + setIsActionsMenuOpen({ + 'toggle-id-0': false, + 'toggle-id-1': false, + 'toggle-id-2': false, + 'toggle-id-3': false, + 'toggle-id-4': false + }); + + const onSelect = () => closeActionMenu(); + + const onListItemClick = (id: 'notification-1' | 'notification-2' | 'notification-3' | 'notification-4') => { + if (isUnreadMap === null) { + return; + } + setIsUnreadMap((prevState) => ({ ...prevState, [id]: false })); + }; + + const getNumberUnread = () => { + if (isUnreadMap === null) { + return 0; + } + 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'); + firstTabbableItem.focus(); + }; + + const PageNav = ( + + ); + const kebabDropdownItems = ( + <> + + Settings + + + Help + + + ); + const userDropdownItems = ( + <> + My profile + User management + Logout + + ); + const headerToolbar = ( + + + + + + onCloseNotificationDrawer(event)} + aria-label="Notifications" + isExpanded={isDrawerExpanded} + > + + + + + + + + + + + + + + + + + + + + ); + + 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 && closeActionMenu()} + 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 && closeActionMenu()} + 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 && closeActionMenu()} + 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 && closeActionMenu()} + 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 && closeActionMenu()} + 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 +
+
+ ); +}; From 14f83c672d2b03030dc1e3f2b205cd815f13015c Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 17 Aug 2023 21:39:29 +0200 Subject: [PATCH 2/6] chore(NotificationDrawer): grouped demo + fixes --- .../NotificationDrawer/NotificationDrawer.md | 840 +----------------- .../examples/NotificationDrawerBasic.tsx | 80 +- .../examples/NotificationDrawerGrouped.tsx | 793 +++++++++++++++++ 3 files changed, 823 insertions(+), 890 deletions(-) create mode 100644 packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx diff --git a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md index fc091cecf96..b7e0991c080 100644 --- a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md +++ b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md @@ -30,844 +30,6 @@ import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg'; 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/Avatar/examples/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} - > - - - - - - - - - - - - - - - - - - - - ); - - 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. - - - - - - - - )} - - - ); +```js 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 index 0ad4c9bdc77..29c81e2206a 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx @@ -59,17 +59,14 @@ 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 BasicNotificationDrawer: React.FunctionComponent = () => { - const drawerRef = React.useRef(null); + 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 { - ['notification-1']?: boolean | undefined; - ['notification-2']?: boolean | undefined; - ['notification-3']?: boolean | undefined; - ['notification-4']?: boolean | undefined; + [notificationId: string]: boolean; } const [activeItem, setActiveItem] = React.useState(0); @@ -83,20 +80,10 @@ export const BasicNotificationDrawer: React.FunctionComponent = () => { const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true); interface ActionsMenu { - ['toggle-id-0']: boolean; - ['toggle-id-1']: boolean; - ['toggle-id-2']: boolean; - ['toggle-id-3']: boolean; - ['toggle-id-4']: boolean; + [toggleId: string]: boolean; } - const [isActionsMenuOpen, setIsActionsMenuOpen] = React.useState({ - 'toggle-id-0': false, - 'toggle-id-1': false, - 'toggle-id-2': false, - 'toggle-id-3': false, - 'toggle-id-4': false - }); + const [isActionsMenuOpen, setIsActionsMenuOpen] = React.useState({}); const onNavSelect = ( _event: React.FormEvent, @@ -113,36 +100,24 @@ export const BasicNotificationDrawer: React.FunctionComponent = () => { const onKebabDropdownSelect = () => setIsKebabDropdownOpen(false); const onCloseNotificationDrawer = (_event: any) => setIsDrawerExpanded((prevState) => !prevState); - const onToggle = (id: 'toggle-id-0' | 'toggle-id-1' | 'toggle-id-2' | 'toggle-id-3' | 'toggle-id-4') => { - setIsActionsMenuOpen((prevState) => ({ - ...prevState, - [id]: !prevState[id] - })); + const onToggle = (id: string) => { + setIsActionsMenuOpen({ [id]: !isActionsMenuOpen[id] }); }; - const closeActionMenu = () => - setIsActionsMenuOpen({ - 'toggle-id-0': false, - 'toggle-id-1': false, - 'toggle-id-2': false, - 'toggle-id-3': false, - 'toggle-id-4': false - }); - - const onSelect = () => closeActionMenu(); + const closeActionsMenu = () => setIsActionsMenuOpen({}); - const onListItemClick = (id: 'notification-1' | 'notification-2' | 'notification-3' | 'notification-4') => { - if (isUnreadMap === null) { + const onListItemClick = (id: string) => { + if (!isUnreadMap) { return; } - setIsUnreadMap((prevState) => ({ ...prevState, [id]: false })); + setIsUnreadMap({ ...isUnreadMap, [id]: false }); }; - const getNumberUnread = () => { - if (isUnreadMap === null) { + const getNumberUnread: () => number = () => { + if (!isUnreadMap) { return 0; } - Object.values(isUnreadMap).reduce((count, value) => count + (value ? 1 : 0), 0); + return Object.values(isUnreadMap).reduce((count, value) => count + (value ? 1 : 0), 0); }; const markAllRead = () => setIsUnreadMap(null); @@ -156,8 +131,11 @@ export const BasicNotificationDrawer: React.FunctionComponent = () => { if (drawerRef.current === null) { return; } - const firstTabbableItem = drawerRef.current.querySelector('a, button'); - firstTabbableItem.focus(); + const firstTabbableItem = drawerRef.current.querySelector('a, button') as + | HTMLAnchorElement + | HTMLButtonElement + | null; + firstTabbableItem?.focus(); }; const PageNav = ( @@ -356,10 +334,10 @@ export const BasicNotificationDrawer: React.FunctionComponent = () => { !isOpen && closeActionMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} toggle={(toggleRef: React.RefObject) => ( { srTitle="Info notification:" > !isOpen && closeActionMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} toggle={(toggleRef: React.RefObject) => ( { srTitle="Danger notification:" > !isOpen && closeActionMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} toggle={(toggleRef: React.RefObject) => ( { srTitle="Warning notification:" > !isOpen && closeActionMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} toggle={(toggleRef: React.RefObject) => ( { srTitle="Success notification:" > !isOpen && closeActionMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} toggle={(toggleRef: React.RefObject) => ( { } /> 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..0b442e94531 --- /dev/null +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx @@ -0,0 +1,793 @@ +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/Avatar/examples/avatarImg.svg'; +import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg'; + +export const GroupedNotificationDrawer: 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] && 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} + > + + + + + + + + + + + + + + + + + + + + ); + + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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) => ( + 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 ( + + 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 +
+
+ ); +}; From e7ee850231757d645fded9598c907305ac79b2e3 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Fri, 18 Aug 2023 10:52:42 +0200 Subject: [PATCH 3/6] chore(NotificationDrawer): add types --- .../examples/NotificationDrawerGrouped.tsx | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx index 0b442e94531..e0729b5c0f6 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx @@ -129,7 +129,8 @@ export const GroupedNotificationDrawer: React.FunctionComponent = () => { } }; - const isUnread = (groupId: string, id: string) => isUnreadMap && isUnreadMap[groupId] && isUnreadMap[groupId][id]; + const isUnread = (groupId: string, id: string) => + isUnreadMap && isUnreadMap[groupId] !== null && isUnreadMap[groupId][id]; const getNumberUnread = (groupId: string | null) => { if (!isUnreadMap) { @@ -270,7 +271,7 @@ export const GroupedNotificationDrawer: React.FunctionComponent = () => { onSelect={onKebabDropdownSelect} onOpenChange={setIsKebabDropdownOpen} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={onDropdownSelect} onOpenChange={setIsDropdownOpen} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { key="link" to="#" // Prevent default onClick behavior for demo purposes - onClick={(ev) => ev.preventDefault()} + onClick={(ev: { preventDefault: () => any }) => ev.preventDefault()} > Link @@ -387,9 +388,9 @@ export const GroupedNotificationDrawer: React.FunctionComponent = () => { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-0'] || false} id="notification-0" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-5'] || false} id="notification-5" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-6'] || false} id="notification-6" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-7'] || false} id="notification-7" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-8'] || false} id="notification-8" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-9'] || false} id="notification-9" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-10'] || false} id="notification-10" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-11'] || false} id="notification-11" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { onSelect={closeActionsMenu} isOpen={isActionsMenuOpen['toggle-id-12'] || false} id="notification-12" - onOpenChange={(isOpen) => !isOpen && closeActionsMenu()} + onOpenChange={(isOpen: boolean) => !isOpen && closeActionsMenu()} popperProps={{ position: 'right' }} - toggle={(toggleRef) => ( + toggle={(toggleRef: React.RefObject) => ( { isManagedSidebar notificationDrawer={notificationDrawer} isNotificationDrawerExpanded={isDrawerExpanded} - onNotificationDrawerExpand={(event) => focusDrawer(event)} + onNotificationDrawerExpand={( + event: React.MouseEvent | KeyboardEvent | React.TransitionEvent + ) => focusDrawer(event)} skipToContent={PageSkipToContent} breadcrumb={PageBreadcrumb} mainContainerId={pageId} From 0c2434435a11cfaa522da89b089f07c953d80752 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 31 Aug 2023 10:01:04 +0200 Subject: [PATCH 4/6] chore(NotificationDrawer): update svg paths --- .../src/demos/NotificationDrawer/NotificationDrawer.md | 2 +- .../NotificationDrawer/examples/NotificationDrawerBasic.tsx | 2 +- .../NotificationDrawer/examples/NotificationDrawerGrouped.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md index b7e0991c080..b1816c6c0b8 100644 --- a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md +++ b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md @@ -9,7 +9,7 @@ import BarsIcon from '@patternfly/react-icons/dist/js/icons/bars-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 SearchIcon from '@patternfly/react-icons/dist/esm/icons/search-icon'; -import imgAvatar from '@patternfly/react-core/src/components/Avatar/examples/avatarImg.svg'; +import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.svg'; import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg'; diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx index 29c81e2206a..987f4ee0a98 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx @@ -54,7 +54,7 @@ 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/Avatar/examples/avatarImg.svg'; +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'; diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx index e0729b5c0f6..c878fe0c328 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx @@ -57,7 +57,7 @@ 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/Avatar/examples/avatarImg.svg'; +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 GroupedNotificationDrawer: React.FunctionComponent = () => { From d16853adf9d247a91aa253dc6f4222a1d3c05d4a Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 31 Aug 2023 10:12:59 +0200 Subject: [PATCH 5/6] chore(NotificationDrawer): use ts shortcut, rename --- .../NotificationDrawer/NotificationDrawer.md | 4 ++-- .../examples/NotificationDrawerBasic.tsx | 10 +++++----- .../examples/NotificationDrawerGrouped.tsx | 18 +++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md index b1816c6c0b8..0340deeb364 100644 --- a/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md +++ b/packages/react-core/src/demos/NotificationDrawer/NotificationDrawer.md @@ -22,7 +22,7 @@ import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg'; ### Basic -```js file='./examples/NotificationDrawerBasic.tsx' isFullscreen +```ts file='./examples/NotificationDrawerBasic.tsx' isFullscreen ``` @@ -30,6 +30,6 @@ import pfLogo from '@patternfly/react-core/src/demos/assets/pf-logo.svg'; 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 file='./examples/NotificationDrawerGrouped.tsx' isFullscreen +```ts file='./examples/NotificationDrawerGrouped.tsx' isFullscreen ``` diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx index 987f4ee0a98..721e40cdb1f 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerBasic.tsx @@ -58,12 +58,12 @@ import imgAvatar from '@patternfly/react-core/src/components/assets/avatarImg.sv 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 BasicNotificationDrawer: React.FunctionComponent = () => { +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); + const [isDropdownOpen, setIsDropdownOpen] = React.useState(false); + const [isKebabDropdownOpen, setIsKebabDropdownOpen] = React.useState(false); + const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false); interface UnreadMap { [notificationId: string]: boolean; @@ -77,7 +77,7 @@ export const BasicNotificationDrawer: React.FunctionComponent = () => { 'notification-4': false }); - const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true); + const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true); interface ActionsMenu { [toggleId: string]: boolean; diff --git a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx index c878fe0c328..e1bf93e05c9 100644 --- a/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx +++ b/packages/react-core/src/demos/NotificationDrawer/examples/NotificationDrawerGrouped.tsx @@ -60,15 +60,15 @@ import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-ico 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 GroupedNotificationDrawer: React.FunctionComponent = () => { +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); + 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]: { @@ -89,7 +89,7 @@ export const GroupedNotificationDrawer: React.FunctionComponent = () => { 'group-3': null }); - const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true); + const [shouldShowNotifications, setShouldShowNotifications] = React.useState(true); interface ActionsMenu { [toggleId: string]: boolean; @@ -369,7 +369,7 @@ export const GroupedNotificationDrawer: React.FunctionComponent = () => { key="link" to="#" // Prevent default onClick behavior for demo purposes - onClick={(ev: { preventDefault: () => any }) => ev.preventDefault()} + onClick={(ev: any) => ev.preventDefault()} > Link From 25b9085a6e91015cab4381a78fcddfd5fc60a8e3 Mon Sep 17 00:00:00 2001 From: adamviktora Date: Thu, 31 Aug 2023 10:37:32 +0200 Subject: [PATCH 6/6] empty commit to solve docs job failing