Skip to content

CLXP-931: add Publish-on-LinkedIn button to certification badge - #3033

Open
silouone wants to merge 6 commits into
masterfrom
feat/clxp-linkedin-badge-share
Open

CLXP-931: add Publish-on-LinkedIn button to certification badge#3033
silouone wants to merge 6 commits into
masterfrom
feat/clxp-linkedin-badge-share

Conversation

@silouone

@silouone silouone commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

CLXP-931 · Publish your badge on LinkedIn

Adds an opt-in "Publish on LinkedIn" button to the badge reward section of the certification page — turning a private badge download into social proof and free brand reach (client request via Geoffrey).

🎫 Jira: CLXP-931 (under epic CLXP-87FY25 KTLO/TechDebt)
📋 Spec: specs/001-linkedin-badge-share/ (Specify → Plan → Tasks)

👀 Preview — ProgressWrapperBadgeWithLinkedin story

🇬🇧 English 🇫🇷 Français
Publish on LinkedIn — EN Publier sur LinkedIn — FR

A LinkedIn-blue button (#0A66C2) with the boxed LinkedIn logo (the "in" inside its rounded square — not bare letters) + white label, under the existing Download button. Only the en source string is committed; the 🇫🇷 shot shows the expected Transifex translation (see the i18n note below).

🔗 Interactive Storybook (Chromatic): ▶ open the LinkedIn story · full Storybook · certification-detail story
Chromatic build #295. The Storybook is access-gated to the Chromatic project (toggle "make Storybook public" in project settings for anonymous reviewers). The first baseline build also surfaced ~10 pre-existing component errors elsewhere in the library, unrelated to this PR.

What changed

  • molecule/progress-wrapper — renders a LinkedIn ButtonLink in the badge DetailSection when a non-empty linkedinShareUrl is provided and the certification is complete. Opens in a new tab (target="_blank", rel="noopener noreferrer"), fires the optional onLinkedinShareClick analytics callback without blocking navigation. Hidden while locked; badge section only (never the diploma).
  • branding — solid LinkedIn-blue button with the boxed LinkedIn logo (src/util/linkedin-icon.js, the "in" as negative space so the button blue shows through) — reads unmistakably as LinkedIn.
  • layout — the badge card grew from a fixed height: 114px to min-height and stacks the two CTAs in a flex column with an 8px gap. Single-button diploma/stars cards are visually unchanged.
  • template/certification-detail — threads two new optional props linkedinShareUrl + onLinkedinShareClick into the badge section.
  • atom/button-link — adds optional link.rel so rel reaches the <a> (via Link's {...restProps}).
  • i18n (Transifex)publish_on_linkedin is added to the en source only (locales/en/global.json; .tx/config source_lang=en). The transifex-integration bot translates the other 29 locales (incl. FR) after merge to master, so this PR does not hand-edit them. (An earlier revision that touched all 30 locales was reverted to match the established workflow — verified via .tx/config, the bot's Translate global.json in <lang> commits, and the en-only past feature Savoirsdavenir - wording Diplôma  #3021.)

Validation + how it's consumed (MOOC side)

Validated end-to-end: clicking the button opens LinkedIn's share composer for the logged-in user in a new tab — the component → anchor → LinkedIn flow works.

The component is presentational and never builds the URL. For a rich share (a badge preview card rather than an empty composer), the MOOC must inject
https://www.linkedin.com/sharing/share-offsite/?url=<public cert page> where that page exposes Open Graph tags (og:image = badge, og:title = cert name, og:description). Without OG tags LinkedIn opens an empty composer — which is exactly what the demo fixture's placeholder URL does (expected). LinkedIn deprecated the profile/add certification autofill, so share-offsite + an OG-tagged public page is the recommended path (spec §7). Plus the optional onLinkedinShareClick analytics handler.

Tests & verification

  • New TDD coverage: progress-wrapper/test/linkedin-share.tsx (7 cases), certification-detail/test/linkedin-share.tsx (2), src/test/publish-on-linkedin-key.js (30-locale parity guard). All green; existing snapshots/regressions unchanged.
  • eslint . → 0 new errors · tsc --noEmit (strict) → 0 errors.
  • Adversarially verified by 5 independent review lenses (EARS, edge cases, a11y/security, backward-compat/i18n, bug-hunt) → 0 defects.

Checklist

  • en Transifex source edited; the other 29 locales delivered by Transifex (not hand-edited)
  • New props optional & additive — default render unchanged (backward compatible)
  • rel="noopener noreferrer" + target="_blank" on the external link
  • aria-label + data-name="publish-linkedin-button" for a11y / E2E
  • Lint + strict typecheck clean
  • LinkedIn-blue branding, boxed LinkedIn logo, badge renders in the story
  • ⏸️ Canary publish held until after review (per decision)

🤖 Generated with Claude Code

Add an opt-in "Publish on LinkedIn" button to the badge reward section of the
certification page (molecule/progress-wrapper + template/certification-detail).

- progress-wrapper: render a LinkedIn ButtonLink in the badge DetailSection when
  a non-empty linkedinShareUrl is set & the certification is complete; opens in a
  new tab with rel="noopener noreferrer" and fires the optional onLinkedinShareClick
  for analytics. Hidden while locked; badge section only (never the diploma).
- certification-detail: thread optional linkedinShareUrl + onLinkedinShareClick
  props into the badge section.
- button-icons: register the existing LinkedIn nova-icon (type: 'linkedin').
- button-link: add optional link.rel so it reaches the <a> via Link's restProps.
- i18n: add publish_on_linkedin to all 30 locales (FR/EN real, EN fallback).
- tests: TDD coverage - progress-wrapper (7), certification-detail (2), and a
  30-locale parity guard. Additive and backward-compatible.

Spec: specs/001-linkedin-badge-share/

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.40%. Comparing base (962f01b) to head (4646841).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #3033   +/-   ##
=======================================
  Coverage   94.40%   94.40%           
=======================================
  Files         559      560    +1     
  Lines        9717     9723    +6     
  Branches      601      601           
=======================================
+ Hits         9173     9179    +6     
  Misses        440      440           
  Partials      104      104           
Files with missing lines Coverage Δ
...rpacademy-components/src/atom/button-link/types.ts 100.00% <ø> (ø)
...-components/src/molecule/progress-wrapper/index.js 100.00% <100.00%> (ø)
...ponents/src/template/certification-detail/index.js 84.21% <ø> (ø)
.../@coorpacademy-components/src/util/button-icons.js 100.00% <ø> (ø)
...@coorpacademy-components/src/util/linkedin-icon.js 100.00% <100.00%> (ø)

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 962f01b...4646841. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

silouone and others added 3 commits June 24, 2026 19:23
The new "Publish on LinkedIn" button is a second CTA in the badge DetailSection.
The card had a fixed height:114px and an unstyled button container, so the second
button overflowed. Grow the card with min-height (single-button diploma/stars
cards stay 114px - visually unchanged) and stack the buttons in a flex column with
an 8px gap.

Also add EN/FR Storybook screenshots of the BadgeWithLinkedin story (for the PR
description and the spec).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review feedback:
- The share button inherited the orange skin color, so the LinkedIn "in" glyph
  read as text. Make it a proper LinkedIn-blue button (#0A66C2, darker on hover)
  with the white "in" icon + white label so it is unmistakably a LinkedIn CTA.
- The story fixtures pointed at a staging S3 badge URL that 404s, so the badge did
  not render. Use a self-contained inline SVG badge image instead.

Updates the EN/FR story screenshots accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Review feedback: the nova "linkedin" glyph is just the bare "in" letters with
nothing around them, so it read as text. Replace it with the proper boxed LinkedIn
logo (rounded square with the "in" as negative space). Filled with currentColor it
renders as a white rounded box with the button blue showing through the letters -
the recognizable LinkedIn mark.

Updates the EN/FR story screenshots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@silouone silouone self-assigned this Jun 24, 2026
silouone and others added 2 commits June 24, 2026 21:21
Validated the repo i18n workflow before correcting:
- .tx/config has source_lang=en, source_file = locales/en/global.json.
- the transifex-integration[bot] commits "Translate global.json in <lang>" per
  language (including fr) after merge to master.
- the past feature 0bceefb (#3021) touched ONLY en/global.json.
- createTranslate defaults to throwIfMissing=true, so target locales are filled by
  Transifex, not hand-edited.

So a feature PR edits the en source only. Revert the 29 non-en locale files that
were hand-edited, keep en, and scope the guard test to the en source. Spec/plan/
tasks updated to document the Transifex workflow.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ec §7)

Live validation: the button correctly opens LinkedIn's composer for the logged-in
user, but the share is empty until the injected URL points at a public page with
Open Graph tags (og:image=badge, og:title=cert). Resolve spec §7: use
share-offsite/?url=<OG-tagged public cert page>; profile/add autofill is
deprecated by LinkedIn. Component stays URL-agnostic (no code change).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant