Skip to content

Refactor: Enterprise-Grade Modal Security, Accessibility, and Perform… - #221

Open
maheshMadiwalar18 wants to merge 2 commits into
AOSSIE-Org:mainfrom
maheshMadiwalar18:refactor/enterprise-modal-security-a11y-perf
Open

Refactor: Enterprise-Grade Modal Security, Accessibility, and Perform…#221
maheshMadiwalar18 wants to merge 2 commits into
AOSSIE-Org:mainfrom
maheshMadiwalar18:refactor/enterprise-modal-security-a11y-perf

Conversation

@maheshMadiwalar18

@maheshMadiwalar18 maheshMadiwalar18 commented Aug 6, 2026

Copy link
Copy Markdown

Hi maintainers! 👋

This is my first contribution to AOSSIE. I have implemented the requested refactor with a focus on security, accessibility, and performance while maintaining backward compatibility.

Changes included:

  • Replaced innerHTML with secure DOM APIs
  • Added WCAG 2.1 AA accessibility improvements (ARIA attributes, focus trapping, and focus restoration)
  • Optimized event handling using event delegation
  • Verified that the existing functionality and public API remain unchanged

I would appreciate any feedback or suggestions for improvement. Thank you for your time and review!

Summary by CodeRabbit

  • New Features

    • Added configurable modal labels and centralized sharing platform support.
    • Improved modal accessibility with focus management, keyboard navigation, Escape-to-close, and focus restoration.
    • Added secure DOM-based rendering and SVG icons.
    • Invalid sharing platforms are now safely ignored.
  • Bug Fixes

    • Improved sharing interactions and modal cleanup.
  • Tests

    • Added comprehensive automated coverage for rendering, accessibility, security, interactions, and compatibility.

…ance

Removed innerHTML in favor of secure DOM APIs. Added WCAG 2.1 AA focus trapping and ARIA attributes. Implemented event delegation for better performance. Added comprehensive vitest test suite.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The share button now uses DOM construction, accessible modal behavior, delegated platform clicks, and configurable labels. Vitest setup and comprehensive tests cover security, accessibility, lifecycle behavior, public API compatibility, and SVG output. The landing page TypeScript configuration also changes its compiler settings.

Changes

Share button refactor and validation

Layer / File(s) Summary
DOM construction and modal semantics
src/social-share-button.js, tests/social-share-button.test.js
The component creates controls, SVGs, platform buttons, and modal elements with DOM APIs. It adds configurable labels, dialog semantics, ARIA labeling, unique title IDs, and SVG validation.
Modal focus and platform interactions
src/social-share-button.js, tests/social-share-button.test.js
The component traps focus, handles Escape, restores focus after closing, delegates platform clicks, filters unknown platforms, and cleans up modal state and listeners.
Vitest setup and compatibility validation
package.json, vitest.config.js, tests/social-share-button.test.js, .github/pr-body.md
Vitest scripts, jsdom, test configuration, test isolation, public API checks, and refactor documentation are added.
Landing page compiler configuration
landing-page/tsconfig.json
The configuration targets ES2017, uses react-jsx, expands arrays, and includes .next/dev/types/**/*.ts.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant SocialShareButton
  participant ModalDOM
  participant PlatformContainer
  User->>SocialShareButton: open modal
  SocialShareButton->>ModalDOM: create and display accessible dialog
  SocialShareButton->>ModalDOM: focus first enabled element
  User->>ModalDOM: press Tab or Escape
  ModalDOM->>SocialShareButton: dispatch keyboard event
  SocialShareButton->>ModalDOM: trap focus or close dialog
  User->>PlatformContainer: click platform control
  PlatformContainer->>SocialShareButton: delegate platform click
Loading

Possibly related PRs

Suggested labels: Typescript Lang

Poem

A rabbit builds each node with care,
While focus hops through modal air.
SVG paths shine, buttons align,
Escape and Tab follow each sign.
Vitest checks the share flow bright—
Secure controls work just right.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main modal refactor, including security, accessibility, and performance improvements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

socket-security Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedvitest@​4.1.10981007997100
Addedjsdom@​29.1.18510010096100

View full report

@socket-security

socket-security Bot commented Aug 6, 2026

Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm data-urls is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package-lock.jsonnpm/jsdom@29.1.1npm/data-urls@7.0.0

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/data-urls@7.0.0. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

Warn High
Obfuscated code: npm jsdom is 90.0% likely obfuscated

