Skip to content

feat: add native Web Component wrapper for SocialShareButton#108

Open
Muneerali199 wants to merge 3 commits intoAOSSIE-Org:mainfrom
Muneerali199:fix/web-component
Open

feat: add native Web Component wrapper for SocialShareButton#108
Muneerali199 wants to merge 3 commits intoAOSSIE-Org:mainfrom
Muneerali199:fix/web-component

Conversation

@Muneerali199
Copy link
Copy Markdown
Contributor

@Muneerali199 Muneerali199 commented Mar 16, 2026

Addressed Issues:

Fixes #50

Screenshots/Recordings:

N/A (library code only)

Additional Notes:

This PR adds a native Web Component wrapper for SocialShareButton.

Features:

  • Native Custom Element - Uses standard Web Component API
  • Framework-agnostic - Works in any HTML page or framework
  • Shadow DOM - Uses Shadow DOM for style encapsulation
  • Reactive attributes - Automatically updates when attributes change

Usage:

<script src="social-share-button.js"></script>
<script src="social-share-button-webcomponent.js"></script>

<social-share-button 
  url="https://example.com"
  title="My Page"
  platforms="whatsapp,facebook,twitter"
></social-share-button>

Checklist

  • My code follows the project's code style and conventions
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contributing Guidelines

AI Usage Disclosure

  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy.

Summary by CodeRabbit

  • New Features

    • Introduced Web Component wrapper enabling framework-agnostic integration of social sharing functionality
    • Added analytics configuration options to customize tracking and event handling behavior
  • Documentation

    • Updated code examples, guides, and configuration documentation with improved formatting and consistency
  • Style

    • Standardized quote conventions across codebase for consistency

- Created social-share-button-webcomponent.js with a native Custom Element
- No external dependencies (pure vanilla Web Component)
- Can be used as <social-share-button> HTML tag in any framework
- Supports all SocialShareButton options as attributes/properties
- Uses Shadow DOM for style encapsulation

Fixes AOSSIE-Org#50
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 16, 2026

🚥 Pre-merge checks | ✅ 2 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements most requirements from issue #50: creates a Web Component wrapper with attribute mapping, lifecycle management, and Shadow DOM usage. However, documentation updates in index.html and README are not completed per the issue checklist. Complete the documentation requirements: update index.html with Web Component Integration section including copy-to-clipboard buttons, and update README to list Web Components as a supported integration.
Out of Scope Changes check ⚠️ Warning Changes include extensive quote style normalization (double to single quotes) across multiple files that appear to be out-of-scope code formatting standardization unrelated to the Web Component feature implementation. Separate quote standardization changes into a dedicated formatting/style PR, keeping this PR focused solely on Web Component wrapper implementation and required documentation.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a native Web Component wrapper for SocialShareButton, which is the primary objective of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (2)
.github/copilot/integrate-analytics.prompt.md (1)

287-296: ⚠️ Potential issue | 🟡 Minor

Remove stray diff markers from the consent snippet.

Line 287 and surrounding lines include literal + prefixes, so copied code is invalid JS.

