diff --git a/lms/static/scripts/frontend_apps/components/dashboard/DashboardActivityFilters.tsx b/lms/static/scripts/frontend_apps/components/dashboard/DashboardActivityFilters.tsx index 5b6d32326b..06bd21c4c7 100644 --- a/lms/static/scripts/frontend_apps/components/dashboard/DashboardActivityFilters.tsx +++ b/lms/static/scripts/frontend_apps/components/dashboard/DashboardActivityFilters.tsx @@ -8,6 +8,7 @@ import { useMemo } from 'preact/hooks'; import type { AssignmentsResponse, CoursesResponse, + Student, StudentsResponse, } from '../../api-types'; import { useConfig } from '../../config'; @@ -23,6 +24,8 @@ export type DashboardActivityFiltersProps = { onClearSelection?: () => void; }; +type FiltersStudent = Student & { has_display_name: boolean }; + /** * Renders drop-downs to select courses, assignments and/or students, used to * filter dashboard activity metrics. @@ -58,8 +61,15 @@ export default function DashboardActivityFilters({ course_id: selectedCourseIds, public_id: dashboard.organization_public_id, }); - const studentsWithName = useMemo( - () => students.data?.students.filter(s => !!s.display_name), + const studentsWithFallbackName: FiltersStudent[] | undefined = useMemo( + () => + students.data?.students.map(({ display_name, ...s }) => ({ + ...s, + display_name: + display_name ?? + `Student name unavailable (ID: ${s.lms_id.substring(0, 5)})`, + has_display_name: !!display_name, + })), [students.data?.students], ); @@ -134,7 +144,7 @@ export default function DashboardActivityFilters({ ) : selectedStudentIds.length === 0 ? ( <>All students ) : selectedStudentIds.length === 1 ? ( - students.data?.students.find( + studentsWithFallbackName?.find( s => s.h_userid === selectedStudentIds[0], )?.display_name ) : ( @@ -144,9 +154,19 @@ export default function DashboardActivityFilters({ data-testid="students-select" > All students - {studentsWithName?.map(student => ( + {studentsWithFallbackName?.map(student => ( - {student.display_name} + + {student.display_name} + ))} diff --git a/lms/static/scripts/frontend_apps/components/dashboard/test/DashboardActivityFilters-test.js b/lms/static/scripts/frontend_apps/components/dashboard/test/DashboardActivityFilters-test.js index de843a702b..0b90c27b58 100644 --- a/lms/static/scripts/frontend_apps/components/dashboard/test/DashboardActivityFilters-test.js +++ b/lms/static/scripts/frontend_apps/components/dashboard/test/DashboardActivityFilters-test.js @@ -47,9 +47,9 @@ describe('DashboardActivityFilters', () => { const students = [ ...studentsWithName, { - lms_id: '3', + lms_id: '123456789', h_userid: 'acct:3@lms.hypothes.is', - display_name: '', // Student with an empty name won't be displayed + display_name: null, // Student with an empty name won't be displayed }, ]; @@ -178,9 +178,11 @@ describe('DashboardActivityFilters', () => { expectedOptions: [ 'All students', ...studentsWithName.map(s => s.display_name), + 'Student name unavailable (ID: 12345)', ], + expectedTitles: [undefined, undefined, undefined, 'User ID: 123456789'], }, - ].forEach(({ id, expectedOptions }) => { + ].forEach(({ id, expectedOptions, expectedTitles = [] }) => { it('renders corresponding options', () => { const wrapper = createComponent(); const select = getSelect(wrapper, id); @@ -189,6 +191,13 @@ describe('DashboardActivityFilters', () => { assert.equal(options.length, expectedOptions.length); options.forEach((option, index) => { assert.equal(option.text(), expectedOptions[index]); + + if (expectedTitles[index]) { + assert.equal( + option.find('[data-testid="option-content-wrapper"]').prop('title'), + expectedTitles[index], + ); + } }); }); }); @@ -302,7 +311,7 @@ describe('DashboardActivityFilters', () => { { props: { selectedAssignmentIds: [...assignments], - selectedStudentIds: [...studentsWithName], + selectedStudentIds: [...students], }, shouldRenderClearButton: false, },