Confidence: 0.90

Location: Package overview

From: package-lock.jsonnpm/jsdom@29.1.1

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/jsdom@29.1.1. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/social-share-button.js`:
- Around line 227-252: Set the social platform buttons created in the platforms
iteration to type="button" immediately after creating each button, ensuring they
do not submit enclosing forms; apply the same attribute to the other newly
created buttons referenced by the consolidated comment.
- Around line 132-134: All dynamically created buttons must explicitly use the
non-submitting button type. In src/social-share-button.js at lines 132-134,
update createButton() to set button.type after creation; make the same change
for closeBtn at lines 174-177, copyBtn at lines 197-199, and each platform btn
in buildPlatformButtons() at lines 231-234.
- Around line 421-423: Update openModal() so previousActiveElement is assigned
only when opening a currently closed modal, preserving the original focused
element across repeated calls. Keep closeModal() restoring focus through the
existing previousActiveElement value.
- Around line 169-199: Externalize the modal strings created in the
modal-building flow around the title, close button, URL input, and copy button.
Add corresponding constructor options alongside the existing buttonText option,
defaulting to “Share”, “Close”, “URL to share”, and “Copy”, then use those
options for the visible text and aria-labels instead of hardcoded values.
- Around line 375-387: Update handleKeydown and closeModal so a pending close
timeout prevents duplicate close handling: when Escape is pressed during the 200
ms close period, do not call closeModal again or emit another close event. Use
the existing close-timeout state as the guard while preserving normal Escape
behavior once no close is pending.
- Around line 114-129: Update _createSVG to stop assigning a hardcoded fill to
the path, allowing the path to inherit the parent SVG’s fill. Preserve the
caller-provided attrs behavior so platform icon creation can pass fill: "white"
and render the glyph with that color.
- Around line 213-226: Move the static `platforms` map out of
`buildPlatformButtons` into a module-level constant, preserving all platform
names, colors, and icon paths so it is allocated once and shared across
instances. Remove the method-local declaration, and add a brief inline comment
immediately before the loop that filters unknown platform keys explaining that
behavior.
- Around line 395-415: Update trapFocus and the modal entrance-focus logic to
use one shared helper for querying focusable elements, excluding disabled
controls. In trapFocus, detect when document.activeElement is outside this.modal
and move focus to the first focusable element while preventing the Tab event;
retain existing first/last wrapping behavior. Replace the duplicated selector
near the entrance-focus call site with the shared helper.

In `@tests/social-share-button.test.js`:
- Around line 346-350: Remove the unused platformCount declaration in
tests/social-share-button.test.js lines 346-350, or use a platforms variable to
assert the rendered button count against platforms.length. Also remove the
unused button variable at lines 424-427, or use it in an assertion confirming
document.body no longer contains it after destroy().
- Around line 227-278: Add a test alongside the existing focus-trap tests that
opens the modal, ensures document.activeElement is outside the modal, dispatches
a bubbling cancelable Tab keydown, and asserts focus is moved into the modal.
Use the existing createInstance and modal setup so trapFocus()’s outside-focus
behavior is covered.
- Around line 71-78: Update the sourceFile construction in the test to resolve
src/social-share-button.js relative to the test module’s import.meta.url rather
than process.cwd(), while preserving the existing file-read and innerHTML
assignment assertion.
- Around line 466-485: Replace the individual public-method existence tests
around createInstance with a single shape check, leaving behavioral coverage to
the method-specific tests. Add behavior coverage for updateOptions(), verifying
that calling updateOptions({ url }) updates the modal input value, using the
existing instance and modal setup helpers.
- Around line 200-230: Replace the real-time Promise delays in the focus and
lifecycle tests around openModal and closeModal with Vitest fake-timer
advancement, enabling fake timers for the test suite and advancing them by the
relevant 10ms or 200ms delays. Add vi.useRealTimers() to the shared afterEach
cleanup so fake timers are restored even when assertions fail.
- Line 26: Update the SocialShareButton assignment in the test setup to use only
mod.default, removing the window.SocialShareButton fallback so broken default
exports fail visibly and the ESLint error is resolved.
- Around line 398-410: Update the “should close modal when clicking the overlay
background” test to trigger the click with instance.modal.click() instead of
constructing and dispatching a MouseEvent or redefining its target. Keep the
existing closeModalSpy assertion unchanged.
- Around line 551-555: Update the test “should set fill='white' on platform icon
SVGs” to query the platform icon’s child path and assert its fill value reflects
the effective white paint, rather than asserting only the parent SVG’s fill
attribute. Use the existing _createSVG behavior as the reference and preserve
the current WhatsApp instance setup.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70854373-404c-4c99-9fe7-3520fdb0163f

📥 Commits

Reviewing files that changed from the base of the PR and between 11d3931 and ec85432.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • package.json
  • src/social-share-button.js
  • tests/social-share-button.test.js
  • vitest.config.js

Comment thread src/social-share-button.js
Comment thread src/social-share-button.js
Comment thread src/social-share-button.js Outdated
Comment thread src/social-share-button.js
Comment thread src/social-share-button.js
Comment thread tests/social-share-button.test.js Outdated
Comment thread tests/social-share-button.test.js Outdated
Comment thread tests/social-share-button.test.js
Comment thread tests/social-share-button.test.js Outdated
Comment on lines +551 to +555
it("should set fill='white' on platform icon SVGs", () => {
const instance = createInstance({ platforms: ["whatsapp"] });
const platformSvg = instance.modal.querySelector(".social-share-platform-icon svg");
expect(platformSvg.getAttribute("fill")).toBe("white");
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

This assertion does not prove the platform icon renders white.

The test checks fill on the <svg> only. _createSVG() also sets fill="currentColor" on the child <path>, and a fill attribute on the path overrides inheritance from the parent <svg>. The icon therefore does not render white, yet this test passes. See the _createSVG() comment in src/social-share-button.js.

Assert the effective paint on the <path>.

💚 Proposed change
   it("should set fill='white' on platform icon SVGs", () => {
     const instance = createInstance({ platforms: ["whatsapp"] });
     const platformSvg = instance.modal.querySelector(".social-share-platform-icon svg");
     expect(platformSvg.getAttribute("fill")).toBe("white");
+    // The path must not override the parent fill, otherwise the icon is not white.
+    expect(platformSvg.querySelector("path").getAttribute("fill")).toBeNull();
   });
📝 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.

Suggested change
it("should set fill='white' on platform icon SVGs", () => {
const instance = createInstance({ platforms: ["whatsapp"] });
const platformSvg = instance.modal.querySelector(".social-share-platform-icon svg");
expect(platformSvg.getAttribute("fill")).toBe("white");
});
it("should set fill='white' on platform icon SVGs", () => {
const instance = createInstance({ platforms: ["whatsapp"] });
const platformSvg = instance.modal.querySelector(".social-share-platform-icon svg");
expect(platformSvg.getAttribute("fill")).toBe("white");
// The path must not override the parent fill, otherwise the icon is not white.
expect(platformSvg.querySelector("path").getAttribute("fill")).toBeNull();
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/social-share-button.test.js` around lines 551 - 555, Update the test
“should set fill='white' on platform icon SVGs” to query the platform icon’s
child path and assert its fill value reflects the effective white paint, rather
than asserting only the parent SVG’s fill attribute. Use the existing _createSVG
behavior as the reference and preserve the current WhatsApp instance setup.