Suggested fix
-+const shareButtonInstance = new SocialShareButton({
-+  container: "#share-button",
-+  analytics: false,
-+});
-+
-+// Activate only after CMP consent
+const shareButtonInstance = new SocialShareButton({
+  container: '#share-button',
+  analytics: false,
+});
+
+// Activate only after CMP consent
 consentManager.onConsent("analytics", () => {
   shareButtonInstance.updateOptions({
-+    analytics: true,
+    analytics: true,
     analyticsPlugins: [new GoogleAnalyticsAdapter()],
   });
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/copilot/integrate-analytics.prompt.md around lines 287 - 296, The
snippet contains stray leading '+' characters making invalid JS; remove the '+'
prefixes from the lines that instantiate shareButtonInstance and that call
updateOptions inside consentManager.onConsent so the code reads as valid
JavaScript: ensure the SocialShareButton instantiation (shareButtonInstance =
new SocialShareButton({...})), the consentManager.onConsent("analytics", ...)
handler, the call shareButtonInstance.updateOptions({ analytics: true,
analyticsPlugins: [new GoogleAnalyticsAdapter()] }) and the
GoogleAnalyticsAdapter reference are plain tokens (no '+' characters) and
properly formatted.
src/social-share-button.js (1)

723-750: ⚠️ Potential issue | 🟠 Major

Replace silent analytics catch blocks with debug-gated warnings via a helper.

_emit() currently swallows failures across all three delivery paths. That keeps the share flow non-fatal, but it also hides broken analytics integrations and makes diagnosis difficult.

Proposed fix
+  _debugWarn(message, err) {
+    if (!this.options.debug) return;
+    // eslint-disable-next-line no-console
+    console.warn(`[SocialShareButton Analytics] ${message}`, err);
+  }
+
   _emit(eventName, interactionType, extra = {}) {
@@
     if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
       try {
         const domEvent = new CustomEvent('social-share', {
@@
         const el = this._getContainer();
         (el || document).dispatchEvent(domEvent);
-      } catch (_) {}
+      } catch (err) {
+        this._debugWarn('CustomEvent dispatch failed', err);
+      }
     }
@@
     if (typeof this.options.onAnalytics === 'function') {
       try {
         this.options.onAnalytics(payload);
-      } catch (_) {}
+      } catch (err) {
+        this._debugWarn('onAnalytics callback failed', err);
+      }
     }
@@
       for (const plugin of this.options.analyticsPlugins) {
         if (plugin && typeof plugin.track === 'function') {
           try {
             plugin.track(payload);
-          } catch (_) {}
+          } catch (err) {
+            this._debugWarn('analytics plugin.track failed', err);
+          }
         }
       }
     }
   }

