From 10b5eabc8654d653c5b92d795a1953582147249a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 29 Jan 2026 14:19:35 +0800 Subject: [PATCH 1/2] feat: support getId --- src/hooks/useId.ts | 15 +++++++++++++++ tests/hooks.test.tsx | 13 +++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/hooks/useId.ts b/src/hooks/useId.ts index 65337a68..cc0e06ee 100644 --- a/src/hooks/useId.ts +++ b/src/hooks/useId.ts @@ -18,6 +18,21 @@ export function resetUuid() { } } +/** + * Generate a valid HTML id from prefix and key. + * Sanitizes the key by replacing invalid characters with hyphens. + * @param prefix - The prefix for the id + * @param key - The key from React element, may contain spaces or invalid characters + * @returns A valid HTML id string + */ +export function getId(prefix: string, key: string): string { + // Valid id characters: letters, digits, hyphen, underscore, colon, period + // Replace all invalid characters (including spaces) with hyphens to preserve length + const sanitizedKey = key.replace(/[^a-zA-Z0-9-_:.]/g, '-'); + + return `${prefix}-${sanitizedKey}`; +} + const useOriginId = getUseId(); export default useOriginId diff --git a/tests/hooks.test.tsx b/tests/hooks.test.tsx index fc096500..86125daa 100644 --- a/tests/hooks.test.tsx +++ b/tests/hooks.test.tsx @@ -2,6 +2,7 @@ import { fireEvent, render } from '@testing-library/react'; import * as React from 'react'; import { renderToString } from 'react-dom/server'; import useId from '../src/hooks/useId'; +import { getId } from '../src/hooks/useId'; import useLayoutEffect from '../src/hooks/useLayoutEffect'; import useMemo from '../src/hooks/useMemo'; import useMergedState from '../src/hooks/useMergedState'; @@ -663,6 +664,18 @@ describe('hooks', () => { errorSpy.mockRestore(); process.env.NODE_ENV = originEnv; }); + + it('should sanitize keys with invalid characters', () => { + expect(getId('item', 'hello world')).toBe('item-hello-world'); + expect(getId('tab', 'user@name#123')).toBe('tab-user-name-123'); + expect(getId('panel', 'test/path\\file')).toBe('panel-test-path-file'); + expect(getId('menu', 'key with multiple spaces')).toBe( + 'menu-key-with--multiple---spaces', + ); + expect(getId('btn', 'valid-key_123:456.789')).toBe( + 'btn-valid-key_123:456.789', + ); + }); }); describe('useMobile', () => { From 77c00642a27deb4dde1645a97c3d9ea6785a11bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E7=88=B1=E5=90=83=E7=99=BD=E8=90=9D?= =?UTF-8?q?=E5=8D=9C?= Date: Thu, 29 Jan 2026 14:23:23 +0800 Subject: [PATCH 2/2] Update src/hooks/useId.ts Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- src/hooks/useId.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks/useId.ts b/src/hooks/useId.ts index cc0e06ee..1b6a0e6d 100644 --- a/src/hooks/useId.ts +++ b/src/hooks/useId.ts @@ -28,7 +28,7 @@ export function resetUuid() { export function getId(prefix: string, key: string): string { // Valid id characters: letters, digits, hyphen, underscore, colon, period // Replace all invalid characters (including spaces) with hyphens to preserve length - const sanitizedKey = key.replace(/[^a-zA-Z0-9-_:.]/g, '-'); + const sanitizedKey = key.replace(/[^a-zA-Z0-9_.:-]/g, '-'); return `${prefix}-${sanitizedKey}`; }