Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function parseStackTraceFromChromeStack(
return parsedFrames;
}

const firefoxFrameRegExp = /^((?:.*".+")?[^@]*)@(.+):(\d+):(\d+)$/;
const firefoxFrameRegExp = /^([^@"]*(?:"[^"]*"[^@"]*)*)@(.+):(\d+):(\d+)$/;
function parseStackTraceFromFirefoxStack(
stack: string,
skipFrames: number,
Expand Down Expand Up @@ -94,7 +94,7 @@ function parseStackTraceFromFirefoxStack(
return parsedFrames;
}

const CHROME_STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
const CHROME_STACK_REGEXP = /^\s*at /m;
export function parseStackTraceFromString(
stack: string,
skipFrames: number,
Expand Down
10 changes: 9 additions & 1 deletion packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,8 @@ export function finalizeInitialChildren(
case 'img':
return true;
default:
return false;
// autoFocus is a global HTML attribute that applies to all elements.
return !!props.autoFocus;
}
}

Expand Down Expand Up @@ -891,6 +892,13 @@ export function commitMount(
}
return;
}
default: {
// autoFocus is a global HTML attribute that applies to all elements.
if (newProps.autoFocus) {
((domElement: any): HTMLElement).focus();
}
return;
}
}
}

Expand Down
67 changes: 67 additions & 0 deletions packages/react-dom/src/__tests__/ReactDOM-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,73 @@ describe('ReactDOM', () => {
}
});

it('calls focus() on autoFocus anchor elements after they have been mounted to the DOM', async () => {
const originalFocus = HTMLElement.prototype.focus;

try {
let focusedElement;
let anchorFocusedAfterMount = false;

HTMLElement.prototype.focus = function () {
focusedElement = this;
anchorFocusedAfterMount = !!this.parentNode;
};

const container = document.createElement('div');
document.body.appendChild(container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<div>
<h1>Auto-focus Test</h1>
<a href="https://react.dev" autoFocus={true}>
Link
</a>
<p>The above anchor should be focused after mount.</p>
</div>,
);
});

expect(anchorFocusedAfterMount).toBe(true);
expect(focusedElement.tagName).toBe('A');
} finally {
HTMLElement.prototype.focus = originalFocus;
}
});

it('calls focus() on autoFocus for any focusable element after mount', async () => {
const originalFocus = HTMLElement.prototype.focus;

try {
let focusedElement;
let elementFocusedAfterMount = false;

HTMLElement.prototype.focus = function () {
focusedElement = this;
elementFocusedAfterMount = !!this.parentNode;
};

const container = document.createElement('div');
document.body.appendChild(container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<div>
<div tabIndex={0} autoFocus={true} id="focusable-div">
Focusable div
</div>
</div>,
);
});

expect(elementFocusedAfterMount).toBe(true);
expect(focusedElement.tagName).toBe('DIV');
expect(focusedElement.id).toBe('focusable-div');
} finally {
HTMLElement.prototype.focus = originalFocus;
}
});

it("shouldn't fire duplicate event handler while handling other nested dispatch", async () => {
const actual = [];

Expand Down