Improve React Doctor score and badge (#1127)

* fix(react doctor): improve quality score and badge

* fix(react doctor): address review feedback

* fix(review): address final bot feedback
This commit is contained in:
Tommaso Casaburi
2026-05-10 18:25:13 +07:00
committed by GitHub
parent e7a6c377c9
commit 556973a445
47 changed files with 771 additions and 442 deletions
@@ -32,6 +32,7 @@ const testState = vi.hoisted(() => ({
quoteInsertNumber: undefined as number | undefined,
quoteInsertRequestId: 0,
quoteInsertSelectedText: '',
dragHandler: undefined as ((state: { active: boolean; event: Pick<Event, 'preventDefault'>; offset: [number, number] }) => void) | undefined,
replyIndex: undefined as number | undefined,
resetPublishReplyOptionsMock: vi.fn(),
resolvedCommunityAddress: undefined as string | undefined,
@@ -208,7 +209,10 @@ vi.mock('@react-spring/web', async () => {
});
vi.mock('@use-gesture/react', () => ({
useDrag: () => () => ({}),
useDrag: (handler: (state: { active: boolean; event: Pick<Event, 'preventDefault'>; offset: [number, number] }) => void) => {
testState.dragHandler = handler;
return () => ({});
},
}));
let container: HTMLDivElement;
@@ -292,6 +296,7 @@ describe('ReplyModal', () => {
testState.quoteInsertNumber = undefined;
testState.quoteInsertRequestId = 0;
testState.quoteInsertSelectedText = '';
testState.dragHandler = undefined;
testState.replyIndex = undefined;
testState.resetPublishReplyOptionsMock.mockReset();
testState.resolvedCommunityAddress = undefined;
@@ -326,6 +331,8 @@ describe('ReplyModal', () => {
afterEach(() => {
act(() => root.unmount());
container.remove();
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
});
it('initializes quoted content, display name, upload controls, and shared offline warning on board routes', async () => {
@@ -493,6 +500,41 @@ describe('ReplyModal', () => {
expect(modal?.style.touchAction).toBe('none');
});
it('closes with Escape from the document on desktop', async () => {
await renderReplyModal('/mu/thread/post-1');
await act(async () => {
document.dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Escape' }));
});
expect(testState.closeModalMock).toHaveBeenCalledTimes(1);
});
it('restores body selection styles if unmounted during a drag', async () => {
document.body.style.userSelect = 'text';
document.body.style.webkitUserSelect = 'auto';
await renderReplyModal('/mu/thread/post-1');
await act(async () => {
testState.dragHandler?.({
active: true,
event: { preventDefault: vi.fn() },
offset: [140, 100],
});
});
expect(document.body.style.userSelect).toBe('none');
expect(document.body.style.webkitUserSelect).toBe('none');
await act(async () => {
root.render(createElement(React.Fragment));
});
expect(document.body.style.userSelect).toBe('text');
expect(document.body.style.webkitUserSelect).toBe('auto');
});
it('initializes the drag spring once so typing rerenders do not recenter the modal', async () => {
await renderReplyModal('/mu/thread/post-1');
+60 -29
View File
@@ -54,6 +54,16 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
const account = useAccount();
const { displayName } = account?.author || {};
const textRef = useRef<HTMLTextAreaElement | null>(null);
const setTextRef = useRef((element: HTMLTextAreaElement | null) => {
textRef.current = element;
if (!element) return;
window.setTimeout(() => {
if (textRef.current === element) {
element.focus();
}
}, 0);
});
const urlRef = useRef<HTMLInputElement>(null);
const lastSelectionStartRef = useRef(0);
const lastSelectionEndRef = useRef(0);
@@ -126,6 +136,27 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
[],
);
const bodySelectionStyleBeforeDragRef = useRef<{ userSelect: string; webkitUserSelect: string } | null>(null);
const disableBodyTextSelection = () => {
if (!bodySelectionStyleBeforeDragRef.current) {
bodySelectionStyleBeforeDragRef.current = {
userSelect: document.body.style.userSelect,
webkitUserSelect: document.body.style.webkitUserSelect,
};
}
Object.assign(document.body.style, { userSelect: 'none', webkitUserSelect: 'none' });
};
const restoreBodyTextSelection = () => {
const previousStyle = bodySelectionStyleBeforeDragRef.current;
Object.assign(document.body.style, {
userSelect: previousStyle?.userSelect ?? '',
webkitUserSelect: previousStyle?.webkitUserSelect ?? '',
});
bodySelectionStyleBeforeDragRef.current = null;
};
const bind = useDrag(
({ active, event, offset: [ox, oy] }) => {
const nextLeft = Math.round(ox);
@@ -133,11 +164,9 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
if (active) {
event.preventDefault();
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
disableBodyTextSelection();
} else {
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
restoreBodyTextSelection();
}
api.start({ left: nextLeft, top: nextTop, immediate: true });
},
@@ -148,6 +177,12 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
},
);
useEffect(() => {
return () => {
restoreBodyTextSelection();
};
}, []);
useEffect(() => {
if (nodeRef.current && isMobile) {
const viewportHeight = window.innerHeight;
@@ -158,6 +193,21 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
const parentCidRef = useRef<HTMLSpanElement>(null);
useEffect(() => {
if (!showReplyModal || isMobile) {
return;
}
const closeReplyModalOnEscape = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
closeModal();
}
};
document.addEventListener('keydown', closeReplyModalOnEscape);
return () => document.removeEventListener('keydown', closeReplyModalOnEscape);
}, [showReplyModal, isMobile, closeModal]);
useEffect(() => {
if (parentCidRef.current) {
const cidWidth = parentCidRef.current.offsetWidth;
@@ -165,29 +215,6 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
}
}, [parentCid]);
useEffect(() => {
if (showReplyModal) {
setTimeout(() => {
if (textRef.current) {
textRef.current.focus();
}
}, 0);
if (!isMobile) {
const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') {
closeModal();
}
};
document.addEventListener('keydown', handleEscape);
return () => {
document.removeEventListener('keydown', handleEscape);
};
}
}
}, [showReplyModal, closeModal, isMobile]);
useEffect(() => {
if (textRef.current) {
const len = textRef.current.value.length;
@@ -209,11 +236,15 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
setPublishReplyOptions({ content });
checkContentLengthRef.current(content, t);
setTimeout(() => {
const spellcheckTimeout = window.setTimeout(() => {
if (textRef.current) {
textRef.current.spellcheck = true;
}
}, 100);
return () => {
window.clearTimeout(spellcheckTimeout);
};
}
}, [showReplyModal, openEmpty, defaultParentQuote, selectedText]);
@@ -351,7 +382,7 @@ const ReplyModal = ({ closeModal, showReplyModal, parentCid, parentNumber, threa
cols={48}
rows={4}
wrap='soft'
ref={textRef}
ref={setTextRef.current}
aria-label={t('comment')}
spellCheck={true}
onInput={handleContentInput}