92 lines
3.6 KiB
Markdown
92 lines
3.6 KiB
Markdown
# @suaveplan/drive-react-sharing — React Bindings for Drive Sharing
|
|
|
|
React-tier UI for managing partitioned drive resources. Wraps the real
|
|
`@suaveplan/drive-sharing` engine (PartitionManager / GrantManager /
|
|
TokenManager) behind a `useSharing` hook + three accessible components.
|
|
|
|
## Requirements
|
|
|
|
### Requirement: useSharing hook
|
|
|
|
The package SHALL expose `useSharing(options): UseSharingResult` that:
|
|
|
|
- Holds a live `recipients` list synchronised with `GrantManager.list()`.
|
|
- Exposes async actions `addRecipient`, `removeRecipient`,
|
|
`updatePermission`, `refresh`. Each action MUST surface failures as
|
|
`DriveReactSharingError` (typed `cause` preserving the underlying
|
|
`DriveShareError`).
|
|
- Increments OTel counters
|
|
`drive.react.sharing.recipients_added_total`,
|
|
`drive.react.sharing.recipients_removed_total`,
|
|
`drive.react.sharing.permissions_updated_total{permission}` on each
|
|
successful action.
|
|
- Threads AbortSignal through every async path.
|
|
- Does NOT mock the engine — consumers pass real `PartitionManager` /
|
|
`GrantManager` / `TokenManager` instances.
|
|
|
|
#### Scenario: updatePermission preserves expiresAt
|
|
|
|
- **WHEN** `updatePermission(grantId, newPermission)` is called on a grant
|
|
with `expiresAt` set
|
|
- **THEN** the resulting grant MUST have the same `expiresAt` value as before
|
|
(the hook revokes-then-creates internally since `GrantManager` has no
|
|
atomic permission mutation).
|
|
|
|
### Requirement: ShareDialog component
|
|
|
|
The package SHALL expose `<ShareDialog>` modal with:
|
|
|
|
- `role="dialog"` + `aria-modal="true"` + `aria-labelledby` referencing the
|
|
heading id.
|
|
- ESC key dismisses (handler bound to dialog `onKeyDown`).
|
|
- Backdrop is a real `<button>` with distinct `aria-label`.
|
|
- Recipient input auto-focuses on open.
|
|
- Permission select (`read | write | admin`) with explicit `aria-label`.
|
|
- Revoke button per row with `aria-label` including recipient id.
|
|
- Error banner rendered with `role="alert"`.
|
|
|
|
### Requirement: PartitionBadge component
|
|
|
|
The package SHALL expose `<PartitionBadge>` compact indicator with:
|
|
|
|
- `role="status"` + composed `aria-label` (e.g. "Partition type shared,
|
|
encrypted").
|
|
- Decorative SVG marked `aria-hidden="true"`.
|
|
- Renders partition `type` (`public | shared | private | encrypted`) + an
|
|
encryption icon when applicable.
|
|
|
|
### Requirement: FileAccessList component
|
|
|
|
The package SHALL expose `<FileAccessList>` read-only list:
|
|
|
|
- Semantic `<ul>` / `<li>` markup (no redundant explicit `role`).
|
|
- Revoke button per row with `aria-label` including recipient id.
|
|
- Delete / Backspace keyboard shortcuts on focused row trigger revoke.
|
|
|
|
### Requirement: Canonical Permission union
|
|
|
|
The package SHALL re-export `Permission` from `@suaveplan/drive-sharing` as
|
|
its canonical type and MUST NOT define a parallel union. Components accept
|
|
`"read" | "write" | "admin"` strings; the hook validates against the engine's
|
|
known permission set.
|
|
|
|
### Requirement: Storybook coverage
|
|
|
|
Each component SHALL ship a `.stories.tsx` with `Default` + `Playground` +
|
|
state variants (loading / error / populated / empty as applicable). The
|
|
`Playground` story for `<ShareDialog>` MUST wire state through `useSharing`
|
|
to exercise the hook end-to-end without an addon story type.
|
|
|
|
### Requirement: Real-engine tests
|
|
|
|
Tests MUST construct real `MemoryPartitionManager` / `MemoryGrantManager` /
|
|
`MemoryTokenManager` instances. NO `vi.mock`, NO `vi.stubGlobal`. Rendering
|
|
uses `@suaveplan/testing-dom`. All test imports come from
|
|
`@suaveplan/testing/runner`.
|
|
|
|
### Requirement: Coverage + gate
|
|
|
|
`bunx turbo lint typecheck test:coverage --filter=@suaveplan/drive-react-sharing`
|
|
MUST be green and istanbul coverage MUST be 100% on every touched module
|
|
before any release.
|