From 50e6453c2c304dfa37471b78cd5899c238f54572 Mon Sep 17 00:00:00 2001 From: Abishek Das Date: Thu, 26 Sep 2024 11:25:59 +0530 Subject: [PATCH] Enhance admin note visibility and improve accessibility for screen reader. - Added a visual indicator for admin notes to display when a note is present, without requiring the modal to be opened. - Standardized the size of all three admin quick action buttons for consistency. - Implemented screen reader messages to improve accessibility for the newly added features. --- .../components/admin_notes/notes_creator.jsx | 2 +- .../admin_notes/notes_modal_trigger.jsx | 59 +++++++++++++++++++ .../components/admin_notes/notes_panel.jsx | 28 +++++---- .../components/admin_notes/notes_row.jsx | 2 +- .../components/admin_notes/status_panel.jsx | 0 .../javascripts/components/common/list.jsx | 2 - .../overview/admin_quick_actions.jsx | 2 +- .../stylesheets/modules/_admin_note.styl | 28 ++++++++- app/assets/stylesheets/modules/_overview.styl | 5 ++ config/locales/en.yml | 2 + 10 files changed, 113 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/components/admin_notes/notes_modal_trigger.jsx delete mode 100644 app/assets/javascripts/components/admin_notes/status_panel.jsx diff --git a/app/assets/javascripts/components/admin_notes/notes_creator.jsx b/app/assets/javascripts/components/admin_notes/notes_creator.jsx index 6a116c0dd7..860f19d5db 100644 --- a/app/assets/javascripts/components/admin_notes/notes_creator.jsx +++ b/app/assets/javascripts/components/admin_notes/notes_creator.jsx @@ -49,7 +49,7 @@ const NotesCreator = ({ noteTitle, setTitle, noteText, setText }) => { {/* Aria-live region for screen reader announcements */} -
+
{liveMessage}
diff --git a/app/assets/javascripts/components/admin_notes/notes_modal_trigger.jsx b/app/assets/javascripts/components/admin_notes/notes_modal_trigger.jsx new file mode 100644 index 0000000000..324739c578 --- /dev/null +++ b/app/assets/javascripts/components/admin_notes/notes_modal_trigger.jsx @@ -0,0 +1,59 @@ +import React, { useState, useEffect } from 'react'; +import PropTypes from 'prop-types'; +import { Cookies } from 'react-cookie-consent'; + +const NotesModalTrigger = ({ setIsModalOpen, notesList, setNoteFetchTimestamp }) => { + const [notificationCount, setNotificationCount] = useState(0); + + const onClickAdminButton = () => { + setIsModalOpen('adminNotePanel'); + setNotificationCount(0); + setNoteFetchTimestamp(); + }; + + useEffect(() => { + // Retrieve the last fetch timestamp from the cookie + const userLastFetchTimestamp = Cookies.get('lastFetchAdminNoteTimestamp'); + + // Check if the cookie value is a valid number + const parsedTimestamp = isFinite(userLastFetchTimestamp) ? parseInt(userLastFetchTimestamp) : 0; + + // Calculate the count of new notes + const newAdminNoteCount = notesList.filter(note => new Date(note.updated_at) > new Date(parsedTimestamp)).length; + + // Update the notification count state + setNotificationCount(newAdminNoteCount); + }, [notesList]); + + // Accessible message for screen readers, indicating whether there are new admin notes or none available + const notesAriaLabel = !notesList.length ? I18n.t('notes.admin.aria_label.no_notes_available') + : I18n.t('notes.admin.aria_label.new_notes_message', { count: notificationCount, plural: notificationCount > 1 ? 's' : '' }); + + return ( +
+ + { + (notificationCount > 0) && ( +
+ {notificationCount} +
+ )} +
+ ); +}; + +// Define PropTypes +NotesModalTrigger.propTypes = { + setIsModalOpen: PropTypes.func.isRequired, // Expecting a function, required + setNoteFetchTimestamp: PropTypes.func.isRequired, + notesList: PropTypes.arrayOf(PropTypes.object).isRequired // Expecting an array of objects, required +}; + +export default NotesModalTrigger; diff --git a/app/assets/javascripts/components/admin_notes/notes_panel.jsx b/app/assets/javascripts/components/admin_notes/notes_panel.jsx index f126a6dcff..1a503b3dc9 100644 --- a/app/assets/javascripts/components/admin_notes/notes_panel.jsx +++ b/app/assets/javascripts/components/admin_notes/notes_panel.jsx @@ -1,8 +1,10 @@ import React, { useEffect, useState } from 'react'; import { useSelector, useDispatch } from 'react-redux'; +import { Cookies } from 'react-cookie-consent'; import { fetchAllAdminCourseNotes, createAdminCourseNote } from '../../actions/admin_course_notes_action'; import NotesList from './notes_list'; import NotesCreator from './notes_creator'; +import NotesModalTrigger from './notes_modal_trigger'; const NotesPanel = () => { // State variables for managing the modal and note creation @@ -23,6 +25,18 @@ const NotesPanel = () => { // Get the dispatch function from the Redux store const dispatch = useDispatch(); + // Updates the cookie timestamp to track when notes were last fetched or created. + const setNoteFetchTimestamp = () => { + // Set the current timestamp as a cookie when the user fetches notes or create notes + const currentTimestamp = Date.now(); + + // Set the expiration date to 10 years from now + const expires = new Date(); + expires.setFullYear(expires.getFullYear() + 10); + + Cookies.set('lastFetchAdminNoteTimestamp', currentTimestamp, { expires: expires }); + }; + // Fetch all course notes when the component mounts useEffect(() => { // Define a function to fetch the course notes @@ -76,6 +90,8 @@ const NotesPanel = () => { setText(''); setTitle(''); + // Set the cookie timestamp after note creation to prevent the admin from receiving redundant notifications for notes they’ve created + setNoteFetchTimestamp(); }; // Close the modal and deactivate note creation @@ -86,15 +102,7 @@ const NotesPanel = () => { // Conditionally render a button if modalType is null if (!isModalOpen) { - return ( - - ); + return (); } return ( @@ -163,7 +171,7 @@ const NotesPanel = () => { {/* Announcement for screen readers */} -
+
{liveMessage}
diff --git a/app/assets/javascripts/components/admin_notes/notes_row.jsx b/app/assets/javascripts/components/admin_notes/notes_row.jsx index 1414076a37..ebe116f5dd 100644 --- a/app/assets/javascripts/components/admin_notes/notes_row.jsx +++ b/app/assets/javascripts/components/admin_notes/notes_row.jsx @@ -197,7 +197,7 @@ const NotesRow = ({ notesList }) => { })} {/* Announcement for screen readers */} - +
{liveMessage} diff --git a/app/assets/javascripts/components/admin_notes/status_panel.jsx b/app/assets/javascripts/components/admin_notes/status_panel.jsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/app/assets/javascripts/components/common/list.jsx b/app/assets/javascripts/components/common/list.jsx index ffedc2934b..565c5b072a 100644 --- a/app/assets/javascripts/components/common/list.jsx +++ b/app/assets/javascripts/components/common/list.jsx @@ -112,10 +112,8 @@ const List = ({ if (elements.length === 0) { let emptyMessage; if (!loading) { - // eslint-disable-next-line let noneMessage = none_message; if (typeof noneMessage === 'undefined' || noneMessage === null) { - // eslint-disable-next-line noneMessage = I18n.t(`${table_key}.none`); } emptyMessage = {noneMessage}; diff --git a/app/assets/javascripts/components/overview/admin_quick_actions.jsx b/app/assets/javascripts/components/overview/admin_quick_actions.jsx index 3985a4452a..24a09f4a63 100644 --- a/app/assets/javascripts/components/overview/admin_quick_actions.jsx +++ b/app/assets/javascripts/components/overview/admin_quick_actions.jsx @@ -37,7 +37,7 @@ export const AdminQuickActions = ({ course, current_user, persistCourse, greetSt )}