Based on learnings: In src/social-share-button.js, empty catch blocks in _emit() should be replaced with debug-conditional warnings, and debug console usage should be centralized through a helper.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-button.js` around lines 723 - 750, The _emit() method
currently swallows all exceptions in its three delivery paths (CustomEvent
dispatch, this.options.onAnalytics callback, and each plugin.track in
this.options.analyticsPlugins); create a centralized debug helper (e.g.,
_debugWarn or warnAnalytics) on the class that accepts a contextual message and
an Error and only logs a console.warn when debugging is enabled, then replace
every empty catch block inside _emit() to call that helper with a clear message
(e.g., "social-share: failed to dispatch event", "social-share: onAnalytics
threw", "social-share: plugin.track threw" along with plugin identity when
available), preserving the non-fatal behavior but surface the error details for
diagnosis.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/copilot/integrate-social-share-button.prompt.md:
- Around line 28-33: Add a new "Method 4 — Web Component" entry that documents
using the native <social-share-button> web component and explicitly instructs
how to obtain the new wrapper/source file (e.g., link to the package repo or the
specific distribution file in the new PR, or how to import it from the npm
package) and update the existing CDN guidance in "Method 1" to avoid referencing
legacy CDN URLs by including a note that older CDN releases do not contain the
new wrapper; reference the existing headings "Method 1 — CDN (Recommended)",
"Method 2 — npm", and "Method 3 — React Wrapper Component (Optional)" so the new
method sits alongside them and ensure the prompt tells users exactly where to
fetch the new wrapper file and the import/usage snippet for
<social-share-button>.

In `@README.md`:
- Around line 85-93: Add a new "Web Component Integration" section to the README
that explains how to load the web-component bundle by referencing
src/social-share-button-webcomponent.js (e.g., via CDN link to that specific
file) and show a usage snippet with the <social-share-button> element; do not
tell users to use the pre-wrapper tag for this file, and explicitly state that
the webcomponent file must be loaded separately from the core CSS/JS if using
the CDN so users can acquire src/social-share-button-webcomponent.js directly.

In `@src/social-share-button-webcomponent.js`:
- Around line 149-155: The polling interval leaks because
_initSocialShareButton() always starts polling and stores the timer only in a
local variable; add this._checkLibInterval = null in the constructor, change the
local interval to this._checkLibInterval and only start it when this.autoDetect
is true, and in _destroySocialShareButton() call
clearInterval(this._checkLibInterval) and set this._checkLibInterval = null to
stop and release the timer; also add brief inline comments in the constructor,
_initSocialShareButton, and _destroySocialShareButton explaining the timer
lifecycle and the auto-detect guard so future readers understand why the
interval is started/cleared and to avoid reinitializing after disconnect.
- Around line 16-30: Add the missing attributes and implement customClass:
include 'analytics', 'component-id' and 'debug' in the static observedAttributes
array so attributeChangedCallback will fire for them; add a property
getter/setter for customClass on the web component class (mirroring the pattern
used by other props) and map its attribute name in _getOptions() so the
underlying instance receives it; ensure attributeChangedCallback calls
_updateOptions() for these attributes (observedAttributes,
attributeChangedCallback, _getOptions, _updateOptions, customClass, analytics,
component-id, debug).
- Around line 39-42: The button inside the shadow root is unstyled because
external CSS cannot cross the shadow boundary; update the component
initialization (where this._shadow is created via this.attachShadow) to inject
the stylesheet into the shadow DOM — either create and append a <link
rel="stylesheet" href="..."> or load the CSS and append a <style> element into
this._shadow (near the code that sets this._shadow.innerHTML / the container
div) so classes like .social-share-btn and .primary take effect inside the
shadow root.
- Around line 15-16: The module currently extends HTMLElement in the
SocialShareButtonElement class and calls customElements at module load, which
breaks SSR; wrap both uses with runtime guards: only define/extend when typeof
HTMLElement !== 'undefined' (e.g., const SocialShareButtonElement = typeof
HTMLElement !== 'undefined' ? class SocialShareButtonElement extends HTMLElement
{ ... } : class SocialShareButtonElement {} ) and only call
customElements.define/get when typeof customElements !== 'undefined' (e.g., if
(typeof customElements !== 'undefined' &&
!customElements.get('social-share-button'))
customElements.define('social-share-button', SocialShareButtonElement)); ensure
the guarded symbols are SocialShareButtonElement and the customElements checks
so the module can be imported in Node without ReferenceError.

---

Outside diff comments:
In @.github/copilot/integrate-analytics.prompt.md:
- Around line 287-296: The snippet contains stray leading '+' characters making
invalid JS; remove the '+' prefixes from the lines that instantiate
shareButtonInstance and that call updateOptions inside consentManager.onConsent
so the code reads as valid JavaScript: ensure the SocialShareButton
instantiation (shareButtonInstance = new SocialShareButton({...})), the
consentManager.onConsent("analytics", ...) handler, the call
shareButtonInstance.updateOptions({ analytics: true, analyticsPlugins: [new
GoogleAnalyticsAdapter()] }) and the GoogleAnalyticsAdapter reference are plain
tokens (no '+' characters) and properly formatted.

In `@src/social-share-button.js`:
- Around line 723-750: The _emit() method currently swallows all exceptions in
its three delivery paths (CustomEvent dispatch, this.options.onAnalytics
callback, and each plugin.track in this.options.analyticsPlugins); create a
centralized debug helper (e.g., _debugWarn or warnAnalytics) on the class that
accepts a contextual message and an Error and only logs a console.warn when
debugging is enabled, then replace every empty catch block inside _emit() to
call that helper with a clear message (e.g., "social-share: failed to dispatch
event", "social-share: onAnalytics threw", "social-share: plugin.track threw"
along with plugin identity when available), preserving the non-fatal behavior
but surface the error details for diagnosis.
🪄 Autofix (Beta)

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

Run ID: 4d07e849-14a1-49a8-81f8-d92a8b432e2c

📥 Commits

Reviewing files that changed from the base of the PR and between 9e7e113 and e65c9f4.

📒 Files selected for processing (10)
  • .github/copilot/integrate-analytics.prompt.md
  • .github/copilot/integrate-social-share-button.prompt.md
  • README.md
  • docs/client-guide.md
  • eslint.config.js
  • src/social-share-analytics.js
  • src/social-share-button-react.jsx
  • src/social-share-button-webcomponent.js
  • src/social-share-button.css
  • src/social-share-button.js

Comment on lines +28 to 33
| Method | When to use |
| ------------------------------------------------- | ------------------------------------------------------------------------- |
| **Method 1 — CDN (Recommended)** | Most projects. No build step needed. Load via `<script>` tag. |
| **Method 2 — npm** | Bundler-based projects (Webpack, Vite, etc.) that prefer `import` syntax. |
| **Method 3 — React Wrapper Component (Optional)** | Developer explicitly wants a reusable JSX component. |

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.

⚠️ Potential issue | 🟠 Major

Integration skill is missing a Web Component path and correct wrapper acquisition guidance.

This prompt currently routes users through only three methods and hardcoded legacy CDN URLs, so it cannot generate valid guidance for the new native <social-share-button> wrapper.

Based on learnings: New wrapper/source files are not available in CDN versions that predate the PR; docs/prompts must include explicit acquisition instructions for the new wrapper file.

Also applies to: 52-57

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/copilot/integrate-social-share-button.prompt.md around lines 28 -
33, Add a new "Method 4 — Web Component" entry that documents using the native
<social-share-button> web component and explicitly instructs how to obtain the
new wrapper/source file (e.g., link to the package repo or the specific
distribution file in the new PR, or how to import it from the npm package) and
update the existing CDN guidance in "Method 1" to avoid referencing legacy CDN
URLs by including a note that older CDN releases do not contain the new wrapper;
reference the existing headings "Method 1 — CDN (Recommended)", "Method 2 —
npm", and "Method 3 — React Wrapper Component (Optional)" so the new method sits
alongside them and ensure the prompt tells users exactly where to fetch the new
wrapper file and the import/usage snippet for <social-share-button>.

Comment on lines 85 to 93
### Via CDN (Recommended)

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/AOSSIE-Org/SocialShareButton@v1.0.3/src/social-share-button.css">
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/AOSSIE-Org/SocialShareButton@v1.0.3/src/social-share-button.css"
/>
<script src="https://cdn.jsdelivr.net/gh/AOSSIE-Org/SocialShareButton@v1.0.3/src/social-share-button.js"></script>
```
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.

