-
-
Notifications
You must be signed in to change notification settings - Fork 219
UI/ux mentorship program update #2244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 12 commits
dc789e7
459b041
901d84e
386c531
b040ec1
0699761
04cddc5
93c1da7
ce06f83
c305655
0f68f13
2ee55cc
0773c70
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,9 @@ | ||
import { faUsers } from '@fortawesome/free-solid-svg-icons' | ||
import { fireEvent, screen, waitFor } from '@testing-library/react' | ||
import { screen } from '@testing-library/react' | ||
import { useRouter } from 'next/navigation' | ||
import { useSession } from 'next-auth/react' | ||
import React from 'react' | ||
import { render } from 'wrappers/testUtil' | ||
import type { ExtendedSession } from 'types/auth' | ||
import type { Module } from 'types/mentorship' | ||
import { ExperienceLevelEnum, ProgramStatusEnum } from 'types/mentorship' | ||
import SingleModuleCard from 'components/SingleModuleCard' | ||
|
@@ -111,16 +110,6 @@ const mockModule: Module = { | |
|
||
const mockAdmins = [{ login: 'admin1' }, { login: 'admin2' }] | ||
|
||
const mockSessionData: ExtendedSession = { | ||
user: { | ||
login: 'admin1', | ||
isLeader: true, | ||
email: '[email protected]', | ||
image: 'https://example.com/admin-avatar.jpg', | ||
}, | ||
expires: '2024-12-31T23:59:59Z', | ||
} | ||
|
||
describe('SingleModuleCard', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks() | ||
|
@@ -146,7 +135,6 @@ describe('SingleModuleCard', () => { | |
expect(screen.getByText('Test Module')).toBeInTheDocument() | ||
expect(screen.getByText('This is a test module description')).toBeInTheDocument() | ||
expect(screen.getByTestId('icon-users')).toBeInTheDocument() | ||
expect(screen.getByTestId('icon-ellipsis')).toBeInTheDocument() | ||
}) | ||
|
||
it('renders module details correctly', () => { | ||
|
@@ -183,128 +171,31 @@ describe('SingleModuleCard', () => { | |
}) | ||
}) | ||
|
||
describe('Dropdown Menu', () => { | ||
it('opens dropdown when ellipsis button is clicked', () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
}) | ||
|
||
it('closes dropdown when clicking outside', async () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
|
||
// Click outside the dropdown | ||
fireEvent.mouseDown(document.body) | ||
|
||
await waitFor(() => { | ||
expect(screen.queryByText('View Module')).not.toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
it('navigates to view module when View Module is clicked', () => { | ||
describe('Simplified Interface', () => { | ||
it('focuses on content display only', () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
const viewButton = screen.getByText('View Module') | ||
fireEvent.click(viewButton) | ||
|
||
expect(mockPush).toHaveBeenCalledWith('//modules/test-module') | ||
}) | ||
|
||
it('shows only View Module option for non-admin users', () => { | ||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
showEdit={true} | ||
accessLevel="user" | ||
admins={mockAdmins} | ||
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
// Should display core content | ||
expect(screen.getByText('Test Module')).toBeInTheDocument() | ||
expect(screen.getByText('This is a test module description')).toBeInTheDocument() | ||
expect(screen.getByText('Experience Level:')).toBeInTheDocument() | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
expect(screen.queryByText('Edit Module')).not.toBeInTheDocument() | ||
expect(screen.queryByText('Create Module')).not.toBeInTheDocument() | ||
// Should have clickable title for navigation | ||
const moduleLink = screen.getByTestId('module-link') | ||
expect(moduleLink).toHaveAttribute('href', '//modules/test-module') | ||
}) | ||
}) | ||
|
||
describe('Admin Functionality', () => { | ||
beforeEach(() => { | ||
mockUseSession.mockReturnValue({ | ||
data: mockSessionData, | ||
status: 'authenticated', | ||
update: jest.fn(), | ||
}) | ||
}) | ||
|
||
it('shows Edit Module option for admin users when showEdit is true', () => { | ||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
showEdit={true} | ||
accessLevel="admin" | ||
admins={mockAdmins} | ||
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
expect(screen.getByText('Edit Module')).toBeInTheDocument() | ||
expect(screen.getByText('Create Module')).toBeInTheDocument() | ||
}) | ||
|
||
it('does not show Edit Module option when showEdit is false', () => { | ||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
showEdit={false} | ||
accessLevel="admin" | ||
admins={mockAdmins} | ||
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
expect(screen.queryByText('Edit Module')).not.toBeInTheDocument() | ||
expect(screen.getByText('Create Module')).toBeInTheDocument() | ||
}) | ||
|
||
it('navigates to edit module when Edit Module is clicked', () => { | ||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
showEdit={true} | ||
accessLevel="admin" | ||
admins={mockAdmins} | ||
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
const editButton = screen.getByText('Edit Module') | ||
fireEvent.click(editButton) | ||
describe('Props Handling', () => { | ||
it('renders correctly with minimal props', () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
expect(mockPush).toHaveBeenCalledWith('//modules/test-module/edit') | ||
expect(screen.getByText('Test Module')).toBeInTheDocument() | ||
expect(screen.getByText('This is a test module description')).toBeInTheDocument() | ||
}) | ||
|
||
it('navigates to create module when Create Module is clicked', () => { | ||
it('ignores admin-related props since menu is removed', () => { | ||
// These props are now ignored but should not cause errors | ||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
|
@@ -314,13 +205,7 @@ describe('SingleModuleCard', () => { | |
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
const createButton = screen.getByText('Create Module') | ||
fireEvent.click(createButton) | ||
|
||
expect(mockPush).toHaveBeenCalledWith('//modules/create') | ||
expect(screen.getByText('Test Module')).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
|
@@ -338,67 +223,31 @@ describe('SingleModuleCard', () => { | |
expect(screen.queryByTestId('top-contributors-list')).not.toBeInTheDocument() | ||
}) | ||
|
||
it('handles undefined admins array', () => { | ||
it('handles undefined admins array gracefully', () => { | ||
render(<SingleModuleCard module={mockModule} showEdit={true} accessLevel="admin" />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
expect(screen.queryByText('Edit Module')).not.toBeInTheDocument() | ||
}) | ||
|
||
it('handles null session data', () => { | ||
mockUseSession.mockReturnValue({ | ||
data: null, | ||
status: 'unauthenticated', | ||
update: jest.fn(), | ||
}) | ||
|
||
render( | ||
<SingleModuleCard | ||
module={mockModule} | ||
showEdit={true} | ||
accessLevel="admin" | ||
admins={mockAdmins} | ||
/> | ||
) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
fireEvent.click(ellipsisButton) | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
expect(screen.queryByText('Edit Module')).not.toBeInTheDocument() | ||
// Should render without errors even with admin props | ||
expect(screen.getByText('Test Module')).toBeInTheDocument() | ||
}) | ||
}) | ||
|
||
describe('Accessibility', () => { | ||
it('has proper button roles and interactions', () => { | ||
it('has accessible link for module navigation', () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
expect(ellipsisButton).toBeInTheDocument() | ||
|
||
fireEvent.click(ellipsisButton) | ||
|
||
const viewButton = screen.getByText('View Module') | ||
expect(viewButton.closest('button')).toBeInTheDocument() | ||
const moduleLink = screen.getByTestId('module-link') | ||
expect(moduleLink).toBeInTheDocument() | ||
expect(moduleLink).toHaveAttribute('href', '//modules/test-module') | ||
expect(moduleLink).toHaveAttribute('target', '_blank') | ||
expect(moduleLink).toHaveAttribute('rel', 'noopener noreferrer') | ||
}) | ||
|
||
it('supports keyboard navigation', () => { | ||
it('has proper heading structure', () => { | ||
render(<SingleModuleCard module={mockModule} />) | ||
|
||
const ellipsisButton = screen.getByRole('button') | ||
|
||
// Focus the button | ||
ellipsisButton.focus() | ||
expect(ellipsisButton).toHaveFocus() | ||
|
||
// Press Enter to open dropdown | ||
fireEvent.keyDown(ellipsisButton, { key: 'Enter', code: 'Enter' }) | ||
fireEvent.click(ellipsisButton) // Simulate the click that would happen | ||
|
||
expect(screen.getByText('View Module')).toBeInTheDocument() | ||
const moduleTitle = screen.getByRole('heading', { level: 1 }) | ||
expect(moduleTitle).toBeInTheDocument() | ||
expect(moduleTitle).toHaveTextContent('Test Module') | ||
}) | ||
}) | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
'use client' | ||
|
||
import { useQuery } from '@apollo/client' | ||
import upperFirst from 'lodash/upperFirst' | ||
import { useParams } from 'next/navigation' | ||
import { useEffect, useState } from 'react' | ||
import { ErrorDisplay, handleAppError } from 'app/global-error' | ||
import { GET_PROGRAM_ADMINS_AND_MODULES } from 'server/queries/moduleQueries' | ||
import type { Module } from 'types/mentorship' | ||
import { titleCaseWord } from 'utils/capitalize' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainImport path alias check for utils/capitalize Using Run: 🏁 Script executed: #!/bin/bash
# Confirm baseUrl/paths config
rg -n -C2 '"baseUrl"|"paths"' frontend/tsconfig*.json
# See if this alias is used elsewhere consistently
rg -nP -C2 "from ['\"]utils/capitalize['\"]" frontend/src Length of output: 2094 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== frontend/tsconfig*.json files ==="
ls -la frontend/tsconfig*.json 2>/dev/null || true
echo
echo "=== frontend/tsconfig*.json: baseUrl / paths / extends ==="
rg -n -C2 '"baseUrl"|"paths"|"extends"' frontend/tsconfig*.json || true
echo
echo "=== repo-wide tsconfig paths occurrences ==="
rg -n -C2 '"paths"\s*:' || true
echo
echo "=== frontend/package.json (first 200 lines) ==="
if [ -f frontend/package.json ]; then
sed -n '1,200p' frontend/package.json
else
echo "no frontend/package.json"
fi
echo
echo "=== Search for Jest/Vitest/moduleNameMapper/tsconfig-paths/module-alias/module-resolver ==="
rg -n -C3 "moduleNameMapper|module-alias|tsconfig-paths|module-resolver|vitest|jest.config|jest\\.config" || true
echo
echo "=== Search for Storybook config (.storybook) and webpack resolve/alias usage ==="
rg -n -C3 "\\.storybook|webpackFinal|resolve\\s*:\\s*\\{|resolve\\.|alias\\s*:" || true
echo
echo "=== Search for next.config.js or Next webpack customizations ==="
rg -n -C3 "next.config|nextConfig|webpack\\s*:\\s*\\(" || true
echo
echo "=== Look for definitions/locations of titleCaseWord / capitalize file ==="
rg -n -C3 "titleCaseWord|utils/capitalize" || true
echo
echo "=== Search for any explicit 'utils/' path mappings in configs or code ==="
rg -n -C3 "\"utils/|\\butils\\/" || true
echo Length of output: 23641 Resolve 'utils/capitalize' import — use project alias or add path mapping frontend/tsconfig.json has baseUrl "." and no "utils/" path mapping; jest only maps "^@/(.)$" → "/src/$1". The file exists at frontend/src/utils/capitalize.ts but imports use "utils/capitalize" in:
Either:
🤖 Prompt for AI Agents
|
||
import { formatDate } from 'utils/dateFormatter' | ||
import DetailsCard from 'components/CardDetailsPage' | ||
import LoadingSpinner from 'components/LoadingSpinner' | ||
|
@@ -49,7 +49,7 @@ const ModuleDetailsPage = () => { | |
} | ||
|
||
const moduleDetails = [ | ||
{ label: 'Experience Level', value: upperFirst(module.experienceLevel) }, | ||
{ label: 'Experience Level', value: titleCaseWord(module.experienceLevel) }, | ||
{ label: 'Start Date', value: formatDate(module.startedAt) }, | ||
{ label: 'End Date', value: formatDate(module.endedAt) }, | ||
{ | ||
|
Uh oh!
There was an error while loading. Please reload this page.