Skip to content

Commit a3d2a74

Browse files
Copilotweareoutman
andcommitted
Replace deprecated navigator.platform with modern platform detection
Co-authored-by: weareoutman <2338946+weareoutman@users.noreply.github.com>
1 parent a163a62 commit a3d2a74

File tree

5 files changed

+161
-30
lines changed

5 files changed

+161
-30
lines changed

docusaurus-search-local/src/client/theme/SearchBar/SearchBar.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import LoadingRing from "../LoadingRing/LoadingRing";
3636
import { normalizeContextByPath } from "../../utils/normalizeContextByPath";
3737
import { searchResultLimits } from "../../utils/proxiedGeneratedConstants";
3838
import { parseKeymap, matchesKeymap, getKeymapHints } from "../../utils/keymap";
39+
import { isMacPlatform } from "../../utils/platform";
3940

4041
import styles from "./SearchBar.module.css";
4142

@@ -389,11 +390,7 @@ export default function SearchBar({
389390
);
390391

391392
// Implement hint icons for the search shortcuts on mac and the rest operating systems.
392-
const isMac = isBrowser
393-
? /mac/i.test(
394-
(navigator as any).userAgentData?.platform ?? navigator.platform
395-
)
396-
: false;
393+
const isMac = isBrowser ? isMacPlatform() : false;
397394

398395
// Sync the input value and focus state for SSR
399396
useEffect(

docusaurus-search-local/src/client/utils/keymap.spec.ts

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
import { parseKeymap, matchesKeymap, getKeymapHints } from './keymap';
2+
import * as platformModule from './platform';
3+
4+
// Mock the platform module
5+
jest.mock('./platform');
26

37
describe('keymap utility functions', () => {
8+
const mockIsMacPlatform = jest.mocked(platformModule.isMacPlatform);
9+
10+
beforeEach(() => {
11+
mockIsMacPlatform.mockClear();
12+
});
413
describe('parseKeymap', () => {
514
test('should parse single key', () => {
615
const result = parseKeymap('s');
@@ -36,12 +45,7 @@ describe('keymap utility functions', () => {
3645
});
3746

3847
test('should parse mod+k on Mac-like platform', () => {
39-
// Mock navigator.platform to simulate Mac
40-
const originalNavigator = global.navigator;
41-
Object.defineProperty(global, 'navigator', {
42-
value: { platform: 'MacIntel' },
43-
configurable: true
44-
});
48+
mockIsMacPlatform.mockReturnValue(true);
4549

4650
const result = parseKeymap('mod+k');
4751
expect(result).toEqual({
@@ -51,21 +55,10 @@ describe('keymap utility functions', () => {
5155
shift: false,
5256
meta: true,
5357
});
54-
55-
// Restore original navigator
56-
Object.defineProperty(global, 'navigator', {
57-
value: originalNavigator,
58-
configurable: true
59-
});
6058
});
6159

6260
test('should parse mod+k on non-Mac platform', () => {
63-
// Mock navigator.platform to simulate non-Mac
64-
const originalNavigator = global.navigator;
65-
Object.defineProperty(global, 'navigator', {
66-
value: { platform: 'Win32' },
67-
configurable: true
68-
});
61+
mockIsMacPlatform.mockReturnValue(false);
6962

7063
const result = parseKeymap('mod+k');
7164
expect(result).toEqual({
@@ -75,12 +68,6 @@ describe('keymap utility functions', () => {
7568
shift: false,
7669
meta: false,
7770
});
78-
79-
// Restore original navigator
80-
Object.defineProperty(global, 'navigator', {
81-
value: originalNavigator,
82-
configurable: true
83-
});
8471
});
8572

8673
test('should parse complex combination', () => {

docusaurus-search-local/src/client/utils/keymap.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { isMacPlatform } from './platform';
2+
13
export interface ParsedKeymap {
24
key: string;
35
ctrl: boolean;
@@ -17,7 +19,7 @@ export function parseKeymap(keymap: string): ParsedKeymap {
1719
};
1820

1921
// Detect if we're on Mac to handle 'mod' appropriately
20-
const isMac = typeof navigator !== 'undefined' && navigator.platform.toLowerCase().includes('mac');
22+
const isMac = isMacPlatform();
2123

2224
for (const part of parts) {
2325
const trimmed = part.trim();
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { isMacPlatform } from './platform';
2+
3+
describe('platform utility functions', () => {
4+
// Store original navigator to restore after tests
5+
const originalNavigator = global.navigator;
6+
7+
afterEach(() => {
8+
// Restore original navigator after each test
9+
Object.defineProperty(global, 'navigator', {
10+
value: originalNavigator,
11+
configurable: true
12+
});
13+
});
14+
15+
describe('isMacPlatform', () => {
16+
test('should return false when navigator is undefined', () => {
17+
Object.defineProperty(global, 'navigator', {
18+
value: undefined,
19+
configurable: true
20+
});
21+
22+
expect(isMacPlatform()).toBe(false);
23+
});
24+
25+
test('should detect Mac using userAgentData.platform', () => {
26+
Object.defineProperty(global, 'navigator', {
27+
value: {
28+
userAgentData: { platform: 'macOS' },
29+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
30+
platform: 'Win32'
31+
},
32+
configurable: true
33+
});
34+
35+
expect(isMacPlatform()).toBe(true);
36+
});
37+
38+
test('should detect non-Mac using userAgentData.platform', () => {
39+
Object.defineProperty(global, 'navigator', {
40+
value: {
41+
userAgentData: { platform: 'Windows' },
42+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
43+
platform: 'MacIntel'
44+
},
45+
configurable: true
46+
});
47+
48+
expect(isMacPlatform()).toBe(false);
49+
});
50+
51+
test('should fallback to userAgent when userAgentData is not available', () => {
52+
Object.defineProperty(global, 'navigator', {
53+
value: {
54+
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
55+
platform: 'Win32'
56+
},
57+
configurable: true
58+
});
59+
60+
expect(isMacPlatform()).toBe(true);
61+
});
62+
63+
test('should detect non-Mac using userAgent fallback', () => {
64+
Object.defineProperty(global, 'navigator', {
65+
value: {
66+
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
67+
platform: 'MacIntel'
68+
},
69+
configurable: true
70+
});
71+
72+
expect(isMacPlatform()).toBe(false);
73+
});
74+
75+
test('should fallback to platform when userAgent is not available', () => {
76+
Object.defineProperty(global, 'navigator', {
77+
value: {
78+
platform: 'MacIntel'
79+
},
80+
configurable: true
81+
});
82+
83+
expect(isMacPlatform()).toBe(true);
84+
});
85+
86+
test('should detect non-Mac using platform fallback', () => {
87+
Object.defineProperty(global, 'navigator', {
88+
value: {
89+
platform: 'Win32'
90+
},
91+
configurable: true
92+
});
93+
94+
expect(isMacPlatform()).toBe(false);
95+
});
96+
97+
test('should return false when no platform detection methods are available', () => {
98+
Object.defineProperty(global, 'navigator', {
99+
value: {},
100+
configurable: true
101+
});
102+
103+
expect(isMacPlatform()).toBe(false);
104+
});
105+
106+
test('should handle case insensitive platform detection', () => {
107+
Object.defineProperty(global, 'navigator', {
108+
value: {
109+
userAgentData: { platform: 'MACOS' },
110+
},
111+
configurable: true
112+
});
113+
114+
expect(isMacPlatform()).toBe(true);
115+
});
116+
});
117+
});
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* Detects if the current platform is macOS using modern, non-deprecated APIs.
3+
* Falls back gracefully for older browsers or server-side rendering.
4+
*/
5+
export function isMacPlatform(): boolean {
6+
// Handle server-side rendering or missing navigator
7+
if (typeof navigator === 'undefined') {
8+
return false;
9+
}
10+
11+
// Try modern User-Agent Client Hints API first (if available)
12+
if ('userAgentData' in navigator && (navigator as any).userAgentData?.platform) {
13+
const platform = (navigator as any).userAgentData.platform.toLowerCase();
14+
return platform.includes('mac');
15+
}
16+
17+
// Fall back to user agent string parsing (more reliable than navigator.platform)
18+
if (navigator.userAgent) {
19+
return /mac/i.test(navigator.userAgent);
20+
}
21+
22+
// Final fallback to deprecated navigator.platform for very old browsers
23+
if (navigator.platform) {
24+
return navigator.platform.toLowerCase().includes('mac');
25+
}
26+
27+
return false;
28+
}

0 commit comments

Comments
 (0)