⚠️ Potential issue | 🟠 Major

Web Component wrapper is undocumented in installation guidance.

This PR adds a new wrapper, but the README still only shows core JS/CSS loading. Please add a dedicated “Web Component Integration” section with how to load src/social-share-button-webcomponent.js (and a <social-share-button> usage snippet). Also avoid pointing users to a pre-wrapper tag for that file.

Based on learnings: In AOSSIE-Org/SocialShareButton, new wrapper files are not part of older CDN tags; docs must include acquisition instructions for the new wrapper file itself.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 85 - 93, Add a new "Web Component Integration"
section to the README that explains how to load the web-component bundle by
referencing src/social-share-button-webcomponent.js (e.g., via CDN link to that
specific file) and show a usage snippet with the <social-share-button> element;
do not tell users to use the pre-wrapper tag for this file, and explicitly state
that the webcomponent file must be loaded separately from the core CSS/JS if
using the CDN so users can acquire src/social-share-button-webcomponent.js
directly.

Comment on lines +15 to +16
class SocialShareButtonElement extends HTMLElement {
static get observedAttributes() {
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "social-share-button-webcomponent.js" -type f

Repository: AOSSIE-Org/SocialShareButton

Length of output: 113


🏁 Script executed:

git ls-files | grep -i webcomponent

Repository: AOSSIE-Org/SocialShareButton

Length of output: 111


🏁 Script executed:

git ls-files src/ | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 246


🏁 Script executed:

git ls-files | grep "social-share" | sort

Repository: AOSSIE-Org/SocialShareButton

Length of output: 286


🏁 Script executed:

ls -la src/ 2>/dev/null || echo "src directory check"; git ls-files | grep -E "^src/" | head -30

Repository: AOSSIE-Org/SocialShareButton

Length of output: 820


🏁 Script executed:

find . -type f -name "*.js" -o -name "*.jsx" | grep -E "(social|share)" | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 210


🏁 Script executed:

pwd && ls -la

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1528


🏁 Script executed:

cat README.md 2>/dev/null | head -50 || echo "Checking repo root"; ls -1

Repository: AOSSIE-Org/SocialShareButton

Length of output: 2169


🏁 Script executed:

find . -name "*webcomponent*" 2>/dev/null

Repository: AOSSIE-Org/SocialShareButton

Length of output: 113


🏁 Script executed:

ls -1 src/social-share-button* 2>/dev/null || echo "No files found"; git ls-files 2>/dev/null | grep social

Repository: AOSSIE-Org/SocialShareButton

Length of output: 444


🏁 Script executed:

git log --oneline -1 2>/dev/null && git status 2>/dev/null | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 368


🏁 Script executed:

git diff --stat HEAD 2>/dev/null | head -30 || echo "Checking working directory"; find . -type f \( -name "*.js" -o -name "*.json" \) -mmin -120 2>/dev/null | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 280


🏁 Script executed:

git diff --cached --name-only 2>/dev/null | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 54


🏁 Script executed:

git rev-parse --show-toplevel 2>/dev/null && echo "---" && ls -1

Repository: AOSSIE-Org/SocialShareButton

Length of output: 261


🏁 Script executed:

ls -la src/ 2>/dev/null && echo "---FILES IN SRC---" && ls -1 src/

Repository: AOSSIE-Org/SocialShareButton

Length of output: 815


🏁 Script executed:

wc -l src/social-share-button-webcomponent.js && cat -n src/social-share-button-webcomponent.js | head -50

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1647


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '1,30p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1035


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '240,250p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 344


🏁 Script executed:

grep -n "HTMLElement\|typeof.*undefined\|window\." src/social-share-button.js | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1012


🏁 Script executed:

grep -r "typeof HTMLElement" src/ || echo "No HTMLElement guard found"

Repository: AOSSIE-Org/SocialShareButton

Length of output: 98


🏁 Script executed:

ls -1 src/social-share-button*.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 138


🏁 Script executed:

head -20 src/social-share-button-react.jsx

Repository: AOSSIE-Org/SocialShareButton

Length of output: 714


🏁 Script executed:

grep -n "typeof window\|typeof document\|customElements" src/social-share-button.js | head -15

Repository: AOSSIE-Org/SocialShareButton

Length of output: 767


🏁 Script executed:

cat src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 5842


🏁 Script executed:

head -40 package.json

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1168


🏁 Script executed:

cat index.js 2>/dev/null | head -30

Repository: AOSSIE-Org/SocialShareButton

Length of output: 54


🏁 Script executed:

grep -E "main|export" package.json | head -10

Repository: AOSSIE-Org/SocialShareButton

Length of output: 111


🏁 Script executed:

ls -1 *.js *.mjs 2>/dev/null

Repository: AOSSIE-Org/SocialShareButton

Length of output: 88


🏁 Script executed:

find . -maxdepth 2 -name "index.*" -type f | grep -v node_modules

Repository: AOSSIE-Org/SocialShareButton

Length of output: 84


🏁 Script executed:

python3 << 'EOF'
# Test the proposed SSR guard pattern logic
test_cases = [
    {"env": "Browser", "HTMLElement": "function", "customElements": "object"},
    {"env": "Node.js", "HTMLElement": "undefined", "customElements": "undefined"},
]

for case in test_cases:
    print(f"\nEnvironment: {case['env']}")
    # Simulate the proposed fix
    html_element_exists = case['HTMLElement'] != 'undefined'
    custom_elements_exists = case['customElements'] != 'undefined'
    
    if html_element_exists:
        base_class = "HTMLElement"
        print(f"  BaseHTMLElement = HTMLElement")
    else:
        base_class = "class {{}}"
        print(f"  BaseHTMLElement = class {{}}")
    
    # Check if the class definition would work
    print(f"  class SocialShareButtonElement extends {base_class}: ✓ Would work")
    
    # Check customElements registration
    print(f"  typeof window !== 'undefined': {html_element_exists}")
    print(f"  'customElements' in window: {custom_elements_exists}")
    print(f"  Would register: {html_element_exists and custom_elements_exists}")
EOF

Repository: AOSSIE-Org/SocialShareButton

Length of output: 504


🏁 Script executed:

grep -r "webcomponent\|social-share-button-webcomponent" . --include="*.js" --include="*.json" --include="*.md" 2>/dev/null | grep -v ".git" | head -20

Repository: AOSSIE-Org/SocialShareButton

Length of output: 54


🏁 Script executed:

cat package.json | grep -A 10 "scripts"

Repository: AOSSIE-Org/SocialShareButton

Length of output: 468


🏁 Script executed:

grep -A 5 -B 5 -i "webcomponent\|ssr\|server" README.md 2>/dev/null | head -30

Repository: AOSSIE-Org/SocialShareButton

Length of output: 416


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '10,25p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 546


🏁 Script executed:

node -c << 'EOF'
// Test if the proposed fix syntax is valid
const BaseHTMLElement = typeof HTMLElement !== 'undefined' ? HTMLElement : class {};

class SocialShareButtonElement extends BaseHTMLElement {
  static get observedAttributes() {
    return ['networks', 'url', 'title', 'text'];
  }
}

if (
  typeof window !== 'undefined' &&
  'customElements' in window &&
  !window.customElements.get('social-share-button')
) {
  window.customElements.define('social-share-button', SocialShareButtonElement);
}

console.log("Syntax check passed");
EOF

Repository: AOSSIE-Org/SocialShareButton

Length of output: 54


Guard HTMLElement usage to prevent SSR module loading errors.

Line 15 extends HTMLElement without checking availability. This throws ReferenceError in Node.js/SSR environments where HTMLElement is undefined, making the entire module unimportable on the server. Line 244 also accesses customElements directly without verifying window.

Apply the guard pattern to both the class definition and the registration check:

Recommended fix
+const BaseHTMLElement = typeof HTMLElement !== 'undefined' ? HTMLElement : class {};
 
-class SocialShareButtonElement extends HTMLElement {
+class SocialShareButtonElement extends BaseHTMLElement {
   static get observedAttributes() {
-if (typeof window !== 'undefined' && !customElements.get('social-share-button')) {
-  customElements.define('social-share-button', SocialShareButtonElement);
+if (
+  typeof window !== 'undefined' &&
+  'customElements' in window &&
+  !window.customElements.get('social-share-button')
+) {
+  window.customElements.define('social-share-button', SocialShareButtonElement);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-button-webcomponent.js` around lines 15 - 16, The module
currently extends HTMLElement in the SocialShareButtonElement class and calls
customElements at module load, which breaks SSR; wrap both uses with runtime
guards: only define/extend when typeof HTMLElement !== 'undefined' (e.g., const
SocialShareButtonElement = typeof HTMLElement !== 'undefined' ? class
SocialShareButtonElement extends HTMLElement { ... } : class
SocialShareButtonElement {} ) and only call customElements.define/get when
typeof customElements !== 'undefined' (e.g., if (typeof customElements !==
'undefined' && !customElements.get('social-share-button'))
customElements.define('social-share-button', SocialShareButtonElement)); ensure
the guarded symbols are SocialShareButtonElement and the customElements checks
so the module can be imported in Node without ReferenceError.

Comment on lines +16 to +30
static get observedAttributes() {
return [
'url',
'title',
'description',
'hashtags',
'via',
'platforms',
'theme',
'button-text',
'button-style',
'modal-position',
'auto-detect',
];
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

wc -l src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 115


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | head -80

Repository: AOSSIE-Org/SocialShareButton

Length of output: 2437


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '180,210p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1202


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '130,185p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1587


🏁 Script executed:

grep -n "get analytics\|set analytics\|get componentId\|set componentId\|get customClass\|set customClass" src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 181


🏁 Script executed:

grep -n "customClass\|custom-class" src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 54


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | tail -70

Repository: AOSSIE-Org/SocialShareButton

Length of output: 2449


Add missing attributes to observedAttributes and implement the customClass property.

analytics, component-id, and debug are defined as properties and passed to _getOptions(), but they're not in observedAttributes (lines 16–30). This prevents post-init attribute updates from triggering attributeChangedCallback() and propagating to the underlying instance via _updateOptions(). Additionally, customClass is missing entirely from the component API—no property getter/setter or attribute mapping in _getOptions() (lines 185–203).

Proposed fix
   static get observedAttributes() {
     return [
       'url',
       'title',
       'description',
       'hashtags',
       'via',
       'platforms',
       'theme',
       'button-text',
       'button-style',
       'modal-position',
       'auto-detect',
+      'analytics',
+      'component-id',
+      'debug',
+      'custom-class',
     ];
   }
@@
+  get customClass() {
+    return this.getAttribute('custom-class') || '';
+  }
+
+  set customClass(value) {
+    if (value == null) {
+      this.removeAttribute('custom-class');
+    } else {
+      this.setAttribute('custom-class', String(value));
+    }
+  }
+
   _getOptions() {
     return {
       container: this._shadow.getElementById('container'),
@@
       buttonText: this.buttonText,
+      customClass: this.customClass,
       buttonStyle: this.buttonStyle,
       modalPosition: this.modalPosition,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-button-webcomponent.js` around lines 16 - 30, Add the
missing attributes and implement customClass: include 'analytics',
'component-id' and 'debug' in the static observedAttributes array so
attributeChangedCallback will fire for them; add a property getter/setter for
customClass on the web component class (mirroring the pattern used by other
props) and map its attribute name in _getOptions() so the underlying instance
receives it; ensure attributeChangedCallback calls _updateOptions() for these
attributes (observedAttributes, attributeChangedCallback, _getOptions,
_updateOptions, customClass, analytics, component-id, debug).

Comment on lines +39 to +42
if (!this._shadow) {
this._shadow = this.attachShadow({ mode: 'open' });
this._shadow.innerHTML = '<div id="container"></div>';
}
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.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | head -60

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1897


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '35,50p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 552


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | sed -n '180,210p'

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1202


🏁 Script executed:

# Search for style-related code in the webcomponent file
rg -n "style|css|inject|shadow" src/social-share-button-webcomponent.js -A 2 -B 2

Repository: AOSSIE-Org/SocialShareButton

Length of output: 867


🏁 Script executed:

# Look for the main SocialShareButton class
fd -t f "social-share-button.js" -not -path "*webcomponent*" -not -path "*preact*"

Repository: AOSSIE-Org/SocialShareButton

Length of output: 302


🏁 Script executed:

# Check how button styling is handled
rg -n "buttonStyle|style|css" src/social-share-button.js -A 3 -B 1 | head -80

Repository: AOSSIE-Org/SocialShareButton

Length of output: 3684


🏁 Script executed:

# Look for innerHTML, createElement patterns that build the button
rg -n "createElement|innerHTML|<button|<div" src/social-share-button.js -B 2 -A 2 | head -100

Repository: AOSSIE-Org/SocialShareButton

Length of output: 2426


🏁 Script executed:

# Find CSS or style files
fd -t f "\\.css$" src/

Repository: AOSSIE-Org/SocialShareButton

Length of output: 99


🏁 Script executed:

# Search for style injection, appendChild, or insertBefore that might inject CSS
rg "appendChild|insertBefore|style|<style" src/social-share-button.js | grep -i "style\|append\|insert"

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1882


🏁 Script executed:

# Check if there's a styles or css reference in the webcomponent file
rg -n "style|css|inject" src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 209


🏁 Script executed:

# List all files in src directory
ls -la src/

Repository: AOSSIE-Org/SocialShareButton

Length of output: 645


🏁 Script executed:

# Check entire webcomponent file for any style handling
cat -n src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 7578


🏁 Script executed:

# Look for stylesheet or style injection patterns
rg -n "link|href|stylesheet|style|css" src/social-share-button-webcomponent.js

Repository: AOSSIE-Org/SocialShareButton

Length of output: 395


🏁 Script executed:

# Check where button and modal are appended - more detailed context
rg -n "appendChild" src/social-share-button.js -B 3 -A 1

Repository: AOSSIE-Org/SocialShareButton

Length of output: 466


🏁 Script executed:

# Also check the CSS file to see what styles are defined
head -100 src/social-share-button.css

Repository: AOSSIE-Org/SocialShareButton

Length of output: 1991


The share button won't be styled due to shadow DOM boundary.

The button is rendered inside the shadow root with class names like .social-share-btn, .primary, etc., but the external stylesheet (src/social-share-button.css) cannot cross the shadow DOM boundary. The modal is appended to document.body and will render correctly, but the button inside the shadow DOM remains unstyled.

Inject the stylesheet into the shadow DOM by appending a <style> tag or <link> element during initialization, or copy relevant styles into the shadow root.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-button-webcomponent.js` around lines 39 - 42, The button
inside the shadow root is unstyled because external CSS cannot cross the shadow
boundary; update the component initialization (where this._shadow is created via
this.attachShadow) to inject the stylesheet into the shadow DOM — either create
and append a <link rel="stylesheet" href="..."> or load the CSS and append a
<style> element into this._shadow (near the code that sets
this._shadow.innerHTML / the container div) so classes like .social-share-btn
and .primary take effect inside the shadow root.

Comment on lines +149 to +155
get autoDetect() {
return this.getAttribute('auto-detect') === 'true';
}

set autoDetect(value) {
this.setAttribute('auto-detect', String(value));
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cd /tmp/repo 2>/dev/null || cd "$(find /root -type d -name 'SocialShareButton' -o -name 'social-share-button*' 2>/dev/null | head -1)" && pwd

Repository: AOSSIE-Org/SocialShareButton

Length of output: 90


🏁 Script executed:

find . -name "*social-share-button-webcomponent.js" -type f

Repository: AOSSIE-Org/SocialShareButton

Length of output: 113


🏁 Script executed:

cat -n src/social-share-button-webcomponent.js | head -250

Repository: AOSSIE-Org/SocialShareButton

Length of output: 7578


Polling interval leaks past disconnect and auto-detect is ignored.

_initSocialShareButton() always starts polling (lines 208–214) regardless of the auto-detect setting, and the interval handle is stored only as a local variable inside the else-if block. When the component is disconnected, the interval continues running and can initialize an instance after unmount. _destroySocialShareButton() (lines 224–229) does not clear the polling interval.

Add this._checkLibInterval = null to the constructor, store and conditionally clear the interval based on this.autoDetect, and clean up the interval in _destroySocialShareButton(). Also add inline comments explaining the timer lifecycle and auto-detect guard logic per coding guidelines for non-obvious edge cases and cleanup callbacks.

Proposed fix
   constructor() {
     super();
     this._instance = null;
     this._shadow = null;
+    this._checkLibInterval = null;
   }
@@
   _initSocialShareButton() {
+    if (this._instance) return;
+
     if (typeof window !== 'undefined' && window.SocialShareButton) {
       this._instance = new window.SocialShareButton(this._getOptions());
-    } else if (typeof window !== 'undefined') {
-      const checkLib = setInterval(() => {
+    } else if (typeof window !== 'undefined' && this.autoDetect) {
+      if (this._checkLibInterval) clearInterval(this._checkLibInterval);
+      this._checkLibInterval = setInterval(() => {
         if (window.SocialShareButton) {
-          clearInterval(checkLib);
+          clearInterval(this._checkLibInterval);
+          this._checkLibInterval = null;
           this._instance = new window.SocialShareButton(this._getOptions());
         }
       }, 100);
     }
   }
@@
   _destroySocialShareButton() {
+    if (this._checkLibInterval) {
+      clearInterval(this._checkLibInterval);
+      this._checkLibInterval = null;
+    }
     if (this._instance && typeof this._instance.destroy === 'function') {
       this._instance.destroy();
       this._instance = null;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/social-share-button-webcomponent.js` around lines 149 - 155, The polling
interval leaks because _initSocialShareButton() always starts polling and stores
the timer only in a local variable; add this._checkLibInterval = null in the
constructor, change the local interval to this._checkLibInterval and only start
it when this.autoDetect is true, and in _destroySocialShareButton() call
clearInterval(this._checkLibInterval) and set this._checkLibInterval = null to
stop and release the timer; also add brief inline comments in the constructor,
_initSocialShareButton, and _destroySocialShareButton explaining the timer
lifecycle and the auto-detect guard so future readers understand why the
interval is started/cleared and to avoid reinitializing after disconnect.

@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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhancement: Add Web Component / Lit wrapper — new custom element and demo page section

1 participant