Skip to content

Commit e310d87

Browse files
authored
Merge pull request #163 from Resgrid/develop
CU-868f7hkrj Minor UI fixes for Light mode, security fix and testing …
2 parents 9830cdd + 409f168 commit e310d87

19 files changed

+1805
-126
lines changed

docs/map-theme-implementation.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Map Theme Implementation
2+
3+
## Overview
4+
This document outlines the implementation of light/dark theme support for the map component in the Resgrid Unit app.
5+
6+
## Changes Made
7+
8+
### 1. Map Component Updates (`src/app/(app)/index.tsx`)
9+
10+
#### Theme Integration
11+
- Imported `useColorScheme` from `nativewind` for theme detection
12+
- Added `getMapStyle()` function that returns appropriate Mapbox style based on current theme:
13+
- **Light mode**: `Mapbox.StyleURL.Street` (professional, muted appearance)
14+
- **Dark mode**: `Mapbox.StyleURL.Dark` (dark theme optimized for low light)
15+
16+
#### Dynamic Styling
17+
- Created `getThemedStyles()` function for theme-aware component styling
18+
- Updated marker and recenter button styles to adapt to theme:
19+
- **Light mode**: White borders and dark shadows
20+
- **Dark mode**: Dark borders and light shadows
21+
22+
#### Analytics Enhancement
23+
- Added theme information to analytics tracking
24+
- Map view rendered events now include current theme data
25+
26+
#### Map Style Updates
27+
- Map style now automatically updates when theme changes
28+
- Added `useEffect` to watch for theme changes and update map style accordingly
29+
30+
### 2. Test Updates (`src/app/(app)/__tests__/index.test.tsx`)
31+
32+
#### Mock Enhancements
33+
- Updated `useColorScheme` mock to include proper interface
34+
- Added support for testing both light and dark themes
35+
- Enhanced Mapbox mock to include Light and Dark style URLs
36+
37+
#### New Test Cases
38+
- **Light theme test**: Verifies map renders correctly in light mode
39+
- **Dark theme test**: Verifies map renders correctly in dark mode
40+
- **Theme change test**: Verifies smooth transition between themes
41+
- **Analytics test**: Verifies theme information is tracked in analytics
42+
43+
## Available Map Styles
44+
45+
| Theme | Mapbox Style | URL | Description |
46+
|-------|-------------|-----|-------------|
47+
| Light | `Mapbox.StyleURL.Street` | `mapbox://styles/mapbox/streets-v11` | Professional street map with balanced colors |
48+
| Dark | `Mapbox.StyleURL.Dark` | `mapbox://styles/mapbox/dark-v10` | Dark theme optimized for low light |
49+
50+
## Theme Detection
51+
52+
The component uses the `useColorScheme` hook from `nativewind` which provides:
53+
- Current theme: `'light' | 'dark' | undefined`
54+
- Theme setter function
55+
- Automatic system theme detection
56+
57+
## User Experience
58+
59+
### Light Mode
60+
- Professional street map appearance suitable for all lighting conditions
61+
- Balanced contrast with clear, readable text and features
62+
- Consistent with the call detail view's static map styling
63+
- Muted colors that reduce eye strain compared to bright white themes
64+
65+
### Dark Mode
66+
- Reduced brightness for low-light environments
67+
- Dark backgrounds with light accents
68+
- Eye-strain reduction for nighttime usage
69+
70+
### Automatic Switching
71+
- Theme changes are applied immediately without restart
72+
- Smooth transitions between light and dark styles
73+
- Maintains map position and zoom during theme changes
74+
75+
## Testing Coverage
76+
77+
All tests pass successfully:
78+
- ✅ Basic map rendering
79+
- ✅ Light theme functionality
80+
- ✅ Dark theme functionality
81+
- ✅ Theme switching behavior
82+
- ✅ Analytics integration with theme data
83+
84+
## Technical Notes
85+
86+
- Map style updates are handled through React state (`styleURL`)
87+
- Theme-aware styling uses `useCallback` for performance optimization
88+
- Analytics tracking includes theme context for usage insights
89+
- Component maintains backward compatibility with existing functionality
90+
- **Style Choice**: Uses `Street` style for light mode instead of `Light` style to match the professional appearance of the call detail view's static map and reduce visual brightness
91+
92+
## Future Enhancements
93+
94+
Potential improvements for future versions:
95+
- Custom map styles for better brand integration
96+
- Theme-specific marker colors
97+
- Transition animations during theme changes
98+
- User preference persistence across app restarts
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/**
2+
* Security Integration Test
3+
*
4+
* This test validates that the security permission checking logic works correctly
5+
* for the calls functionality without complex component mocking.
6+
*/
7+
8+
import { type DepartmentRightsResultData } from '@/models/v4/security/departmentRightsResultData';
9+
10+
describe('Security Permission Logic', () => {
11+
// This mimics the logic in useSecurityStore.canUserCreateCalls
12+
const canUserCreateCalls = (rights: DepartmentRightsResultData | null): boolean => {
13+
return rights?.CanCreateCalls === true;
14+
};
15+
16+
describe('canUserCreateCalls', () => {
17+
it('should return true when user has CanCreateCalls permission', () => {
18+
const rights: DepartmentRightsResultData = {
19+
DepartmentName: 'Test Department',
20+
DepartmentCode: 'TEST',
21+
FullName: 'Test User',
22+
EmailAddress: '[email protected]',
23+
DepartmentId: '1',
24+
IsAdmin: false,
25+
CanViewPII: false,
26+
CanCreateCalls: true,
27+
CanAddNote: false,
28+
CanCreateMessage: false,
29+
Groups: []
30+
};
31+
32+
expect(canUserCreateCalls(rights)).toBe(true);
33+
});
34+
35+
it('should return false when user does not have CanCreateCalls permission', () => {
36+
const rights: DepartmentRightsResultData = {
37+
DepartmentName: 'Test Department',
38+
DepartmentCode: 'TEST',
39+
FullName: 'Test User',
40+
EmailAddress: '[email protected]',
41+
DepartmentId: '1',
42+
IsAdmin: false,
43+
CanViewPII: true,
44+
CanCreateCalls: false,
45+
CanAddNote: true,
46+
CanCreateMessage: true,
47+
Groups: []
48+
};
49+
50+
expect(canUserCreateCalls(rights)).toBe(false);
51+
});
52+
53+
it('should return false when rights is null', () => {
54+
expect(canUserCreateCalls(null)).toBe(false);
55+
});
56+
57+
it('should return false when CanCreateCalls is undefined', () => {
58+
const rights = {
59+
DepartmentName: 'Test Department',
60+
DepartmentCode: 'TEST',
61+
FullName: 'Test User',
62+
EmailAddress: '[email protected]',
63+
DepartmentId: '1',
64+
IsAdmin: false,
65+
CanViewPII: true,
66+
CanAddNote: true,
67+
CanCreateMessage: true,
68+
Groups: []
69+
} as unknown as DepartmentRightsResultData;
70+
71+
expect(canUserCreateCalls(rights)).toBe(false);
72+
});
73+
});
74+
75+
describe('UI Logic Validation', () => {
76+
it('should show FAB when user can create calls', () => {
77+
const rights: DepartmentRightsResultData = {
78+
DepartmentName: 'Test Department',
79+
DepartmentCode: 'TEST',
80+
FullName: 'Test User',
81+
EmailAddress: '[email protected]',
82+
DepartmentId: '1',
83+
IsAdmin: false,
84+
CanViewPII: false,
85+
CanCreateCalls: true,
86+
CanAddNote: false,
87+
CanCreateMessage: false,
88+
Groups: []
89+
};
90+
91+
const shouldShowFab = canUserCreateCalls(rights);
92+
const shouldShowMenu = canUserCreateCalls(rights);
93+
94+
expect(shouldShowFab).toBe(true);
95+
expect(shouldShowMenu).toBe(true);
96+
});
97+
98+
it('should hide FAB and menu when user cannot create calls', () => {
99+
const rights: DepartmentRightsResultData = {
100+
DepartmentName: 'Test Department',
101+
DepartmentCode: 'TEST',
102+
FullName: 'Test User',
103+
EmailAddress: '[email protected]',
104+
DepartmentId: '1',
105+
IsAdmin: false,
106+
CanViewPII: true,
107+
CanCreateCalls: false,
108+
CanAddNote: true,
109+
CanCreateMessage: true,
110+
Groups: []
111+
};
112+
113+
const shouldShowFab = canUserCreateCalls(rights);
114+
const shouldShowMenu = canUserCreateCalls(rights);
115+
116+
expect(shouldShowFab).toBe(false);
117+
expect(shouldShowMenu).toBe(false);
118+
});
119+
120+
it('should hide FAB and menu when rights are not available', () => {
121+
const shouldShowFab = canUserCreateCalls(null);
122+
const shouldShowMenu = canUserCreateCalls(null);
123+
124+
expect(shouldShowFab).toBe(false);
125+
expect(shouldShowMenu).toBe(false);
126+
});
127+
});
128+
});

0 commit comments

Comments
 (0)