-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: focus [autofocus]
element in a popup
#2736
Conversation
Warning Rate limit exceeded@limonte has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 32 minutes and 35 seconds before requesting another review. How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. WalkthroughThe updates introduce a new test case in the Cypress end-to-end tests to verify autofocus behavior for modals. Additionally, the Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant CypressTest
participant ModalComponent
participant SweetAlert
User->>CypressTest: Trigger modal open
CypressTest->>ModalComponent: Open modal
ModalComponent->>SweetAlert: Request to initialize focus
SweetAlert->>SweetAlert: Execute focusAutofocus
SweetAlert->>ModalComponent: Set focus on autofocus element
ModalComponent-->>User: Modal with autofocus active
Poem
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (invoked as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Outside diff range and nitpick comments (3)
src/SweetAlert.js (3)
Line range hint
77-77
: Optimize performance by avoiding the use of thedelete
operator.The use of the
delete
operator on properties (likeglobalState.timeout
) can lead to performance degradations because it modifies the object's shape. Consider setting the property toundefined
instead, which avoids these issues:- delete globalState.timeout; + globalState.timeout = undefined;Apply this change wherever the
delete
operator is used on properties.Also applies to: 208-208
Line range hint
214-214
: Use optional chaining for safer and cleaner code.Consider using optional chaining when accessing properties on potentially null or undefined objects to prevent runtime errors:
- if (globalState.timeout && globalState.timeout.running) { + if (globalState.timeout?.running) {This change ensures that the code is more robust and easier to read.
Also applies to: 354-354
Line range hint
353-358
: Convert function expressions to arrow functions for consistency.The function expressions used in the event handlers can be converted to arrow functions for better consistency and to avoid issues related to the
this
context:- SweetAlert[key] = function (...args) { + SweetAlert[key] = (...args) => { if (currentInstance && currentInstance[key]) { return currentInstance[key](...args); } return null; }This change aligns with modern JavaScript practices and improves the readability of the code.
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- cypress/e2e/accessibility.cy.js (1 hunks)
- src/SweetAlert.js (1 hunks)
Files skipped from review due to trivial changes (1)
- cypress/e2e/accessibility.cy.js
Additional context used
Biome
src/SweetAlert.js
[error] 77-77: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 208-208: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 214-214: Change to an optional chain. (lint/complexity/useOptionalChain)
Unsafe fix: Change to an optional chain.
[error] 354-354: Change to an optional chain. (lint/complexity/useOptionalChain)
Unsafe fix: Change to an optional chain.
[error] 353-358: This function expression can be turned into an arrow function. (lint/complexity/useArrowFunction)
Function expressions that don't use this can be turned into arrow functions.
Safe fix: Use an arrow function instead.
[error] 93-93: Do not add then to a class. (lint/suspicious/noThenProperty)
src/SweetAlert.js
Outdated
const focusAutofocus = (domCache) => { | ||
const autofocusElement = domCache.popup.querySelector('[autofocus]') | ||
if (autofocusElement instanceof HTMLElement && dom.isVisible(autofocusElement)) { | ||
autofocusElement.focus() | ||
return true | ||
} | ||
return false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure proper accessibility handling when focusing elements.
The focusAutofocus
function correctly identifies and focuses an element with the [autofocus]
attribute. However, consider verifying that this behavior aligns with accessibility standards, especially in scenarios where multiple autofocus elements might be present, which is generally discouraged but should still be handled gracefully.
src/SweetAlert.js
Outdated
* Initialize focus in the popup: | ||
* | ||
* 1. If `toast` is `true`, don't steal focus from the document. | ||
* 2. Else if there is an [autofocus] element, focus it. | ||
* 3. Else if `focusConfirm` is `true` and confirm button is visible, focus it. | ||
* 4. Else if `focusDeny` is `true` and deny button is visible, focus it. | ||
* 5. Else if `focusCancel` is `true` and cancel button is visible, focus it. | ||
* 6. Else focus the first focusable element in a popup (if any). | ||
* | ||
* @param {DomCache} domCache | ||
* @param {SweetAlertOptions} innerParams | ||
*/ | ||
const initFocus = (domCache, innerParams) => { | ||
if (innerParams.toast) { | ||
return | ||
} | ||
|
||
// TODO: this is dumb, remove `allowEnterKey` param in the next major version | ||
if (!callIfFunction(innerParams.allowEnterKey)) { | ||
blurActiveElement() | ||
return | ||
} | ||
|
||
if (!focusButton(domCache, innerParams)) { | ||
setFocus(-1, 1) | ||
if (focusAutofocus(domCache)) { | ||
return | ||
} | ||
|
||
if (focusButton(domCache, innerParams)) { | ||
return | ||
} | ||
|
||
setFocus(-1, 1) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactor initFocus
to improve readability and maintainability.
The initFocus
function has several conditional checks to determine where the focus should be set. To improve readability and maintainability, consider restructuring this function to reduce nesting and clarify the logic flow:
const initFocus = (domCache, innerParams) => {
if (innerParams.toast) return;
if (!callIfFunction(innerParams.allowEnterKey)) {
blurActiveElement();
return;
}
const focusHandlers = [
() => focusAutofocus(domCache),
() => focusButton(domCache, innerParams)
];
for (const handler of focusHandlers) {
if (handler()) return;
}
setFocus(-1, 1);
}
This refactoring uses an array of functions that encapsulate the focus logic, making it easier to add or remove focus strategies and improving the code's testability.
Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
* Initialize focus in the popup: | |
* | |
* 1. If `toast` is `true`, don't steal focus from the document. | |
* 2. Else if there is an [autofocus] element, focus it. | |
* 3. Else if `focusConfirm` is `true` and confirm button is visible, focus it. | |
* 4. Else if `focusDeny` is `true` and deny button is visible, focus it. | |
* 5. Else if `focusCancel` is `true` and cancel button is visible, focus it. | |
* 6. Else focus the first focusable element in a popup (if any). | |
* | |
* @param {DomCache} domCache | |
* @param {SweetAlertOptions} innerParams | |
*/ | |
const initFocus = (domCache, innerParams) => { | |
if (innerParams.toast) { | |
return | |
} | |
// TODO: this is dumb, remove `allowEnterKey` param in the next major version | |
if (!callIfFunction(innerParams.allowEnterKey)) { | |
blurActiveElement() | |
return | |
} | |
if (!focusButton(domCache, innerParams)) { | |
setFocus(-1, 1) | |
if (focusAutofocus(domCache)) { | |
return | |
} | |
if (focusButton(domCache, innerParams)) { | |
return | |
} | |
setFocus(-1, 1) | |
} | |
/** | |
* Initialize focus in the popup: | |
* | |
* 1. If `toast` is `true`, don't steal focus from the document. | |
* 2. Else if there is an [autofocus] element, focus it. | |
* 3. Else if `focusConfirm` is `true` and confirm button is visible, focus it. | |
* 4. Else if `focusDeny` is `true` and deny button is visible, focus it. | |
* 5. Else if `focusCancel` is `true` and cancel button is visible, focus it. | |
* 6. Else focus the first focusable element in a popup (if any). | |
* | |
* @param {DomCache} domCache | |
* @param {SweetAlertOptions} innerParams | |
*/ | |
const initFocus = (domCache, innerParams) => { | |
if (innerParams.toast) return; | |
if (!callIfFunction(innerParams.allowEnterKey)) { | |
blurActiveElement(); | |
return; | |
} | |
const focusHandlers = [ | |
() => focusAutofocus(domCache), | |
() => focusButton(domCache, innerParams) | |
]; | |
for (const handler of focusHandlers) { | |
if (handler()) return; | |
} | |
setFocus(-1, 1); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Files selected for processing (2)
- cypress/e2e/accessibility.cy.js (1 hunks)
- src/SweetAlert.js (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- cypress/e2e/accessibility.cy.js
Additional context used
Biome
src/SweetAlert.js
[error] 77-77: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 208-208: Avoid the delete operator which can impact performance. (lint/performance/noDelete)
Unsafe fix: Use an undefined assignment instead.
[error] 214-214: Change to an optional chain. (lint/complexity/useOptionalChain)
Unsafe fix: Change to an optional chain.
[error] 356-356: Change to an optional chain. (lint/complexity/useOptionalChain)
Unsafe fix: Change to an optional chain.
[error] 355-360: This function expression can be turned into an arrow function. (lint/complexity/useArrowFunction)
Function expressions that don't use this can be turned into arrow functions.
Safe fix: Use an arrow function instead.
[error] 93-93: Do not add then to a class. (lint/suspicious/noThenProperty)
Additional comments not posted (2)
src/SweetAlert.js (2)
224-255
: The refactoring ofinitFocus
to includefocusAutofocus
is well-integrated. The step-by-step focusing logic is clear and maintainable. However, consider using an array of focus strategies as suggested in a previous review to further improve readability and maintainability.
[REFACTOR_SUGGESTion]const initFocus = (domCache, innerParams) => { if (innerParams.toast) return; if (!callIfFunction(innerParams.allowEnterKey)) { blurActiveElement(); return; } const focusHandlers = [ () => focusAutofocus(domCache), () => focusButton(domCache, innerParams) ]; for (const handler of focusHandlers) { if (handler()) return; } setFocus(-1, 1); }
261-269
: The implementation offocusAutofocus
correctly focuses the first visible element with the[autofocus]
attribute. This aligns with accessibility best practices as mentioned in the previous review. However, ensure that this behavior is consistent across all supported browsers, especially in handling multiple autofocus elements.
🎉 This PR is included in version 11.12.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary by CodeRabbit
New Features
autofocus
attribute receive initial focus.Tests
autofocus
attribute.