From 5b081158830ec569c848ade760aba0fac9b0fbb9 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Fri, 14 Jul 2023 17:52:55 -0400 Subject: [PATCH 01/20] First pass for notification drawers, dropdowns and filters. --- package-lock.json | 1 - src/components/Header/Tools.tsx | 2 +- .../DrawerPanelContent.tsx | 83 +++++++++++++++---- .../NotificationsDrawer/NotificationItem.tsx | 73 ++++++++++++++++ src/hooks/useChromeServiceEvents.ts | 3 +- src/layouts/DefaultLayout.tsx | 3 +- src/redux/chromeReducers.ts | 2 + src/redux/notificationTestData.js | 37 +++++++++ src/redux/store.d.ts | 6 +- 9 files changed, 191 insertions(+), 19 deletions(-) create mode 100644 src/components/NotificationsDrawer/NotificationItem.tsx create mode 100644 src/redux/notificationTestData.js diff --git a/package-lock.json b/package-lock.json index e4e5807a0..b8d347a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,6 @@ "requires": true, "packages": { "": { - "name": "insights-chrome", "version": "0.0.0", "hasInstallScript": true, "license": "MIT", diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index 93d5b460c..00ab9f3af 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -96,7 +96,7 @@ const Tools = () => { const enableAuthDropdownOption = useFlag('platform.chrome.dropdown.authfactor'); const previewEnabled = useFlag('platform.chrome.preview'); - const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + const isNotificationsEnabled = true; // useFlag('platform.chrome.notifications-drawer'); /* list out the items for the settings menu */ const settingsMenuDropdownItems = [ diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index a0f80d281..bff1e3f5e 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -1,21 +1,42 @@ -import React from 'react'; -import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; -import { EmptyState, EmptyStateBody, EmptyStateIcon } from '@patternfly/react-core/dist/dynamic/components/EmptyState'; -import { NotificationDrawer, NotificationDrawerHeader } from '@patternfly/react-core/dist/dynamic/components/NotificationDrawer'; -import { Text } from '@patternfly/react-core/dist/dynamic/components/Text'; -import { Title } from '@patternfly/react-core/dist/dynamic/components/Title'; +import React, { useState } from 'react'; +import { + Button, + EmptyState, + EmptyStateBody, + EmptyStateIcon, + KebabToggle, + Dropdown, + DropdownToggle, + DropdownItem, + DropdownSeparator, + DropdownPosition, + NotificationDrawer, + NotificationDrawerBody, + NotificationDrawerHeader, + Text, + Title, +} from '@patternfly/react-core'; import { useDispatch, useSelector } from 'react-redux'; import { toggleNotificationsDrawer } from '../../redux/actions'; import FilterIcon from '@patternfly/react-icons/dist/dynamic/icons/filter-icon'; -import EllipsisVIcon from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; import BellSlashIcon from '@patternfly/react-icons/dist/dynamic/icons/bell-slash-icon'; import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/dynamic/icons/external-link-square-alt-icon'; import { ReduxState } from '../../redux/store'; +import NotificationItem from './NotificationItem'; export type DrawerPanelProps = { innerRef: React.Ref; }; +const dropdownItems = [ + Mark visible as read, + Mark visible as unread, + , + View event log, + Configure notifications settings, + Manage my notification preferences, +]; + const EmptyNotifications = () => ( @@ -39,19 +60,53 @@ const EmptyNotifications = () => ( ); const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); const dispatch = useDispatch(); const notifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data || []); + + const onDropdownToggle = (isOpen: boolean) => { + setIsDropdownOpen(isOpen); + }; + + const onFilterDropdownToggle = (isOpen: boolean) => { + setIsFilterDropdownOpen(isOpen); + }; + + // TODO: IGNORE REPEATING SOURCES (ONLY COUNT ONCE) + const filterDropdownItems = () => { + return notifications.map(( notification, index ) => ( + {notification.source} + )); + }; + return ( dispatch(toggleNotificationsDrawer())}> - - + + + + } + isOpen={isFilterDropdownOpen} + dropdownItems={filterDropdownItems()} + id='filter-dropdown' + aria-label="Notifications filter" + isPlain + /> + } + isOpen={isDropdownOpen} + isPlain + dropdownItems={dropdownItems} + id="notification-dropdown" + /> - {notifications.length === 0 ? : 'TODO'} + + {notifications.length === 0 ? : } + ); }; diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx new file mode 100644 index 000000000..b060d69d3 --- /dev/null +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import { + Label, + Dropdown, + DropdownItem, + DropdownPosition, + Checkbox, + KebabToggle, + NotificationDrawerList, + NotificationDrawerListItem, + NotificationDrawerListItemBody, + NotificationDrawerListItemHeader, +} from '@patternfly/react-core'; +import { Notifications } from '../../redux/store'; + +// TODO: Switch from local state to redux management. Needed for "mark all visible" functionality from the panel. +const NotificationItem = ({ notifications }: { notifications: Notifications["data"] }) => { + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [isNotificationRead, setIsNotificationRead] = useState(false); + console.log('This is my current notification item: ', notifications); + + const onDropdownToggle = (isOpen: boolean) => { + setIsDropdownOpen(isOpen); + } + + const onCheckboxToggle = () => { + setIsNotificationRead(!isNotificationRead); + } + + const dropdownItems = [ + {`Mark as ${!isNotificationRead ? 'read' : 'unread'}`}, + ] + + return ( + + {notifications.map(( notification, index ) => ( + + + + + } + isOpen={isDropdownOpen} + isPlain + dropdownItems={dropdownItems} + id="notification-dropdown" + /> + + {/* TODO: Modify timestamp to only show correct "x minutes ago" */} + + + {notification.description} + + + + ))} + + ); +}; + +export default NotificationItem; diff --git a/src/hooks/useChromeServiceEvents.ts b/src/hooks/useChromeServiceEvents.ts index 136447a5d..68d268480 100644 --- a/src/hooks/useChromeServiceEvents.ts +++ b/src/hooks/useChromeServiceEvents.ts @@ -32,7 +32,8 @@ function isGenericEvent(event: unknown): event is GenericEvent { const useChromeServiceEvents = () => { const connection = useRef(); const dispatch = useDispatch(); - const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + // const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + const isNotificationsEnabled = true; const handlerMap: { [key in EventTypes]: (payload: Payload) => void } = useMemo( () => ({ diff --git a/src/layouts/DefaultLayout.tsx b/src/layouts/DefaultLayout.tsx index 4995c33c0..91502d10f 100644 --- a/src/layouts/DefaultLayout.tsx +++ b/src/layouts/DefaultLayout.tsx @@ -54,7 +54,8 @@ const DefaultLayout: React.FC = ({ hasBanner, selectedAccoun const tabbableElement = drawerPanelRef.current?.querySelector('a, button') as HTMLAnchorElement | HTMLButtonElement; tabbableElement.focus(); }; - const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + // const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + const isNotificationsEnabled = true; return ( ; count: number; }; From c4caa32d41a1034a1da1b5214a3a61f5d3983a2b Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Mon, 17 Jul 2023 13:13:32 -0400 Subject: [PATCH 02/20] Fixed issue with notifications items sharing state, and repeating filter options. --- .../DrawerPanelContent.tsx | 15 +++-- .../NotificationsDrawer/NotificationItem.tsx | 66 +++++++++---------- 2 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index bff1e3f5e..47bd59ec2 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -11,6 +11,7 @@ import { DropdownSeparator, DropdownPosition, NotificationDrawer, + NotificationDrawerList, NotificationDrawerBody, NotificationDrawerHeader, Text, @@ -73,10 +74,10 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { setIsFilterDropdownOpen(isOpen); }; - // TODO: IGNORE REPEATING SOURCES (ONLY COUNT ONCE) const filterDropdownItems = () => { - return notifications.map(( notification, index ) => ( - {notification.source} + const uniqueSources = new Set(notifications.map(notification => notification.source)); + return Array.from(uniqueSources).map((source, index) => ( + {source} )); }; @@ -104,8 +105,12 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { id="notification-dropdown" /> - - {notifications.length === 0 ? : } + + + {notifications.length === 0 ? : notifications.map((notification, index) => ( + + ))} + ); diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index b060d69d3..7f13ef081 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -14,10 +14,10 @@ import { import { Notifications } from '../../redux/store'; // TODO: Switch from local state to redux management. Needed for "mark all visible" functionality from the panel. -const NotificationItem = ({ notifications }: { notifications: Notifications["data"] }) => { +const NotificationItem = ({ notification }: { notification: Notifications["data"][0] }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isNotificationRead, setIsNotificationRead] = useState(false); - console.log('This is my current notification item: ', notifications); + console.log('This is my current notification item: ', notification); const onDropdownToggle = (isOpen: boolean) => { setIsDropdownOpen(isOpen); @@ -33,39 +33,37 @@ const NotificationItem = ({ notifications }: { notifications: Notifications["dat return ( - {notifications.map(( notification, index ) => ( - - + + - - - } - isOpen={isDropdownOpen} - isPlain - dropdownItems={dropdownItems} - id="notification-dropdown" - /> - - {/* TODO: Modify timestamp to only show correct "x minutes ago" */} - - - {notification.description} - - - - ))} + + } + isOpen={isDropdownOpen} + isPlain + dropdownItems={dropdownItems} + id="notification-dropdown" + /> + + {/* TODO: Modify timestamp to only show correct "x minutes ago" */} + + + {notification.description} + + + ); }; From 04fd7ee0850c1198865e6ef0e9f107f5880560d0 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 26 Jul 2023 09:11:27 -0400 Subject: [PATCH 03/20] Connected redux management for notifications. --- .../DrawerPanelContent.tsx | 55 +++++++++++++---- .../NotificationsDrawer/NotificationItem.tsx | 21 ++++--- src/redux/action-types.ts | 6 ++ src/redux/actions.ts | 21 +++++++ src/redux/chromeReducers.ts | 61 ++++++++++++++++++- src/redux/index.ts | 11 +++- src/redux/notificationTestData.js | 5 ++ src/redux/store.d.ts | 17 +++--- 8 files changed, 168 insertions(+), 29 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index 47bd59ec2..2f1f6c09c 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -18,26 +18,23 @@ import { Title, } from '@patternfly/react-core'; import { useDispatch, useSelector } from 'react-redux'; -import { toggleNotificationsDrawer } from '../../redux/actions'; -import FilterIcon from '@patternfly/react-icons/dist/dynamic/icons/filter-icon'; -import BellSlashIcon from '@patternfly/react-icons/dist/dynamic/icons/bell-slash-icon'; -import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/dynamic/icons/external-link-square-alt-icon'; +import { + toggleNotificationsDrawer, + markAllNotificationsAsRead, + markAllNotificationsAsUnread, +} from '../../redux/actions'; +import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; +import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; +import BellSlashIcon from '@patternfly/react-icons/dist/esm/icons/bell-slash-icon'; +import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; import { ReduxState } from '../../redux/store'; import NotificationItem from './NotificationItem'; +import { MARK_ALL_NOTIFICATION_AS_READ } from '../../redux/action-types'; export type DrawerPanelProps = { innerRef: React.Ref; }; -const dropdownItems = [ - Mark visible as read, - Mark visible as unread, - , - View event log, - Configure notifications settings, - Manage my notification preferences, -]; - const EmptyNotifications = () => ( @@ -74,6 +71,38 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { setIsFilterDropdownOpen(isOpen); }; + // const onMarkAllAsRead = () => { + // console.log('MARKING ALL AS READ'); + + // notifications.map((notification, index) => { + // if(!notification.read){ + // notification.read = true; // TODO: Switch to redux state changes + // } + // }); + // } + + const onMarkAllAsRead = () => { + console.log('MARKING ALL AS READ'); + dispatch({ type: MARK_ALL_NOTIFICATION_AS_READ }); + }; + + const onMarkAllAsUnread = () => { + notifications.map((notification, index) => { + if(notification.read){ + notification.read = false; + } + }); + } + + const dropdownItems = [ + Mark visible as read, + Mark visible as unread, + , + View event log, + Configure notifications settings, + Manage my notification preferences, + ]; + const filterDropdownItems = () => { const uniqueSources = new Set(notifications.map(notification => notification.source)); return Array.from(uniqueSources).map((source, index) => ( diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 7f13ef081..09bdd0792 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -11,12 +11,16 @@ import { NotificationDrawerListItemBody, NotificationDrawerListItemHeader, } from '@patternfly/react-core'; +import { useDispatch } from 'react-redux'; import { Notifications } from '../../redux/store'; +import { + MARK_NOTIFICATION_AS_READ, + MARK_NOTIFICATION_AS_UNREAD, +} from '../../redux/action-types'; -// TODO: Switch from local state to redux management. Needed for "mark all visible" functionality from the panel. const NotificationItem = ({ notification }: { notification: Notifications["data"][0] }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); - const [isNotificationRead, setIsNotificationRead] = useState(false); + const dispatch = useDispatch(); console.log('This is my current notification item: ', notification); const onDropdownToggle = (isOpen: boolean) => { @@ -24,11 +28,14 @@ const NotificationItem = ({ notification }: { notification: Notifications["data" } const onCheckboxToggle = () => { - setIsNotificationRead(!isNotificationRead); - } + if(!notification.read) + dispatch({ type: MARK_NOTIFICATION_AS_READ, payload: notification.id }); + else + dispatch({ type: MARK_NOTIFICATION_AS_UNREAD, payload: notification.id }); + }; const dropdownItems = [ - {`Mark as ${!isNotificationRead ? 'read' : 'unread'}`}, + {`Mark as ${!notification.read ? 'read' : 'unread'}`}, ] return ( @@ -36,14 +43,14 @@ const NotificationItem = ({ notification }: { notification: Notifications["data" ({ export const toggleNotificationsDrawer = () => ({ type: actionTypes.TOGGLE_NOTIFICATIONS_DRAWER, }); + +export const markNotificationAsRead = (id: number) => ({ + type: actionTypes.MARK_NOTIFICATION_AS_READ, + payload: id, +}); + +export const markNotificationAsUnread = (id: number) => ({ + type: actionTypes.MARK_NOTIFICATION_AS_UNREAD, + payload: id, +}); + +export const markAllNotificationsAsRead = () => ({ + type: actionTypes.MARK_ALL_NOTIFICATION_AS_READ, +}); + +export const markAllNotificationsAsUnread = () => ({ + type: actionTypes.MARK_ALL_NOTIFICATION_AS_READ, +}); + + + diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 7e5d32172..113b265bd 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -4,8 +4,14 @@ import { REQUESTS_COUNT, REQUESTS_DATA } from '../utils/consts'; import { ChromeModule, NavItem, Navigation } from '../@types/types'; import { ITLess, generateRoutesList, highlightItems, isBeta, levelArray } from '../utils/common'; import { ThreeScaleError } from '../utils/responseInterceptors'; -import { AccessRequest, ChromeState } from './store'; +import { AccessRequest, ChromeState, Notifications, NotificationData } from './store'; import { testData } from './notificationTestData'; +import { + MARK_NOTIFICATION_AS_READ, + MARK_NOTIFICATION_AS_UNREAD, + MARK_ALL_NOTIFICATION_AS_READ, + MARK_ALL_NOTIFICATION_AS_UNREAD, +} from './action-types'; export function contextSwitcherBannerReducer(state: ChromeState): ChromeState { return { @@ -344,3 +350,56 @@ export function toggleNotificationsReducer(state: ChromeState) { }, }; } + +export function notificationsReducer(state: ChromeState, action: { type: string; payload?: number }): ChromeState { + console.log('Looking at our state bfore updating: ', state.notifications?.data); + switch(action.type) { + case MARK_NOTIFICATION_AS_READ: + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification: NotificationData) => + notification.id === action.payload ? { ...notification, read: true } : notification + ), + }, + }; + case MARK_NOTIFICATION_AS_UNREAD: + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification: NotificationData) => + notification.id === action.payload ? { ...notification, read: false } : notification + ), + }, + }; + case MARK_ALL_NOTIFICATION_AS_READ: + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification) => ( + { ...notification, read: true } + )), + }, + }; + case MARK_ALL_NOTIFICATION_AS_UNREAD: + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification) => ( + { ...notification, read: false } + )), + }, + }; + default: + return state; + }; +}; + diff --git a/src/redux/index.ts b/src/redux/index.ts index d3c058adc..93d4e7e1d 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -15,6 +15,7 @@ import { loginReducer, markAccessRequestRequestReducer, markActiveProduct, + notificationsReducer, onPageAction, onPageObjectId, onRegisterModule, @@ -59,6 +60,10 @@ import { LOAD_NAVIGATION_LANDING_PAGE, MARK_ACTIVE_PRODUCT, MARK_REQUEST_NOTIFICATION_SEEN, + MARK_NOTIFICATION_AS_READ, + MARK_NOTIFICATION_AS_UNREAD, + MARK_ALL_NOTIFICATION_AS_READ, + MARK_ALL_NOTIFICATION_AS_UNREAD, POPULATE_QUICKSTARTS_CATALOG, REGISTER_MODULE, SET_GATEWAY_ERROR, @@ -100,6 +105,10 @@ const reducers = { [SET_GATEWAY_ERROR]: setGatewayError, [CLEAR_QUICKSTARTS]: clearQuickstartsReducer, [TOGGLE_NOTIFICATIONS_DRAWER]: toggleNotificationsReducer, + [MARK_NOTIFICATION_AS_READ]: notificationsReducer, + [MARK_NOTIFICATION_AS_UNREAD]: notificationsReducer, + [MARK_ALL_NOTIFICATION_AS_READ]: notificationsReducer, + [MARK_ALL_NOTIFICATION_AS_UNREAD]: notificationsReducer, }; const globalFilter = { @@ -154,7 +163,7 @@ export default function (): { modules: {}, scalprumConfig: {}, moduleRoutes: [], - notifications: { + notifications: { //TODO: Figure out why this is the difference from chromeReducers data: [], isExpanded: false, count: 0, diff --git a/src/redux/notificationTestData.js b/src/redux/notificationTestData.js index 84d06b1f6..c5b97d149 100644 --- a/src/redux/notificationTestData.js +++ b/src/redux/notificationTestData.js @@ -1,5 +1,6 @@ export const testData = [ { + id: 1, title: 'TEST NOTIFICATION', description: 'This is a notification test bro, bet, no cap, on god.', read: false, @@ -7,6 +8,7 @@ export const testData = [ created: new Date().toString(), }, { + id: 2, title: 'PRUEBA DE NOTIFICACIONES', description: 'This is a notification test bro, bet, no cap, on god.', read: false, @@ -14,6 +16,7 @@ export const testData = [ created: new Date().toString(), }, { + id: 3, title: 'NOTIFICATION TEST', description: 'This is a notification test bro, bet, no cap, on god.', read: false, @@ -21,6 +24,7 @@ export const testData = [ created: new Date().toString(), }, { + id: 4, title: 'ALERT', description: 'This is a notification test bro, bet, no cap, on god.', read: false, @@ -28,6 +32,7 @@ export const testData = [ created: new Date().toString(), }, { + id: 5, title: 'HONEY I BLEW UP THE BACKEND AGAIN', description: 'This is a notification test bro, bet, no cap, on god.', read: false, diff --git a/src/redux/store.d.ts b/src/redux/store.d.ts index 7235d9110..a72375108 100644 --- a/src/redux/store.d.ts +++ b/src/redux/store.d.ts @@ -11,15 +11,18 @@ export type InternalNavigation = { export type AccessRequest = { request_id: string; created: string; seen: boolean }; +export type NotificationData = { + id: number; + title: string; + description: string; + read: boolean; + source: string; + created: string; +}; + export type Notifications = { isExpanded: boolean; - data: Array<{ - title: string; - description: string; - read: boolean; - source: string; - created: string; - }>; + data: NotificationData[]; count: number; }; From 6c900fd35d2afcaa3c5924a7d90d01c692e6f26b Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 26 Jul 2023 13:15:41 -0400 Subject: [PATCH 04/20] Adding source filtering functionality. --- .../DrawerPanelContent.tsx | 113 ++++++++++-------- .../NotificationsDrawer/NotificationItem.tsx | 52 +++----- src/hooks/useChromeServiceEvents.ts | 2 +- src/layouts/DefaultLayout.tsx | 2 +- src/redux/actions.ts | 3 - src/redux/chromeReducers.ts | 27 ++--- src/redux/index.ts | 9 +- src/redux/notificationTestData.js | 2 +- 8 files changed, 100 insertions(+), 110 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index 2f1f6c09c..23ca0e52a 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -1,35 +1,30 @@ import React, { useState } from 'react'; import { Button, + Dropdown, + DropdownItem, + DropdownPosition, + DropdownSeparator, + DropdownToggle, EmptyState, EmptyStateBody, EmptyStateIcon, KebabToggle, - Dropdown, - DropdownToggle, - DropdownItem, - DropdownSeparator, - DropdownPosition, NotificationDrawer, - NotificationDrawerList, NotificationDrawerBody, NotificationDrawerHeader, + NotificationDrawerList, Text, Title, } from '@patternfly/react-core'; import { useDispatch, useSelector } from 'react-redux'; -import { - toggleNotificationsDrawer, - markAllNotificationsAsRead, - markAllNotificationsAsUnread, -} from '../../redux/actions'; +import { toggleNotificationsDrawer } from '../../redux/actions'; import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; -import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import BellSlashIcon from '@patternfly/react-icons/dist/esm/icons/bell-slash-icon'; import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; -import { ReduxState } from '../../redux/store'; +import { NotificationData, ReduxState } from '../../redux/store'; import NotificationItem from './NotificationItem'; -import { MARK_ALL_NOTIFICATION_AS_READ } from '../../redux/action-types'; +import { MARK_ALL_NOTIFICATION_AS_READ, MARK_ALL_NOTIFICATION_AS_UNREAD } from '../../redux/action-types'; export type DrawerPanelProps = { innerRef: React.Ref; @@ -60,9 +55,15 @@ const EmptyNotifications = () => ( const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); + const [filteredNotifications, setFilteredNotifications] = useState([]); const dispatch = useDispatch(); const notifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data || []); + const onNotificationsDrawerClose = () => { + setFilteredNotifications([]); + dispatch(toggleNotificationsDrawer()); + }; + const onDropdownToggle = (isOpen: boolean) => { setIsDropdownOpen(isOpen); }; @@ -71,57 +72,73 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { setIsFilterDropdownOpen(isOpen); }; - // const onMarkAllAsRead = () => { - // console.log('MARKING ALL AS READ'); - - // notifications.map((notification, index) => { - // if(!notification.read){ - // notification.read = true; // TODO: Switch to redux state changes - // } - // }); - // } - const onMarkAllAsRead = () => { - console.log('MARKING ALL AS READ'); dispatch({ type: MARK_ALL_NOTIFICATION_AS_READ }); + onDropdownToggle(false); }; const onMarkAllAsUnread = () => { - notifications.map((notification, index) => { - if(notification.read){ - notification.read = false; - } - }); - } + dispatch({ type: MARK_ALL_NOTIFICATION_AS_UNREAD }); + onDropdownToggle(false); + }; + + const onFilterSelect = (chosenFilter: string) => { + setFilteredNotifications(notifications.filter((notification) => notification.source === chosenFilter)); + onFilterDropdownToggle(false); + }; const dropdownItems = [ - Mark visible as read, - Mark visible as unread, + + Mark visible as read + , + + Mark visible as unread + , , - View event log, - Configure notifications settings, - Manage my notification preferences, + + View event log + , + + Configure notificatio settings + , + + Manage my notification preferences + , ]; const filterDropdownItems = () => { - const uniqueSources = new Set(notifications.map(notification => notification.source)); + const uniqueSources = new Set(notifications.map((notification) => notification.source)); return Array.from(uniqueSources).map((source, index) => ( - {source} - )); + onFilterSelect(source)}> + {source} + + )); + }; + + const renderNotifications = () => { + if (notifications.length === 0) { + return ; + } + + if (filteredNotifications?.length > 0) { + return filteredNotifications?.map((notification, index) => ); + } else { + return notifications.map((notification, index) => ); + } }; - + return ( - dispatch(toggleNotificationsDrawer())}> - onNotificationsDrawerClose()}> + + } isOpen={isFilterDropdownOpen} dropdownItems={filterDropdownItems()} - id='filter-dropdown' + id="filter-dropdown" aria-label="Notifications filter" isPlain /> @@ -134,12 +151,8 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { id="notification-dropdown" /> - - - {notifications.length === 0 ? : notifications.map((notification, index) => ( - - ))} - + + {renderNotifications()} ); diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 09bdd0792..422ea1b90 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -1,11 +1,11 @@ import React, { useState } from 'react'; import { - Label, + Checkbox, Dropdown, DropdownItem, DropdownPosition, - Checkbox, KebabToggle, + Label, NotificationDrawerList, NotificationDrawerListItem, NotificationDrawerListItemBody, @@ -13,48 +13,30 @@ import { } from '@patternfly/react-core'; import { useDispatch } from 'react-redux'; import { Notifications } from '../../redux/store'; -import { - MARK_NOTIFICATION_AS_READ, - MARK_NOTIFICATION_AS_UNREAD, -} from '../../redux/action-types'; +import { MARK_NOTIFICATION_AS_READ, MARK_NOTIFICATION_AS_UNREAD } from '../../redux/action-types'; -const NotificationItem = ({ notification }: { notification: Notifications["data"][0] }) => { +const NotificationItem = ({ notification }: { notification: Notifications['data'][0] }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dispatch = useDispatch(); console.log('This is my current notification item: ', notification); const onDropdownToggle = (isOpen: boolean) => { setIsDropdownOpen(isOpen); - } + }; const onCheckboxToggle = () => { - if(!notification.read) - dispatch({ type: MARK_NOTIFICATION_AS_READ, payload: notification.id }); - else - dispatch({ type: MARK_NOTIFICATION_AS_UNREAD, payload: notification.id }); + if (!notification.read) dispatch({ type: MARK_NOTIFICATION_AS_READ, payload: notification.id }); + else dispatch({ type: MARK_NOTIFICATION_AS_UNREAD, payload: notification.id }); }; - const dropdownItems = [ - {`Mark as ${!notification.read ? 'read' : 'unread'}`}, - ] + const dropdownItems = [{`Mark as ${!notification.read ? 'read' : 'unread'}`}]; return ( - - - + + + } @@ -64,11 +46,13 @@ const NotificationItem = ({ notification }: { notification: Notifications["data" id="notification-dropdown" /> - {/* TODO: Modify timestamp to only show correct "x minutes ago" */} - - - {notification.description} - + {/* TODO: Modify timestamp to only show correct "x minutes ago" */} + + + {notification.description} + diff --git a/src/hooks/useChromeServiceEvents.ts b/src/hooks/useChromeServiceEvents.ts index 68d268480..af145fb47 100644 --- a/src/hooks/useChromeServiceEvents.ts +++ b/src/hooks/useChromeServiceEvents.ts @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; -import { useFlag } from '@unleash/proxy-client-react'; +// import { useFlag } from '@unleash/proxy-client-react'; import { getEncodedToken, setCookie } from '../jwt/jwt'; diff --git a/src/layouts/DefaultLayout.tsx b/src/layouts/DefaultLayout.tsx index 91502d10f..bfa314af5 100644 --- a/src/layouts/DefaultLayout.tsx +++ b/src/layouts/DefaultLayout.tsx @@ -26,7 +26,7 @@ import { ReduxState } from '../redux/store'; import useNavigation from '../utils/useNavigation'; import { NavigationProps } from '../components/Navigation'; import { getUrl } from '../hooks/useBundle'; -import { useFlag } from '@unleash/proxy-client-react'; +// import { useFlag } from '@unleash/proxy-client-react'; type ShieldedRootProps = { hideNav?: boolean; diff --git a/src/redux/actions.ts b/src/redux/actions.ts index ced22ac08..a7670a77f 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -224,6 +224,3 @@ export const markAllNotificationsAsRead = () => ({ export const markAllNotificationsAsUnread = () => ({ type: actionTypes.MARK_ALL_NOTIFICATION_AS_READ, }); - - - diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 113b265bd..26005cdf6 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -4,13 +4,13 @@ import { REQUESTS_COUNT, REQUESTS_DATA } from '../utils/consts'; import { ChromeModule, NavItem, Navigation } from '../@types/types'; import { ITLess, generateRoutesList, highlightItems, isBeta, levelArray } from '../utils/common'; import { ThreeScaleError } from '../utils/responseInterceptors'; -import { AccessRequest, ChromeState, Notifications, NotificationData } from './store'; +import { AccessRequest, ChromeState, NotificationData } from './store'; import { testData } from './notificationTestData'; -import { - MARK_NOTIFICATION_AS_READ, - MARK_NOTIFICATION_AS_UNREAD, +import { MARK_ALL_NOTIFICATION_AS_READ, MARK_ALL_NOTIFICATION_AS_UNREAD, + MARK_NOTIFICATION_AS_READ, + MARK_NOTIFICATION_AS_UNREAD, } from './action-types'; export function contextSwitcherBannerReducer(state: ChromeState): ChromeState { @@ -353,14 +353,14 @@ export function toggleNotificationsReducer(state: ChromeState) { export function notificationsReducer(state: ChromeState, action: { type: string; payload?: number }): ChromeState { console.log('Looking at our state bfore updating: ', state.notifications?.data); - switch(action.type) { + switch (action.type) { case MARK_NOTIFICATION_AS_READ: return { ...state, notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => + data: (state.notifications?.data || []).map((notification: NotificationData) => notification.id === action.payload ? { ...notification, read: true } : notification ), }, @@ -371,7 +371,7 @@ export function notificationsReducer(state: ChromeState, action: { type: string; notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => + data: (state.notifications?.data || []).map((notification: NotificationData) => notification.id === action.payload ? { ...notification, read: false } : notification ), }, @@ -382,9 +382,7 @@ export function notificationsReducer(state: ChromeState, action: { type: string; notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification) => ( - { ...notification, read: true } - )), + data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: true })), }, }; case MARK_ALL_NOTIFICATION_AS_UNREAD: @@ -393,13 +391,10 @@ export function notificationsReducer(state: ChromeState, action: { type: string; notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification) => ( - { ...notification, read: false } - )), + data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: false })), }, }; default: return state; - }; -}; - + } +} diff --git a/src/redux/index.ts b/src/redux/index.ts index 93d4e7e1d..598847830 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -59,11 +59,11 @@ import { LOAD_MODULES_SCHEMA, LOAD_NAVIGATION_LANDING_PAGE, MARK_ACTIVE_PRODUCT, - MARK_REQUEST_NOTIFICATION_SEEN, - MARK_NOTIFICATION_AS_READ, - MARK_NOTIFICATION_AS_UNREAD, MARK_ALL_NOTIFICATION_AS_READ, MARK_ALL_NOTIFICATION_AS_UNREAD, + MARK_NOTIFICATION_AS_READ, + MARK_NOTIFICATION_AS_UNREAD, + MARK_REQUEST_NOTIFICATION_SEEN, POPULATE_QUICKSTARTS_CATALOG, REGISTER_MODULE, SET_GATEWAY_ERROR, @@ -163,7 +163,8 @@ export default function (): { modules: {}, scalprumConfig: {}, moduleRoutes: [], - notifications: { //TODO: Figure out why this is the difference from chromeReducers + notifications: { + //TODO: Figure out why this is the difference from chromeReducers data: [], isExpanded: false, count: 0, diff --git a/src/redux/notificationTestData.js b/src/redux/notificationTestData.js index c5b97d149..efb4ceacf 100644 --- a/src/redux/notificationTestData.js +++ b/src/redux/notificationTestData.js @@ -39,4 +39,4 @@ export const testData = [ source: 'OpenShift', created: new Date().toString(), }, -] +]; From a0e0e01166a950580b5fddb0fc6932593b30c8ae Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 9 Aug 2023 15:51:34 -0400 Subject: [PATCH 05/20] Refactoring reducers/actions and other parts of code. --- .../DrawerPanelContent.tsx | 18 +--- .../NotificationsDrawer/NotificationItem.tsx | 7 +- src/redux/action-types.ts | 9 +- src/redux/chromeReducers.ts | 101 +++++++++--------- src/redux/index.ts | 17 +-- 5 files changed, 69 insertions(+), 83 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index 23ca0e52a..ec35af861 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -64,27 +64,19 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { dispatch(toggleNotificationsDrawer()); }; - const onDropdownToggle = (isOpen: boolean) => { - setIsDropdownOpen(isOpen); - }; - - const onFilterDropdownToggle = (isOpen: boolean) => { - setIsFilterDropdownOpen(isOpen); - }; - const onMarkAllAsRead = () => { dispatch({ type: MARK_ALL_NOTIFICATION_AS_READ }); - onDropdownToggle(false); + setIsDropdownOpen(false); }; const onMarkAllAsUnread = () => { dispatch({ type: MARK_ALL_NOTIFICATION_AS_UNREAD }); - onDropdownToggle(false); + setIsDropdownOpen(false); }; const onFilterSelect = (chosenFilter: string) => { setFilteredNotifications(notifications.filter((notification) => notification.source === chosenFilter)); - onFilterDropdownToggle(false); + setIsFilterDropdownOpen(false); }; const dropdownItems = [ @@ -132,7 +124,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { onNotificationsDrawerClose()}> + setIsFilterDropdownOpen(!isFilterDropdownOpen)} id="filter-toggle"> } @@ -144,7 +136,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { /> } + toggle={ setIsDropdownOpen(!isDropdownOpen)} id="kebab-toggle" />} isOpen={isDropdownOpen} isPlain dropdownItems={dropdownItems} diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 422ea1b90..f1a39f556 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -13,20 +13,19 @@ import { } from '@patternfly/react-core'; import { useDispatch } from 'react-redux'; import { Notifications } from '../../redux/store'; -import { MARK_NOTIFICATION_AS_READ, MARK_NOTIFICATION_AS_UNREAD } from '../../redux/action-types'; +import { markNotificationAsRead, markNotificationAsUnread } from '../../redux/actions'; const NotificationItem = ({ notification }: { notification: Notifications['data'][0] }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dispatch = useDispatch(); - console.log('This is my current notification item: ', notification); const onDropdownToggle = (isOpen: boolean) => { setIsDropdownOpen(isOpen); }; const onCheckboxToggle = () => { - if (!notification.read) dispatch({ type: MARK_NOTIFICATION_AS_READ, payload: notification.id }); - else dispatch({ type: MARK_NOTIFICATION_AS_UNREAD, payload: notification.id }); + if (!notification.read) dispatch(markNotificationAsRead(notification.id)); + else dispatch(markNotificationAsUnread(notification.id)); }; const dropdownItems = [{`Mark as ${!notification.read ? 'read' : 'unread'}`}]; diff --git a/src/redux/action-types.ts b/src/redux/action-types.ts index 68307356e..1e2dfee27 100644 --- a/src/redux/action-types.ts +++ b/src/redux/action-types.ts @@ -42,8 +42,7 @@ export const SET_GATEWAY_ERROR = '@@chrome/set-gateway-error'; export const TOGGLE_NOTIFICATIONS_DRAWER = '@@chrome/toggle-notifications-drawer'; -// TODO: Verify that this format is acceptable -export const MARK_NOTIFICATION_AS_READ = 'MARK_NOTIFICATION_AS_READ'; -export const MARK_NOTIFICATION_AS_UNREAD = 'MARK_NOTIFICATION_AS_UNREAD'; -export const MARK_ALL_NOTIFICATION_AS_READ = 'MARK_ALL_NOTIFICATION_AS_READ'; -export const MARK_ALL_NOTIFICATION_AS_UNREAD = 'MARK_ALL_NOTIFICATION_AS_UNREAD'; +export const MARK_NOTIFICATION_AS_READ = '@@chrome/mark-notification-as-read'; +export const MARK_NOTIFICATION_AS_UNREAD = '@@chrome/mark-notification-as-unread'; +export const MARK_ALL_NOTIFICATION_AS_READ = '@@chrome/mark-all-notification-as-read'; +export const MARK_ALL_NOTIFICATION_AS_UNREAD = '@@chrome/mark-all-notification-as-unread'; diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 26005cdf6..5aed0eb26 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -6,12 +6,6 @@ import { ITLess, generateRoutesList, highlightItems, isBeta, levelArray } from ' import { ThreeScaleError } from '../utils/responseInterceptors'; import { AccessRequest, ChromeState, NotificationData } from './store'; import { testData } from './notificationTestData'; -import { - MARK_ALL_NOTIFICATION_AS_READ, - MARK_ALL_NOTIFICATION_AS_UNREAD, - MARK_NOTIFICATION_AS_READ, - MARK_NOTIFICATION_AS_UNREAD, -} from './action-types'; export function contextSwitcherBannerReducer(state: ChromeState): ChromeState { return { @@ -345,56 +339,57 @@ export function toggleNotificationsReducer(state: ChromeState) { ...state, notifications: { ...state.notifications, - data: testData, // This is where we would pass the data instead of simply hardcoding it in + data: testData, isExpanded: !state.notifications?.isExpanded, }, }; } -export function notificationsReducer(state: ChromeState, action: { type: string; payload?: number }): ChromeState { - console.log('Looking at our state bfore updating: ', state.notifications?.data); - switch (action.type) { - case MARK_NOTIFICATION_AS_READ: - return { - ...state, - notifications: { - isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => - notification.id === action.payload ? { ...notification, read: true } : notification - ), - }, - }; - case MARK_NOTIFICATION_AS_UNREAD: - return { - ...state, - notifications: { - isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => - notification.id === action.payload ? { ...notification, read: false } : notification - ), - }, - }; - case MARK_ALL_NOTIFICATION_AS_READ: - return { - ...state, - notifications: { - isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: true })), - }, - }; - case MARK_ALL_NOTIFICATION_AS_UNREAD: - return { - ...state, - notifications: { - isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: false })), - }, - }; - default: - return state; - } +export function markNotificationAsRead(state: ChromeState, payload: Number): ChromeState { + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification: NotificationData) => + notification.id === payload ? { ...notification, read: true } : notification + ), + }, + }; +} + +export function markNotificationAsUnread(state: ChromeState, payload: Number): ChromeState { + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification: NotificationData) => + notification.id === payload ? { ...notification, read: false } : notification + ), + }, + }; } + +export function markAllNotificationsAsRead(state: ChromeState): ChromeState { + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: true })), + }, + }; +} + +export function markAllNotificationsAsUnread(state: ChromeState): ChromeState { + return { + ...state, + notifications: { + isExpanded: state.notifications?.isExpanded || false, + count: state.notifications?.count || 0, + data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: false })), + }, + }; +} + diff --git a/src/redux/index.ts b/src/redux/index.ts index 598847830..9f490f5a3 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -15,7 +15,10 @@ import { loginReducer, markAccessRequestRequestReducer, markActiveProduct, - notificationsReducer, + markNotificationAsRead, + markNotificationAsUnread, + markAllNotificationsAsRead, + markAllNotificationsAsUnread, onPageAction, onPageObjectId, onRegisterModule, @@ -105,10 +108,10 @@ const reducers = { [SET_GATEWAY_ERROR]: setGatewayError, [CLEAR_QUICKSTARTS]: clearQuickstartsReducer, [TOGGLE_NOTIFICATIONS_DRAWER]: toggleNotificationsReducer, - [MARK_NOTIFICATION_AS_READ]: notificationsReducer, - [MARK_NOTIFICATION_AS_UNREAD]: notificationsReducer, - [MARK_ALL_NOTIFICATION_AS_READ]: notificationsReducer, - [MARK_ALL_NOTIFICATION_AS_UNREAD]: notificationsReducer, + [MARK_NOTIFICATION_AS_READ]: markNotificationAsRead, + [MARK_NOTIFICATION_AS_UNREAD]: markNotificationAsUnread, + [MARK_ALL_NOTIFICATION_AS_READ]: markAllNotificationsAsRead, + [MARK_ALL_NOTIFICATION_AS_UNREAD]: markAllNotificationsAsUnread, }; const globalFilter = { @@ -145,7 +148,6 @@ export default function (): { chrome: (state: ChromeState, action: AnyAction) => ChromeState; globalFilter: (state: GlobalFilterState, action: AnyAction) => ChromeState; } { - // const chromeInitialState = JSON.parse(localStorage.getItem('chrome')) || {}; return { chrome: ( @@ -164,8 +166,7 @@ export default function (): { scalprumConfig: {}, moduleRoutes: [], notifications: { - //TODO: Figure out why this is the difference from chromeReducers - data: [], + data: [], isExpanded: false, count: 0, }, From aa4c288b3b17de8f3a3af9ee21b586a06e036cc0 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Tue, 15 Aug 2023 21:33:07 -0400 Subject: [PATCH 06/20] Switching to dispatchion action creator --- .../NotificationsDrawer/DrawerPanelContent.tsx | 13 +++++++++---- .../NotificationsDrawer/NotificationItem.tsx | 12 ++++-------- src/redux/chromeReducers.ts | 10 ++++++++-- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index ec35af861..d09d31843 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -24,7 +24,7 @@ import BellSlashIcon from '@patternfly/react-icons/dist/esm/icons/bell-slash-ico import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; import { NotificationData, ReduxState } from '../../redux/store'; import NotificationItem from './NotificationItem'; -import { MARK_ALL_NOTIFICATION_AS_READ, MARK_ALL_NOTIFICATION_AS_UNREAD } from '../../redux/action-types'; +import { markAllNotificationsAsRead, markAllNotificationsAsUnread } from '../../redux/actions'; export type DrawerPanelProps = { innerRef: React.Ref; @@ -65,12 +65,12 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { }; const onMarkAllAsRead = () => { - dispatch({ type: MARK_ALL_NOTIFICATION_AS_READ }); + dispatch(markAllNotificationsAsRead()); setIsDropdownOpen(false); }; const onMarkAllAsUnread = () => { - dispatch({ type: MARK_ALL_NOTIFICATION_AS_UNREAD }); + dispatch(markAllNotificationsAsUnread()); setIsDropdownOpen(false); }; @@ -91,7 +91,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { View event log , - Configure notificatio settings + Configure notification settings , Manage my notification preferences @@ -117,6 +117,11 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { } else { return notifications.map((notification, index) => ); } + + return (filteredNotifications?.length > 0 + ? filteredNotifications + : notifications + ).map((notification, index) => ); }; return ( diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index f1a39f556..c5dd0b712 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -12,17 +12,13 @@ import { NotificationDrawerListItemHeader, } from '@patternfly/react-core'; import { useDispatch } from 'react-redux'; -import { Notifications } from '../../redux/store'; +import { NotificationData } from '../../redux/store'; import { markNotificationAsRead, markNotificationAsUnread } from '../../redux/actions'; -const NotificationItem = ({ notification }: { notification: Notifications['data'][0] }) => { +const NotificationItem = ({ notification }: { notification: NotificationData }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dispatch = useDispatch(); - const onDropdownToggle = (isOpen: boolean) => { - setIsDropdownOpen(isOpen); - }; - const onCheckboxToggle = () => { if (!notification.read) dispatch(markNotificationAsRead(notification.id)); else dispatch(markNotificationAsUnread(notification.id)); @@ -35,10 +31,10 @@ const NotificationItem = ({ notification }: { notification: Notifications['data' - + onCheckboxToggle()} id="read-checkbox" name="read-checkbox" /> } + toggle={ setIsDropdownOpen(!isDropdownOpen)} id="kebab-toggle" />} isOpen={isDropdownOpen} isPlain dropdownItems={dropdownItems} diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 5aed0eb26..d66beb5f3 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -351,8 +351,14 @@ export function markNotificationAsRead(state: ChromeState, payload: Number): Chr notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => - notification.id === payload ? { ...notification, read: true } : notification + data: (state.notifications?.data || []).map((notification: NotificationData) => { + console.log('VIENDO EL notification en state: ', notification); + console.log('VIENDO mi payload: ', payload); + if (notification.id === payload){ + console.log('PUTA MADRE', notification); + } + return notification.id === payload ? { ...notification, read: true } : notification + } ), }, }; From dda00c86b1639d3f796831bd1c98cd9e7956a77f Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Thu, 17 Aug 2023 11:20:32 -0400 Subject: [PATCH 07/20] Fixed marking as read issue. --- src/redux/actions.ts | 2 +- src/redux/chromeReducers.ts | 14 ++++---------- src/redux/notificationTestData.js | 12 ++++++------ 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/redux/actions.ts b/src/redux/actions.ts index a7670a77f..fb46f0101 100644 --- a/src/redux/actions.ts +++ b/src/redux/actions.ts @@ -222,5 +222,5 @@ export const markAllNotificationsAsRead = () => ({ }); export const markAllNotificationsAsUnread = () => ({ - type: actionTypes.MARK_ALL_NOTIFICATION_AS_READ, + type: actionTypes.MARK_ALL_NOTIFICATION_AS_UNREAD, }); diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index d66beb5f3..fba86043c 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -345,26 +345,20 @@ export function toggleNotificationsReducer(state: ChromeState) { }; } -export function markNotificationAsRead(state: ChromeState, payload: Number): ChromeState { +export function markNotificationAsRead(state: ChromeState, { payload }: { payload: Number }): ChromeState { return { ...state, notifications: { isExpanded: state.notifications?.isExpanded || false, count: state.notifications?.count || 0, - data: (state.notifications?.data || []).map((notification: NotificationData) => { - console.log('VIENDO EL notification en state: ', notification); - console.log('VIENDO mi payload: ', payload); - if (notification.id === payload){ - console.log('PUTA MADRE', notification); - } - return notification.id === payload ? { ...notification, read: true } : notification - } + data: (state.notifications?.data || []).map((notification: NotificationData) => + notification.id === payload ? { ...notification, read: true } : notification ), }, }; } -export function markNotificationAsUnread(state: ChromeState, payload: Number): ChromeState { +export function markNotificationAsUnread(state: ChromeState, { payload }: { payload: Number }): ChromeState { return { ...state, notifications: { diff --git a/src/redux/notificationTestData.js b/src/redux/notificationTestData.js index efb4ceacf..7588f5fd0 100644 --- a/src/redux/notificationTestData.js +++ b/src/redux/notificationTestData.js @@ -2,15 +2,15 @@ export const testData = [ { id: 1, title: 'TEST NOTIFICATION', - description: 'This is a notification test bro, bet, no cap, on god.', + description: 'This is a notification test.', read: false, - source: 'ur mom lolz', + source: 'ROSA', created: new Date().toString(), }, { id: 2, title: 'PRUEBA DE NOTIFICACIONES', - description: 'This is a notification test bro, bet, no cap, on god.', + description: 'This is a notification test.', read: false, source: 'RHEL', created: new Date().toString(), @@ -18,7 +18,7 @@ export const testData = [ { id: 3, title: 'NOTIFICATION TEST', - description: 'This is a notification test bro, bet, no cap, on god.', + description: 'This is a notification test.', read: false, source: 'RBAC', created: new Date().toString(), @@ -26,7 +26,7 @@ export const testData = [ { id: 4, title: 'ALERT', - description: 'This is a notification test bro, bet, no cap, on god.', + description: 'This is a notification test.', read: false, source: 'REMEDIATIONS', created: new Date().toString(), @@ -34,7 +34,7 @@ export const testData = [ { id: 5, title: 'HONEY I BLEW UP THE BACKEND AGAIN', - description: 'This is a notification test bro, bet, no cap, on god.', + description: 'This is a notification test.', read: false, source: 'OpenShift', created: new Date().toString(), From e3bfecf98029285e1298615959af4d76798db1f7 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Fri, 18 Aug 2023 15:39:26 -0400 Subject: [PATCH 08/20] Refactoring of notification filter menu and functionality. Matching mockups now. --- src/components/Header/Tools.tsx | 3 +- .../DrawerPanelContent.tsx | 67 +++++++++++++------ .../NotificationsDrawer/NotificationItem.tsx | 5 +- src/hooks/useChromeServiceEvents.ts | 6 +- src/layouts/DefaultLayout.tsx | 6 +- src/redux/chromeReducers.ts | 8 +-- src/redux/index.ts | 7 +- src/redux/notificationTestData.js | 42 ------------ 8 files changed, 61 insertions(+), 83 deletions(-) delete mode 100644 src/redux/notificationTestData.js diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index 00ab9f3af..a32f3b415 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -96,7 +96,8 @@ const Tools = () => { const enableAuthDropdownOption = useFlag('platform.chrome.dropdown.authfactor'); const previewEnabled = useFlag('platform.chrome.preview'); - const isNotificationsEnabled = true; // useFlag('platform.chrome.notifications-drawer'); + // const isNotificationsEnabled = true; + const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); /* list out the items for the settings menu */ const settingsMenuDropdownItems = [ diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index d09d31843..da6ae7551 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -1,15 +1,18 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { + Badge, Button, + Checkbox, Dropdown, + DropdownGroup, DropdownItem, DropdownPosition, DropdownSeparator, - DropdownToggle, EmptyState, EmptyStateBody, EmptyStateIcon, KebabToggle, + MenuToggle, NotificationDrawer, NotificationDrawerBody, NotificationDrawerHeader, @@ -55,12 +58,22 @@ const EmptyNotifications = () => ( const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); + const [activeFilters, setActiveFilters] = useState([]); const [filteredNotifications, setFilteredNotifications] = useState([]); const dispatch = useDispatch(); const notifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data || []); + useEffect(() => { + const modifiedNotifications = (activeFilters || []).reduce( + (acc: NotificationData[], chosenFilter: string) => [...acc, ...notifications.filter(({ source }) => source === chosenFilter)], + [] + ); + + setFilteredNotifications(modifiedNotifications); + }, [activeFilters]); + const onNotificationsDrawerClose = () => { - setFilteredNotifications([]); + setActiveFilters([]); dispatch(toggleNotificationsDrawer()); }; @@ -75,8 +88,9 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { }; const onFilterSelect = (chosenFilter: string) => { - setFilteredNotifications(notifications.filter((notification) => notification.source === chosenFilter)); - setIsFilterDropdownOpen(false); + activeFilters.includes(chosenFilter) + ? setActiveFilters(activeFilters.filter((filter) => filter !== chosenFilter)) + : setActiveFilters([...activeFilters, chosenFilter]); }; const dropdownItems = [ @@ -99,12 +113,24 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { ]; const filterDropdownItems = () => { - const uniqueSources = new Set(notifications.map((notification) => notification.source)); - return Array.from(uniqueSources).map((source, index) => ( - onFilterSelect(source)}> - {source} - - )); + const sources = notifications.reduce((acc: string[], { source }) => (acc.includes(source) ? acc : [...acc, source]), []); + + return [ + + {sources.map((source, index) => ( + onFilterSelect(source)}> + + {source} + + ))} + + setActiveFilters([])}> + + + , + ]; }; const renderNotifications = () => { @@ -112,26 +138,23 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { return ; } - if (filteredNotifications?.length > 0) { - return filteredNotifications?.map((notification, index) => ); - } else { - return notifications.map((notification, index) => ); - } + // TODO: Add sorting by timestamps as primary sort, then by read/unread. + const sortedNotifications = (filteredNotifications?.length > 0 ? filteredNotifications : notifications).sort( + (currentNotification, nextNotification) => (currentNotification.read === nextNotification.read ? 0 : currentNotification.read ? 1 : -1) + ); - return (filteredNotifications?.length > 0 - ? filteredNotifications - : notifications - ).map((notification, index) => ); + return sortedNotifications.map((notification, index) => ); }; return ( onNotificationsDrawerClose()}> + {activeFilters.length > 0 && {activeFilters.length}} setIsFilterDropdownOpen(!isFilterDropdownOpen)} id="filter-toggle"> + setIsFilterDropdownOpen(!isFilterDropdownOpen)} id="filter-toggle" isFullWidth variant="plainText"> - + } isOpen={isFilterDropdownOpen} dropdownItems={filterDropdownItems()} diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index c5dd0b712..bf63e3e43 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -20,8 +20,7 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) const dispatch = useDispatch(); const onCheckboxToggle = () => { - if (!notification.read) dispatch(markNotificationAsRead(notification.id)); - else dispatch(markNotificationAsUnread(notification.id)); + dispatch(!notification.read ? markNotificationAsRead(notification.id) : markNotificationAsUnread(notification.id)); }; const dropdownItems = [{`Mark as ${!notification.read ? 'read' : 'unread'}`}]; @@ -42,7 +41,7 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) /> {/* TODO: Modify timestamp to only show correct "x minutes ago" */} - + diff --git a/src/hooks/useChromeServiceEvents.ts b/src/hooks/useChromeServiceEvents.ts index af145fb47..be4669e28 100644 --- a/src/hooks/useChromeServiceEvents.ts +++ b/src/hooks/useChromeServiceEvents.ts @@ -1,6 +1,6 @@ import { useEffect, useMemo, useRef } from 'react'; import { useDispatch } from 'react-redux'; -// import { useFlag } from '@unleash/proxy-client-react'; +import { useFlag } from '@unleash/proxy-client-react'; import { getEncodedToken, setCookie } from '../jwt/jwt'; @@ -32,8 +32,8 @@ function isGenericEvent(event: unknown): event is GenericEvent { const useChromeServiceEvents = () => { const connection = useRef(); const dispatch = useDispatch(); - // const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); - const isNotificationsEnabled = true; + const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + // const isNotificationsEnabled = true; const handlerMap: { [key in EventTypes]: (payload: Payload) => void } = useMemo( () => ({ diff --git a/src/layouts/DefaultLayout.tsx b/src/layouts/DefaultLayout.tsx index bfa314af5..a962b5b7a 100644 --- a/src/layouts/DefaultLayout.tsx +++ b/src/layouts/DefaultLayout.tsx @@ -26,7 +26,7 @@ import { ReduxState } from '../redux/store'; import useNavigation from '../utils/useNavigation'; import { NavigationProps } from '../components/Navigation'; import { getUrl } from '../hooks/useBundle'; -// import { useFlag } from '@unleash/proxy-client-react'; +import { useFlag } from '@unleash/proxy-client-react'; type ShieldedRootProps = { hideNav?: boolean; @@ -54,8 +54,8 @@ const DefaultLayout: React.FC = ({ hasBanner, selectedAccoun const tabbableElement = drawerPanelRef.current?.querySelector('a, button') as HTMLAnchorElement | HTMLButtonElement; tabbableElement.focus(); }; - // const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); - const isNotificationsEnabled = true; + const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + return ( ChromeState; globalFilter: (state: GlobalFilterState, action: AnyAction) => ChromeState; } { - return { chrome: ( state = { @@ -166,7 +165,7 @@ export default function (): { scalprumConfig: {}, moduleRoutes: [], notifications: { - data: [], + data: [], isExpanded: false, count: 0, }, diff --git a/src/redux/notificationTestData.js b/src/redux/notificationTestData.js deleted file mode 100644 index 7588f5fd0..000000000 --- a/src/redux/notificationTestData.js +++ /dev/null @@ -1,42 +0,0 @@ -export const testData = [ - { - id: 1, - title: 'TEST NOTIFICATION', - description: 'This is a notification test.', - read: false, - source: 'ROSA', - created: new Date().toString(), - }, - { - id: 2, - title: 'PRUEBA DE NOTIFICACIONES', - description: 'This is a notification test.', - read: false, - source: 'RHEL', - created: new Date().toString(), - }, - { - id: 3, - title: 'NOTIFICATION TEST', - description: 'This is a notification test.', - read: false, - source: 'RBAC', - created: new Date().toString(), - }, - { - id: 4, - title: 'ALERT', - description: 'This is a notification test.', - read: false, - source: 'REMEDIATIONS', - created: new Date().toString(), - }, - { - id: 5, - title: 'HONEY I BLEW UP THE BACKEND AGAIN', - description: 'This is a notification test.', - read: false, - source: 'OpenShift', - created: new Date().toString(), - }, -]; From a4b8c21b3ca2c018b805bab97a4f17777d684f23 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Tue, 22 Aug 2023 17:41:49 -0400 Subject: [PATCH 09/20] Fixing PF5 related issues after rebasing --- package-lock.json | 2 +- package.json | 2 +- src/components/Header/Tools.tsx | 4 +- .../DrawerPanelContent.tsx | 61 +++++++++++++------ .../NotificationsDrawer/NotificationItem.tsx | 25 ++++++-- src/hooks/useChromeServiceEvents.ts | 4 +- src/layouts/DefaultLayout.tsx | 3 +- src/redux/chromeReducers.ts | 3 +- testData.js | 27 ++++++++ 9 files changed, 100 insertions(+), 31 deletions(-) create mode 100644 testData.js diff --git a/package-lock.json b/package-lock.json index b8d347a75..9d2d2121c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@data-driven-forms/react-form-renderer": "^3.20.2", "@formatjs/cli": "4.8.4", "@openshift/dynamic-plugin-sdk": "^3.0.0", - "@patternfly/patternfly": "^5.0.0", + "@patternfly/patternfly": "^5.0.2", "@patternfly/quickstarts": "^5.0.0", "@patternfly/react-charts": "^7.0.0", "@patternfly/react-core": "^5.0.0", diff --git a/package.json b/package.json index 63cc8429a..6c99f00e1 100644 --- a/package.json +++ b/package.json @@ -128,7 +128,7 @@ "@data-driven-forms/react-form-renderer": "^3.20.2", "@formatjs/cli": "4.8.4", "@openshift/dynamic-plugin-sdk": "^3.0.0", - "@patternfly/patternfly": "^5.0.0", + "@patternfly/patternfly": "^5.0.2", "@patternfly/quickstarts": "^5.0.0", "@patternfly/react-charts": "^7.0.0", "@patternfly/react-core": "^5.0.0", diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index a32f3b415..c2f14587d 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -96,8 +96,8 @@ const Tools = () => { const enableAuthDropdownOption = useFlag('platform.chrome.dropdown.authfactor'); const previewEnabled = useFlag('platform.chrome.preview'); - // const isNotificationsEnabled = true; - const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); + const isNotificationsEnabled = true; + // const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); /* list out the items for the settings menu */ const settingsMenuDropdownItems = [ diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index da6ae7551..e5880104a 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -3,16 +3,16 @@ import { Badge, Button, Checkbox, + Divider, Dropdown, DropdownGroup, DropdownItem, - DropdownPosition, - DropdownSeparator, EmptyState, EmptyStateBody, EmptyStateIcon, - KebabToggle, + Icon, MenuToggle, + MenuToggleElement, NotificationDrawer, NotificationDrawerBody, NotificationDrawerHeader, @@ -25,6 +25,7 @@ import { toggleNotificationsDrawer } from '../../redux/actions'; import FilterIcon from '@patternfly/react-icons/dist/esm/icons/filter-icon'; import BellSlashIcon from '@patternfly/react-icons/dist/esm/icons/bell-slash-icon'; import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/esm/icons/external-link-square-alt-icon'; +import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import { NotificationData, ReduxState } from '../../redux/store'; import NotificationItem from './NotificationItem'; import { markAllNotificationsAsRead, markAllNotificationsAsUnread } from '../../redux/actions'; @@ -100,14 +101,23 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { Mark visible as unread , - , - + , + + + + View event log , - + + + + Configure notification settings , - + + + + Manage my notification preferences , ]; @@ -123,7 +133,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { {source} ))} - + setActiveFilters([])}> @@ -163,26 +175,44 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { {activeFilters.length > 0 && {activeFilters.length}} ) => ( - setIsFilterDropdownOpen(!isFilterDropdownOpen)} id="filter-toggle" isFullWidth variant="plain"> + setIsFilterDropdownOpen(!isFilterDropdownOpen)} + id="notifications-filter-toggle" + isFullWidth + variant="plain" + > )} isOpen={isFilterDropdownOpen} - id="filter-dropdown" + onOpenChange={setIsFilterDropdownOpen} + popperProps={{ + position: PopoverPosition.right, + }} + id="notifications-filter-dropdown" aria-label="Notifications filter" - isPlain > {filterDropdownItems()} ) => ( - setIsDropdownOpen(!isDropdownOpen)} variant="plain" id="kebab-toggle" isFullWidth> + setIsDropdownOpen(!isDropdownOpen)} + variant="plain" + id="notifications-actions-toggle" + isFullWidth + > )} isOpen={isDropdownOpen} - isPlain - id="notification-dropdown" + onOpenChange={setIsDropdownOpen} + popperProps={{ + position: PopoverPosition.right, + }} + id="notifications-actions-dropdown" > {dropdownItems.map((dropdownItem) => dropdownItem)} diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 35ba4f6ee..180a7411e 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -1,16 +1,15 @@ import React, { useState } from 'react'; import { - Checkbox, - Dropdown, - DropdownItem, - Label, - MenuToggle, - MenuToggleElement, NotificationDrawerList, NotificationDrawerListItem, NotificationDrawerListItemBody, NotificationDrawerListItemHeader, -} from '@patternfly/react-core'; +} from '@patternfly/react-core/dist/dynamic/components/NotificationDrawer'; +import { PopoverPosition } from '@patternfly/react-core/dist/dynamic/components/Popover'; +import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbox'; +import { Label } from '@patternfly/react-core/dist/dynamic/components/Label'; +import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; +import { Dropdown, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; @@ -40,7 +39,7 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) ref={toggleRef} aria-label="Notification actions dropdown" onClick={() => setIsDropdownOpen(!isDropdownOpen)} - id="kebab-toggle" + id="notification-item-toggle" isExpanded={isDropdownOpen} variant="plain" > @@ -48,10 +47,13 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) )} isOpen={isDropdownOpen} - isPlain - id="notification-dropdown" + onOpenChange={setIsDropdownOpen} + popperProps={{ + position: PopoverPosition.right, + }} + id="notification-item-dropdown" > - {dropdownItems.map((dropDownItem) => dropDownItem)} + {dropdownItems} }> diff --git a/testData.js b/src/components/NotificationsDrawer/notificationDrawerUtils.js similarity index 55% rename from testData.js rename to src/components/NotificationsDrawer/notificationDrawerUtils.js index 24ddcfacb..8a681c110 100644 --- a/testData.js +++ b/src/components/NotificationsDrawer/notificationDrawerUtils.js @@ -4,7 +4,7 @@ export const testData = [ title: 'Test Notification 1', description: 'Testing of notification', read: false, - source: 'RHEL', // the origin of the message + source: 'rhel', // the origin of the message created: '2023-08-18T12:00:00Z', }, { @@ -12,7 +12,7 @@ export const testData = [ title: 'Test Notification 2', description: 'Testing of notification', read: false, - source: 'RBAC', // the origin of the message + source: 'ansible', // the origin of the message created: '2023-08-18T12:05:00Z', }, { @@ -20,8 +20,14 @@ export const testData = [ title: 'Test Notification 3', description: 'Testin of notification', read: false, - source: 'SATURN', // the origin of the message + source: 'openshift', // the origin of the message created: '2023-08-18T12:10:00Z', }, ]; +export const filterConfig = [ + { title: 'Console', value: 'console' }, + { title: 'OpenShift', value: 'openshift' }, + { title: 'Red Hat Enterprise Linux', value: 'rhel' }, + { title: 'Ansible Automation Platform', value: 'ansible' }, +]; diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 0b48a4a3b..6323a404c 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -5,7 +5,6 @@ import { ChromeModule, NavItem, Navigation } from '../@types/types'; import { ITLess, generateRoutesList, highlightItems, isBeta, levelArray } from '../utils/common'; import { ThreeScaleError } from '../utils/responseInterceptors'; import { AccessRequest, ChromeState, NotificationData } from './store'; -import { testData } from '../../testData'; export function contextSwitcherBannerReducer(state: ChromeState): ChromeState { return { @@ -339,7 +338,7 @@ export function toggleNotificationsReducer(state: ChromeState) { ...state, notifications: { ...state.notifications, - data: testData, + data: [], isExpanded: !state.notifications?.isExpanded, }, }; From bcaa731f9c0ab5ca03816664e237393678885b5a Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Fri, 1 Sep 2023 22:14:21 -0400 Subject: [PATCH 13/20] Further code refactoring, and adding navigation links. --- package-lock.json | 2 +- .../DrawerPanelContent.tsx | 24 +++++++++-------- .../NotificationsDrawer/NotificationItem.tsx | 8 +++--- .../notificationDrawerUtils.js | 27 ------------------- 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d2d2121c..b8d347a75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@data-driven-forms/react-form-renderer": "^3.20.2", "@formatjs/cli": "4.8.4", "@openshift/dynamic-plugin-sdk": "^3.0.0", - "@patternfly/patternfly": "^5.0.2", + "@patternfly/patternfly": "^5.0.0", "@patternfly/quickstarts": "^5.0.0", "@patternfly/react-charts": "^7.0.0", "@patternfly/react-core": "^5.0.0", diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index e9f9ff591..c8de8ffc1 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -23,7 +23,8 @@ import BellSlashIcon from '@patternfly/react-icons/dist/dynamic/icons/bell-slash import ExternalLinkSquareAltIcon from '@patternfly/react-icons/dist/dynamic/icons/external-link-square-alt-icon'; import ExternalLinkAltIcon from '@patternfly/react-icons/dist/dynamic/icons/external-link-alt-icon'; import EllipsisVIcon from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; -import { orderBy } from 'lodash'; +import orderBy from 'lodash/orderBy'; +import { useNavigate } from 'react-router-dom'; import { NotificationData, ReduxState } from '../../redux/store'; import NotificationItem from './NotificationItem'; import { markAllNotificationsAsRead, markAllNotificationsAsUnread, toggleNotificationsDrawer } from '../../redux/actions'; @@ -60,6 +61,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { const [isFilterDropdownOpen, setIsFilterDropdownOpen] = useState(false); const [activeFilters, setActiveFilters] = useState([]); const [filteredNotifications, setFilteredNotifications] = useState([]); + const navigate = useNavigate(); const dispatch = useDispatch(); const notifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data || []); const isOrgAdmin = useSelector(({ chrome }: ReduxState) => chrome.user?.identity.user?.is_org_admin); @@ -95,14 +97,14 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { }; const dropdownItems = [ - onMarkAllAsRead()} isDisabled={notifications.length === 0}> + Mark visible as read , - onMarkAllAsUnread()} isDisabled={notifications.length === 0}> + Mark visible as unread , , - + navigate('/settings/notifications/eventlog')}> View event log @@ -113,7 +115,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { , isOrgAdmin && ( - + navigate('/settings/notifications/configure-events')}> Configure notification settings @@ -124,7 +126,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { ), - + navigate('/settings/notifications/user-preferences')}> Manage my notification preferences @@ -139,9 +141,9 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { const filterDropdownItems = () => { return [ - {filterConfig.map((source, index) => ( - onFilterSelect(source.value)}> - + {filterConfig.map((source) => ( + onFilterSelect(source.value)}> + {source.title} ))} @@ -166,12 +168,12 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { ['asc', 'asc'] ); - return sortedNotifications.map((notification, index) => ); + return sortedNotifications.map((notification) => ); }; return ( - onNotificationsDrawerClose()}> + {activeFilters.length > 0 && {activeFilters.length}} ) => ( diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 180a7411e..1f6123d00 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -10,7 +10,7 @@ import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbo import { Label } from '@patternfly/react-core/dist/dynamic/components/Label'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; import { Dropdown, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; -import EllipsisVIcon from '@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon'; +import EllipsisVIcon from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; import { NotificationData } from '../../redux/store'; @@ -25,14 +25,12 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) setIsDropdownOpen(false); }; - const dropdownItems = [{`Mark as ${!notification.read ? 'read' : 'unread'}`}]; - return ( - onCheckboxToggle()} id="read-checkbox" name="read-checkbox" /> + ) => ( - {dropdownItems} + {`Mark as ${!notification.read ? 'read' : 'unread'}`} }> diff --git a/src/components/NotificationsDrawer/notificationDrawerUtils.js b/src/components/NotificationsDrawer/notificationDrawerUtils.js index 8a681c110..cb7279e35 100644 --- a/src/components/NotificationsDrawer/notificationDrawerUtils.js +++ b/src/components/NotificationsDrawer/notificationDrawerUtils.js @@ -1,30 +1,3 @@ -export const testData = [ - { - id: 1, - title: 'Test Notification 1', - description: 'Testing of notification', - read: false, - source: 'rhel', // the origin of the message - created: '2023-08-18T12:00:00Z', - }, - { - id: 2, - title: 'Test Notification 2', - description: 'Testing of notification', - read: false, - source: 'ansible', // the origin of the message - created: '2023-08-18T12:05:00Z', - }, - { - id: 3, - title: 'Test Notification 3', - description: 'Testin of notification', - read: false, - source: 'openshift', // the origin of the message - created: '2023-08-18T12:10:00Z', - }, -]; - export const filterConfig = [ { title: 'Console', value: 'console' }, { title: 'OpenShift', value: 'openshift' }, From 9362cd6704aa324d793051919e3806f43f4df9a2 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Mon, 4 Sep 2023 17:52:51 -0400 Subject: [PATCH 14/20] Changing data loading, adding dropdown options and refactoring --- .../DrawerPanelContent.tsx | 3 +- .../NotificationsDrawer/NotificationItem.tsx | 10 +++++- .../notificationDrawerUtils.js | 6 ---- .../notificationDrawerUtils.ts | 33 +++++++++++++++++++ src/redux/chromeReducers.ts | 8 ++--- 5 files changed, 47 insertions(+), 13 deletions(-) delete mode 100644 src/components/NotificationsDrawer/notificationDrawerUtils.js create mode 100644 src/components/NotificationsDrawer/notificationDrawerUtils.ts diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index c8de8ffc1..9ceaad651 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -173,7 +173,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { return ( - + {activeFilters.length > 0 && {activeFilters.length}} ) => ( @@ -181,7 +181,6 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { ref={toggleRef} onClick={() => setIsFilterDropdownOpen(!isFilterDropdownOpen)} id="notifications-filter-toggle" - isFullWidth variant="plain" > diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 1f6123d00..333ba1025 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -15,16 +15,24 @@ import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; import { NotificationData } from '../../redux/store'; import { markNotificationAsRead, markNotificationAsUnread } from '../../redux/actions'; +import { useNavigate } from 'react-router-dom'; const NotificationItem = ({ notification }: { notification: NotificationData }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dispatch = useDispatch(); + const navigate = useNavigate(); const onCheckboxToggle = () => { dispatch(!notification.read ? markNotificationAsRead(notification.id) : markNotificationAsUnread(notification.id)); setIsDropdownOpen(false); }; + const notificationDropdownItems = [ + {`Mark as ${!notification.read ? 'read' : 'unread'}`}, + navigate('settings/notifications/configure-events')}> + Manage this event + , + ]; return ( @@ -51,7 +59,7 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) }} id="notification-item-dropdown" > - {`Mark as ${!notification.read ? 'read' : 'unread'}`} + {notificationDropdownItems} }> diff --git a/src/components/NotificationsDrawer/notificationDrawerUtils.js b/src/components/NotificationsDrawer/notificationDrawerUtils.js deleted file mode 100644 index cb7279e35..000000000 --- a/src/components/NotificationsDrawer/notificationDrawerUtils.js +++ /dev/null @@ -1,6 +0,0 @@ -export const filterConfig = [ - { title: 'Console', value: 'console' }, - { title: 'OpenShift', value: 'openshift' }, - { title: 'Red Hat Enterprise Linux', value: 'rhel' }, - { title: 'Ansible Automation Platform', value: 'ansible' }, -]; diff --git a/src/components/NotificationsDrawer/notificationDrawerUtils.ts b/src/components/NotificationsDrawer/notificationDrawerUtils.ts new file mode 100644 index 000000000..8a681c110 --- /dev/null +++ b/src/components/NotificationsDrawer/notificationDrawerUtils.ts @@ -0,0 +1,33 @@ +export const testData = [ + { + id: 1, + title: 'Test Notification 1', + description: 'Testing of notification', + read: false, + source: 'rhel', // the origin of the message + created: '2023-08-18T12:00:00Z', + }, + { + id: 2, + title: 'Test Notification 2', + description: 'Testing of notification', + read: false, + source: 'ansible', // the origin of the message + created: '2023-08-18T12:05:00Z', + }, + { + id: 3, + title: 'Test Notification 3', + description: 'Testin of notification', + read: false, + source: 'openshift', // the origin of the message + created: '2023-08-18T12:10:00Z', + }, +]; + +export const filterConfig = [ + { title: 'Console', value: 'console' }, + { title: 'OpenShift', value: 'openshift' }, + { title: 'Red Hat Enterprise Linux', value: 'rhel' }, + { title: 'Ansible Automation Platform', value: 'ansible' }, +]; diff --git a/src/redux/chromeReducers.ts b/src/redux/chromeReducers.ts index 6323a404c..83d149abf 100644 --- a/src/redux/chromeReducers.ts +++ b/src/redux/chromeReducers.ts @@ -338,7 +338,7 @@ export function toggleNotificationsReducer(state: ChromeState) { ...state, notifications: { ...state.notifications, - data: [], + data: state.notifications?.data || [], isExpanded: !state.notifications?.isExpanded, }, }; @@ -349,7 +349,7 @@ export function markNotificationAsRead(state: ChromeState, { payload }: { payloa ...state, notifications: { isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, + count: state.notifications?.data?.length || 0, data: (state.notifications?.data || []).map((notification: NotificationData) => notification.id === payload ? { ...notification, read: true } : notification ), @@ -362,7 +362,7 @@ export function markNotificationAsUnread(state: ChromeState, { payload }: { payl ...state, notifications: { isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, + count: state.notifications?.data?.length || 0, data: (state.notifications?.data || []).map((notification: NotificationData) => notification.id === payload ? { ...notification, read: false } : notification ), @@ -386,7 +386,7 @@ export function markAllNotificationsAsUnread(state: ChromeState): ChromeState { ...state, notifications: { isExpanded: state.notifications?.isExpanded || false, - count: state.notifications?.count || 0, + count: state.notifications?.data?.length || 0, data: (state.notifications?.data || []).map((notification) => ({ ...notification, read: false })), }, }; From 58dcec060848267484f7e8c95e6b059f3cc2f088 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Tue, 5 Sep 2023 09:09:40 -0400 Subject: [PATCH 15/20] Updating utils after refactoring. --- .../notificationDrawerUtils.ts | 27 ------------------- src/layouts/DefaultLayout.tsx | 1 - 2 files changed, 28 deletions(-) diff --git a/src/components/NotificationsDrawer/notificationDrawerUtils.ts b/src/components/NotificationsDrawer/notificationDrawerUtils.ts index 8a681c110..cb7279e35 100644 --- a/src/components/NotificationsDrawer/notificationDrawerUtils.ts +++ b/src/components/NotificationsDrawer/notificationDrawerUtils.ts @@ -1,30 +1,3 @@ -export const testData = [ - { - id: 1, - title: 'Test Notification 1', - description: 'Testing of notification', - read: false, - source: 'rhel', // the origin of the message - created: '2023-08-18T12:00:00Z', - }, - { - id: 2, - title: 'Test Notification 2', - description: 'Testing of notification', - read: false, - source: 'ansible', // the origin of the message - created: '2023-08-18T12:05:00Z', - }, - { - id: 3, - title: 'Test Notification 3', - description: 'Testin of notification', - read: false, - source: 'openshift', // the origin of the message - created: '2023-08-18T12:10:00Z', - }, -]; - export const filterConfig = [ { title: 'Console', value: 'console' }, { title: 'OpenShift', value: 'openshift' }, diff --git a/src/layouts/DefaultLayout.tsx b/src/layouts/DefaultLayout.tsx index a962b5b7a..4995c33c0 100644 --- a/src/layouts/DefaultLayout.tsx +++ b/src/layouts/DefaultLayout.tsx @@ -55,7 +55,6 @@ const DefaultLayout: React.FC = ({ hasBanner, selectedAccoun tabbableElement.focus(); }; const isNotificationsEnabled = useFlag('platform.chrome.notifications-drawer'); - return ( Date: Tue, 5 Sep 2023 14:03:58 -0400 Subject: [PATCH 16/20] Closing drawer on navigation, and fixed small filtering bug. --- package-lock.json | 1 + .../DrawerPanelContent.tsx | 49 ++++++++++--------- .../NotificationsDrawer/NotificationItem.tsx | 10 ++-- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index b8d347a75..e4e5807a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "insights-chrome", "version": "0.0.0", "hasInstallScript": true, "license": "MIT", diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index 9ceaad651..a57cadb13 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -4,7 +4,7 @@ import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { Badge } from '@patternfly/react-core/dist/dynamic/components/Badge'; import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbox'; import { Flex, FlexItem } from '@patternfly/react-core/dist/dynamic/layouts/Flex'; -import { Dropdown, DropdownGroup, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; +import { Dropdown, DropdownGroup, DropdownItem, DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; import { Divider } from '@patternfly/react-core/dist/dynamic/components/Divider'; import { Button } from '@patternfly/react-core/dist/dynamic/components/Button'; @@ -96,6 +96,11 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { : setActiveFilters([...activeFilters, chosenFilter]); }; + const onNavigateTo = (link: string) => { + navigate(link); + onNotificationsDrawerClose(); + }; + const dropdownItems = [ Mark visible as read @@ -104,7 +109,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { Mark visible as unread , , - navigate('/settings/notifications/eventlog')}> + onNavigateTo('/settings/notifications/eventlog')}> View event log @@ -115,7 +120,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { , isOrgAdmin && ( - navigate('/settings/notifications/configure-events')}> + onNavigateTo('/settings/notifications/configure-events')}> Configure notification settings @@ -126,7 +131,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { ), - navigate('/settings/notifications/user-preferences')}> + onNavigateTo('/settings/notifications/user-preferences')}> Manage my notification preferences @@ -141,18 +146,20 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { const filterDropdownItems = () => { return [ - {filterConfig.map((source) => ( - onFilterSelect(source.value)}> - - {source.title} + + {filterConfig.map((source) => ( + onFilterSelect(source.value)} isDisabled={notifications.length === 0}> + + {source.title} + + ))} + + setActiveFilters([])}> + - ))} - - setActiveFilters([])}> - - + , ]; }; @@ -162,13 +169,11 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { return ; } - const sortedNotifications = orderBy( - filteredNotifications?.length > 0 ? filteredNotifications : notifications, - ['read', 'created'], - ['asc', 'asc'] - ); + const sortedNotifications = orderBy(activeFilters?.length > 0 ? filteredNotifications : notifications, ['read', 'created'], ['asc', 'asc']); - return sortedNotifications.map((notification) => ); + return sortedNotifications.map((notification) => ( + + )); }; return ( @@ -215,7 +220,7 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { }} id="notifications-actions-dropdown" > - {dropdownItems.map((dropdownItem) => dropdownItem)} + {dropdownItems} diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 333ba1025..128745270 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -15,12 +15,14 @@ import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; import { NotificationData } from '../../redux/store'; import { markNotificationAsRead, markNotificationAsUnread } from '../../redux/actions'; -import { useNavigate } from 'react-router-dom'; -const NotificationItem = ({ notification }: { notification: NotificationData }) => { +interface NotificationItemProps { + notification: NotificationData; + onNavigateTo: (link: string) => void; +} +const NotificationItem: React.FC = ({ notification, onNavigateTo }) => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dispatch = useDispatch(); - const navigate = useNavigate(); const onCheckboxToggle = () => { dispatch(!notification.read ? markNotificationAsRead(notification.id) : markNotificationAsUnread(notification.id)); @@ -29,7 +31,7 @@ const NotificationItem = ({ notification }: { notification: NotificationData }) const notificationDropdownItems = [ {`Mark as ${!notification.read ? 'read' : 'unread'}`}, - navigate('settings/notifications/configure-events')}> + onNavigateTo('settings/notifications/configure-events')}> Manage this event , ]; From 793793451221844a79d7373394b128f173364653 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Tue, 5 Sep 2023 14:52:22 -0400 Subject: [PATCH 17/20] Adding notifications initial state to redux. --- src/redux/index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/redux/index.ts b/src/redux/index.ts index 73f8db188..587c27f92 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -140,6 +140,11 @@ export const chromeInitialState: ReduxState = { quickstarts: {}, }, moduleRoutes: [], + notifications: { + data: [], + isExpanded: false, + count: 0, + }, }, globalFilter: globalFilterDefaultState, }; From 41f50d5de2e93abab01a1c5a1035013a9a718fdb Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 6 Sep 2023 07:44:09 -0400 Subject: [PATCH 18/20] Adding dropdown wrapper and adjusting bell icon logic. --- src/components/Header/Tools.tsx | 2 +- src/components/NotificationsDrawer/NotificationItem.tsx | 4 ++-- src/redux/index.ts | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Header/Tools.tsx b/src/components/Header/Tools.tsx index 93d5b460c..5c0d70753 100644 --- a/src/components/Header/Tools.tsx +++ b/src/components/Header/Tools.tsx @@ -82,7 +82,7 @@ const Tools = () => { }); const { xs } = useWindowWidth(); const user = useSelector(({ chrome: { user } }: ReduxState) => user!); - const unreadNotifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data?.filter((isRead) => isRead) || []); + const unreadNotifications = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.data?.filter((isRead) => !isRead) || []); const isDrawerExpanded = useSelector(({ chrome: { notifications } }: ReduxState) => notifications?.isExpanded); const dispatch = useDispatch(); const libjwt = useContext(LibtJWTContext); diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 128745270..9709224c1 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -9,7 +9,7 @@ import { PopoverPosition } from '@patternfly/react-core/dist/dynamic/components/ import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbox'; import { Label } from '@patternfly/react-core/dist/dynamic/components/Label'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; -import { Dropdown, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; +import { Dropdown, DropdownList, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import EllipsisVIcon from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; @@ -61,7 +61,7 @@ const NotificationItem: React.FC = ({ notification, onNav }} id="notification-item-dropdown" > - {notificationDropdownItems} + {notificationDropdownItems} }> diff --git a/src/redux/index.ts b/src/redux/index.ts index 587c27f92..8b745ba48 100644 --- a/src/redux/index.ts +++ b/src/redux/index.ts @@ -153,6 +153,8 @@ export default function (): { chrome: (state: ChromeState, action: AnyAction) => ChromeState; globalFilter: (state: GlobalFilterState, action: AnyAction) => ChromeState; } { + // const chromeInitialState = JSON.parse(localStorage.getItem('chrome')) || {}; + return { chrome: ( state = { From 351ba962ef7acfddfe04d70da5ac60f9dd11e1ea Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 6 Sep 2023 07:46:20 -0400 Subject: [PATCH 19/20] Readjusting code line. --- src/components/NotificationsDrawer/NotificationItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NotificationsDrawer/NotificationItem.tsx b/src/components/NotificationsDrawer/NotificationItem.tsx index 9709224c1..dabf6fbd4 100644 --- a/src/components/NotificationsDrawer/NotificationItem.tsx +++ b/src/components/NotificationsDrawer/NotificationItem.tsx @@ -9,7 +9,7 @@ import { PopoverPosition } from '@patternfly/react-core/dist/dynamic/components/ import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbox'; import { Label } from '@patternfly/react-core/dist/dynamic/components/Label'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; -import { Dropdown, DropdownList, DropdownItem } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; +import { Dropdown, DropdownItem, DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import EllipsisVIcon from '@patternfly/react-icons/dist/dynamic/icons/ellipsis-v-icon'; import DateFormat from '@redhat-cloud-services/frontend-components/DateFormat'; import { useDispatch } from 'react-redux'; From e9d3834d5d6cae5e0957080615355ce936249e03 Mon Sep 17 00:00:00 2001 From: Alexander Rivera Date: Wed, 6 Sep 2023 14:00:44 -0400 Subject: [PATCH 20/20] Eliminating console errors. --- .../NotificationsDrawer/DrawerPanelContent.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/components/NotificationsDrawer/DrawerPanelContent.tsx b/src/components/NotificationsDrawer/DrawerPanelContent.tsx index a57cadb13..bdf629598 100644 --- a/src/components/NotificationsDrawer/DrawerPanelContent.tsx +++ b/src/components/NotificationsDrawer/DrawerPanelContent.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react'; import { PopoverPosition } from '@patternfly/react-core/dist/dynamic/components/Popover'; import { Icon } from '@patternfly/react-core/dist/dynamic/components/Icon'; import { Badge } from '@patternfly/react-core/dist/dynamic/components/Badge'; -import { Checkbox } from '@patternfly/react-core/dist/dynamic/components/Checkbox'; import { Flex, FlexItem } from '@patternfly/react-core/dist/dynamic/layouts/Flex'; import { Dropdown, DropdownGroup, DropdownItem, DropdownList } from '@patternfly/react-core/dist/dynamic/components/Dropdown'; import { MenuToggle, MenuToggleElement } from '@patternfly/react-core/dist/dynamic/components/MenuToggle'; @@ -148,16 +147,19 @@ const DrawerPanelBase = ({ innerRef }: DrawerPanelProps) => { {filterConfig.map((source) => ( - onFilterSelect(source.value)} isDisabled={notifications.length === 0}> - + onFilterSelect(source.value)} + isDisabled={notifications.length === 0} + isSelected={activeFilters.includes(source.value)} + hasCheckbox + > {source.title} ))} - setActiveFilters([])}> - + setActiveFilters([])}> + Reset filters ,