@gitcordapp

gitcordapp Bot commented Aug 7, 2026

Copy link
Copy Markdown

Link your account with Gitcord

Thanks for opening this PR, @maheshMadiwalar18!

To receive Discord notifications and contributor tracking for this organization:

  1. Join Discord: https://discord.gg/hjUhu33uAn
  2. In Discord, run /link maheshMadiwalar18
  3. Paste the verification code into your GitHub bio (or a public gist)
  4. Click Verify in Discord (or run /verify-link maheshMadiwalar18)

Once linked, Gitcord can notify you about reviews, merges, and more.

Posted by Gitcord

@github-actions github-actions Bot added no-issue-linked PR is not linked to any issue ci-cd CI/CD pipeline changes configuration Configuration file changes dependencies Dependency file changes documentation Changes to documentation files javascript JavaScript/TypeScript code changes tests Test file changes size/XL Extra large PR (>500 lines changed) first-time-contributor First PR of an external contributor needs-review labels Aug 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/social-share-button.js (1)

383-399: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Handle keyboard events only for the topmost modal.

Each open instance receives the same document keydown event. If two modals are open, one Escape key calls closeModal() for both instances. Each instance also emits a close event and attempts focus restoration.

SocialShareButton.openModalCount already supports concurrent modals. Track an active-modal stack, and let only the topmost modal process Escape and Tab events. Remove the instance from that stack during close and destroy. Add a test that opens two instances and verifies that Escape closes only the topmost modal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/social-share-button.js` around lines 383 - 399, Update the keyboard
handling around handleKeydown to process Escape and Tab only when the instance
is the topmost entry in a shared active-modal stack, using
SocialShareButton.openModalCount as the existing concurrent-modal state. Push
instances when opening, remove them during closeModal and destroy, and ensure
stack cleanup remains correct across all close paths. Add a test that opens two
instances and verifies Escape closes only the topmost modal.
tests/social-share-button.test.js (1)

279-295: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test the pending-close Escape guard.

This test verifies one Escape event only. It does not verify the branch added for a second Escape during the 200 ms close transition.

Dispatch Escape twice before advancing timers. Assert that closeModal() or the close analytics event occurs once.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/social-share-button.test.js` around lines 279 - 295, Extend the “should
close the modal on Escape key” test around createInstance and the Escape
dispatches to send a second Escape before advancing the 200 ms close-transition
timer, then assert that closeModal or its close analytics event is triggered
exactly once while the close is pending.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/social-share-button.js`:
- Line 52: Update the timeout callbacks in both copyLink() and fallbackCopy() to
restore the button text from this.options.copyButtonText instead of the
hardcoded "Copy" value, preserving custom and localized labels after copy
feedback. Add coverage verifying a non-default label remains intact after
successful and failed copy operations.

---

Outside diff comments:
In `@src/social-share-button.js`:
- Around line 383-399: Update the keyboard handling around handleKeydown to
process Escape and Tab only when the instance is the topmost entry in a shared
active-modal stack, using SocialShareButton.openModalCount as the existing
concurrent-modal state. Push instances when opening, remove them during
closeModal and destroy, and ensure stack cleanup remains correct across all
close paths. Add a test that opens two instances and verifies Escape closes only
the topmost modal.

In `@tests/social-share-button.test.js`:
- Around line 279-295: Extend the “should close the modal on Escape key” test
around createInstance and the Escape dispatches to send a second Escape before
advancing the 200 ms close-transition timer, then assert that closeModal or its
close analytics event is triggered exactly once while the close is pending.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 550b4ed1-2ebe-4b45-b42d-8a94b6f10077

📥 Commits

Reviewing files that changed from the base of the PR and between ec85432 and ae7d55a.

⛔ Files ignored due to path filters (1)
  • landing-page/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (4)
  • .github/pr-body.md
  • landing-page/tsconfig.json
  • src/social-share-button.js
  • tests/social-share-button.test.js

modalTitleText: options.modalTitleText || "Share",
closeButtonText: options.closeButtonText || "Close",
urlInputAriaLabel: options.urlInputAriaLabel || "URL to share",
copyButtonText: options.copyButtonText || "Copy",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve the configured copy label after feedback.

Line 52 configures copyButtonText, but copyLink() and fallbackCopy() reset the button text to the hardcoded "Copy" value. A localized or customized label is lost after a successful or failed copy operation.

Reset the button with this.options.copyButtonText. Add coverage for a non-default label.

Proposed fix
-          copyBtn.textContent = "Copy";
+          copyBtn.textContent = this.options.copyButtonText;

Apply the same replacement in both fallbackCopy() timeout callbacks.

📝 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.

Suggested change
copyButtonText: options.copyButtonText || "Copy",
copyBtn.textContent = this.options.copyButtonText;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/social-share-button.js` at line 52, Update the timeout callbacks in both
copyLink() and fallbackCopy() to restore the button text from
this.options.copyButtonText instead of the hardcoded "Copy" value, preserving
custom and localized labels after copy feedback. Add coverage verifying a
non-default label remains intact after successful and failed copy operations.

@github-actions

Copy link
Copy Markdown

⚠️ This PR has merge conflicts.

Please resolve the merge conflicts before review.

Your PR will only be reviewed by a maintainer after all conflicts have been resolved.

📺 Watch this video to understand why conflicts occur and how to resolve them:
https://www.youtube.com/watch?v=Sqsz1-o7nXk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci-cd CI/CD pipeline changes configuration Configuration file changes dependencies Dependency file changes documentation Changes to documentation files first-time-contributor First PR of an external contributor javascript JavaScript/TypeScript code changes needs-review no-issue-linked PR is not linked to any issue PR has merge conflicts size/XL Extra large PR (>500 lines changed) tests Test file changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant