Skip to content

FRD-060: Share Modal Widget

FieldValue
OwnerDavid Holmes
StatusDraft
Last Updated2026-05-26
Target Releasev2.0.0 (P2)
T-Shirt SizeM
TypeWidget

Document Summary

A share modal widget that provides copy-link, invite-by-email, and permission-level toggling in a single dialog. Suitable for sharing documents, dashboards, projects, or any resource that supports link-based and email-based collaboration.


Introduction

Overview

Sharing resources is a universal SaaS pattern. Teams repeatedly build share dialogs with inconsistent UX for link copying, email invitations, and permission management. This widget standardizes the pattern into a reusable, accessible modal.

Goals

  • One-click copy-link with confirmation feedback.
  • Email invitation input with multiple-recipient support.
  • Permission-level selector (e.g., Viewer, Editor, Admin) per invite.
  • Display existing share recipients with their permission levels.
  • Ship Storybook stories covering all interaction states.

Non-Goals

  • Actual email delivery (consumer handles via callback).
  • User search or autocomplete from a directory service.
  • Granular per-field or per-section permissions.
  • Public/private link toggle with server-side enforcement.

Scope

In Scope

ItemDescription
ShareModal componentModal dialog with copy-link, invite form, and recipient list
Copy-link sectionURL display with copy button and toast/inline confirmation
Invite-by-email sectionEmail input, permission selector, and send button
Recipient listShows current recipients with name, email, and permission level
Permission toggleDropdown to change permission level per recipient
Storybook storiesDefault, WithRecipients, InviteSent, CopyConfirmation, Loading

Out of Scope

ItemRationale
Email deliveryBackend concern; widget calls onInvite callback
User directory searchRequires integration with identity provider
Link expiration settingsFuture enhancement
Embedded share (non-modal)Can extract to a separate component later

Users and Pain Points

UserPain Point
SaaS product teamsBuilding share dialogs from scratch per project
End usersInconsistent sharing flows across different product areas
AdministratorsNo standard way to view and manage who has access

Definitions

TermDefinition
Share linkA URL that grants access to a resource
Permission levelThe access tier granted to a recipient (e.g., Viewer, Editor)
RecipientA user who has been granted access via email invite or link

Current State

No share modal exists in the design system. Products implement ad-hoc share dialogs using the base Dialog component, leading to inconsistent patterns for link copying, invitation, and permission management.


Proposed Solution

Create a ShareModal widget at src/components/widgets/share-modal.tsx that:

  1. Wraps the existing Dialog component.
  2. Displays a read-only URL field with a copy-to-clipboard button.
  3. Provides an email input for inviting new recipients with a permission-level dropdown.
  4. Lists current recipients with their permission levels and an option to revoke.
  5. Calls consumer-provided callbacks for invite, permission change, and revoke actions.

Requirements

The modal must be fully controlled (open state via props), use existing Dialog primitives, and handle clipboard API gracefully with fallback.


Functional Requirements

IDRequirementPriority
FR-01Display a share URL with a copy-to-clipboard buttonMust
FR-02Show inline confirmation (“Copied!”) after successful copyMust
FR-03Provide an email text input for entering recipient addressesMust
FR-04Support comma-separated multiple email entryMust
FR-05Include a permission-level dropdown (configurable options)Must
FR-06Call onInvite({ emails, permission }) on sendMust
FR-07Display a list of current recipients with name, email, and permissionMust
FR-08Allow permission changes on existing recipients via onChangePermissionShould
FR-09Allow revoking access via onRevoke(recipientId)Should
FR-10Validate email format before allowing sendMust

Non-Functional Requirements

IDRequirement
NFR-01Clipboard copy works in all modern browsers; graceful fallback with execCommand
NFR-02Modal animation follows design-system motion tokens
NFR-03Full light/dark theme support

API / Interface Requirements

interface ShareRecipient {
id: string;
name?: string;
email: string;
permission: string;
avatarUrl?: string;
}
interface ShareModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
shareUrl: string;
recipients?: ShareRecipient[];
permissionOptions: { value: string; label: string }[];
defaultPermission?: string;
title?: string;
onInvite?: (payload: { emails: string[]; permission: string }) => Promise<void>;
onChangePermission?: (recipientId: string, permission: string) => void;
onRevoke?: (recipientId: string) => void;
className?: string;
}

Accessibility Requirements

IDRequirement
A11Y-01Modal traps focus and returns focus on close per WAI-ARIA dialog pattern
A11Y-02Copy button announces result via aria-live region
A11Y-03Email input has a visible label
A11Y-04Permission dropdown is keyboard-navigable
A11Y-05Recipient list items have accessible names including email and permission
A11Y-06Revoke buttons include aria-label with recipient name

Content and Documentation Requirements

  • Storybook doc page with props, usage examples, and composition guidance.
  • Stories: Default, WithRecipients, InviteSending, CopyConfirmation, EmptyRecipients.
  • JSDoc on all exported types and the main component.

Dependencies

DependencyTypeNotes
DialogInternalModal container
ButtonInternalCopy, send, and revoke actions
InputInternalEmail entry
DropdownMenu or SelectInternalPermission selector
Clipboard APIBrowserCopy-to-clipboard; execCommand fallback

Risks and Tradeoffs

RiskImpactMitigation
Clipboard API unavailable in older browsersCopy fails silentlyImplement execCommand fallback; show manual-copy instructions
Email validation false negativesValid emails rejectedUse a permissive regex; let server do final validation
Large recipient listsModal becomes unwieldyCap visible list at 10; add “Show all” expansion

Open Questions

  1. Should the modal support a “link permissions” section (e.g., “Anyone with link can view”)?
  2. Do we need avatar rendering for recipients, or just initials?
  3. Should there be a “copy invite link” per-recipient option?

Acceptance Criteria

  • Share URL is displayed and copyable with one click.
  • Copy confirmation appears and auto-dismisses.
  • Email input validates format and supports multiple addresses.
  • Permission dropdown works for new invites and existing recipients.
  • onInvite, onChangePermission, and onRevoke callbacks fire correctly.
  • All Storybook stories render without errors.
  • Passes axe accessibility audit with zero violations.
  • Unit tests cover copy, invite, permission change, and revoke flows.

LLM Handoff Instructions

When implementing this FRD:

  1. Create src/components/widgets/share-modal.tsx.
  2. Use the existing Dialog component as the modal shell.
  3. Use navigator.clipboard.writeText with document.execCommand("copy") fallback.
  4. Create src/components/widgets/share-modal.stories.tsx with all listed stories.
  5. Create src/components/widgets/share-modal.test.tsx.
  6. Follow existing widget conventions (see team-switcher.tsx for modal-based widget patterns).
  7. Email validation: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ is sufficient for client-side.

Decision Log

DateDecisionRationale
2026-05-26Fully controlled modal (open/onOpenChange)Consistent with Dialog API; consumer owns trigger
2026-05-26Permission options are consumer-configurableDifferent products have different permission models

Document History

DateVersionAuthorChanges
2026-05-260.1David HolmesInitial draft