From 71e6d7205105cb7414e1ee635688a2ae9b9072cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Thu, 27 Aug 2026 05:19:32 +0800 Subject: [PATCH 1/3] fix: remove button semantics when click upload is disabled --- src/AjaxUploader.tsx | 9 +++++++-- tests/uploader.spec.tsx | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 0244c3be..62f61b77 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -368,10 +368,15 @@ class AjaxUploader extends Component { onMouseLeave, onDrop: this.onFileDrop, onDragOver: this.onFileDragOver, - tabIndex: hasControlInside ? undefined : '0', + tabIndex: hasControlInside || !openFileDialogOnClick ? undefined : '0', }; return ( - + { expect(container.querySelector('span')!).not.toHaveAttribute('role', 'button'); }); + it("Should not be focusable and doesn't have role=button when click upload is disabled", () => { + const { container } = render(); + + expect(container.querySelector('span')!.tabIndex).not.toBe(0); + expect(container.querySelector('span')!).not.toHaveAttribute('role', 'button'); + }); + it('should receive same defaultRequest as src', done => { const { default: srcRequest } = require('../src/request'); let receivedDefaultRequest: any; From e0569f2d1bd3b3a48dca8640ec44764ef48803b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Fri, 28 Aug 2026 01:09:07 +0800 Subject: [PATCH 2/3] fix: support Space activation for upload controls --- src/AjaxUploader.tsx | 3 ++- tests/uploader.spec.tsx | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 62f61b77..7734b0b6 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -86,7 +86,8 @@ class AjaxUploader extends Component { }; onKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); this.onClick(e); } }; diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 89d5deb4..607de804 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -1313,6 +1313,21 @@ describe('uploader', () => { expect(container.querySelector('span')).toHaveAttribute('role', 'button'); }); + it.each(['Enter', ' '])('Should open the file dialog with the %p key', key => { + const { container } = render(); + const input = container.querySelector('input')!; + const clickSpy = jest.spyOn(input, 'click').mockImplementation(() => {}); + const preventDefaultSpy = jest.spyOn(Event.prototype, 'preventDefault'); + + fireEvent.keyDown(container.querySelector('span')!, { key }); + + expect(clickSpy).toHaveBeenCalledTimes(1); + expect(preventDefaultSpy).toHaveBeenCalledTimes(1); + + clickSpy.mockRestore(); + preventDefaultSpy.mockRestore(); + }); + it("Should not be focusable and doesn't have role=button with hasControlInside=true", () => { const { container } = render(); From bca59c9860ee24ed2dad605494dfea8c8a9f76c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B3=E5=BB=B7=E5=AE=89?= <73953029+nrps9909@users.noreply.github.com> Date: Fri, 28 Aug 2026 01:21:53 +0800 Subject: [PATCH 3/3] fix: defer keyboard activation to child controls --- src/AjaxUploader.tsx | 2 +- tests/uploader.spec.tsx | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/AjaxUploader.tsx b/src/AjaxUploader.tsx index 7734b0b6..be41699b 100644 --- a/src/AjaxUploader.tsx +++ b/src/AjaxUploader.tsx @@ -364,7 +364,7 @@ class AjaxUploader extends Component { ? {} : { onClick: openFileDialogOnClick ? this.onClick : () => {}, - onKeyDown: openFileDialogOnClick ? this.onKeyDown : () => {}, + onKeyDown: openFileDialogOnClick && !hasControlInside ? this.onKeyDown : () => {}, onMouseEnter, onMouseLeave, onDrop: this.onFileDrop, diff --git a/tests/uploader.spec.tsx b/tests/uploader.spec.tsx index 607de804..6fc010a7 100644 --- a/tests/uploader.spec.tsx +++ b/tests/uploader.spec.tsx @@ -1335,6 +1335,25 @@ describe('uploader', () => { expect(container.querySelector('span')!).not.toHaveAttribute('role', 'button'); }); + it.each(['Enter', ' '])('Should leave %p activation to the child control', key => { + const { container } = render( + + + , + ); + const input = container.querySelector('input')!; + const button = container.querySelector('button')!; + const clickSpy = jest.spyOn(input, 'click').mockImplementation(() => {}); + + expect(fireEvent.keyDown(button, { key })).toBe(true); + expect(clickSpy).not.toHaveBeenCalled(); + + fireEvent.click(button); + expect(clickSpy).toHaveBeenCalledTimes(1); + + clickSpy.mockRestore(); + }); + it("Should not be focusable and doesn't have role=button when click upload is disabled", () => { const { container } = render();