Asset Pickers — Browse Assets Only¶
Widget editors must not let authors paste external URLs directly. The asset library is the single point of ingestion for any external URL (YouTube, Vimeo, S3, CDN-hosted images, etc.). Authors register the URL there once and then pick it through Browse Assets.
This rule applies to images, audio, video, downloadable files — anything an author would otherwise want to "paste a link into." It does not apply to learner-facing URL fields that point at non-asset destinations (e.g. external website redirects, link metadata).
Required pattern¶
Use the shared MediaField component for every asset slot:
<MediaField
value={content.imageUrl}
onChange={(url) => updateContent({ imageUrl: url })}
accept={["images"]}
label={t("imageLabel")}
testId="widget-<type>-image"
/>
MediaField only renders a "Browse Assets" button — there is no allowExternalUrl prop, no toggle, no URL input. The browse flow opens AssetPickerDialog, which is the only entry point for external URLs.
Forbidden¶
- A plain
<Input type="url">field for an asset URL in a widget editor. - Re-introducing an
allowExternalUrlprop, URL toggle, or URL input onMediaFieldor a sibling component. - A per-widget free-form URL field that bypasses the asset library (e.g. "Paste a YouTube link here").
- Bypassing
MediaFieldand rolling your own browse-vs-paste UI.
Allowed¶
- Non-asset URL fields (learner-facing redirect URLs, external website embeds, link metadata) authored through plain
<Input>— these never become an asset. - The asset library's own URL-registration screen — it remains the canonical URL ingestion point.
- Capturing per-widget metadata (filename, size, mime) via
MediaField'sonAssetSelectcallback.
Test expectations¶
- Tests that stub
MediaFieldmust not surface adata-allow-external-urlattribute (the prop no longer exists). Existing assertions that depend on it should be deleted. - A new MediaField widget should have a regression test that
*-url-toggleand*-url-inputtestids are never rendered.