07 — Component Library (Feature Flags Module)
- A. Flag representation
- B. Editor & bulk
- C. Affected-features map
- D. Status & feedback
- E. Only elements the API supports
- F. Flutter widget map (module)
- G. Golden tests (base 00-shared/03 G)
Module-specific components built from shared
App*primitives (00-shared/03). Reuse over new — only components absent from the shared library are defined here.
A. Flag representation
AppFlagRow
- Purpose: one flag in the list (switch + identity + menu).
- Props:
key,label?,enabled,module?,toggleEnabled(bool — offline/no-perm),onToggle(bool),pending(bool — write in flight),onEdit,onDelete,selectable,selected. - Rendering:
AppListTileheight 56; leadingAppFlagKeyChip-ish iconIcons.toggle_on/off(orIcons.flag); title =label ?? key; subtitle = keymonobodySmall+ moduleAppBadge; trailingAppSwitch(value=enabled, onChanged=onToggle, disabled= !toggleEnabled || pending). RowAppMenu(Edit / Delete) — menu hidden whenonEdit/onDeletenull. - States: default / selected (checkbox overlay,
primaryContainertint) / pending (switch disabled + row subtitle "Saving…") / disabled-perm (switch grey, tooltip "Ask an admin") / offline (switch disabled, tooltip "You're offline"). - a11y: single semantics node
button, toggled:, label: "<label>, key <key>, module <m>". - Tokens:
surfaceContainerLowrow,R-md,El-e-0; selectedprimaryContainer. - Motion:
m-fastswitch;m-entrancestagger on list insert. - Flutter:
ListTile+Switch+MenuAnchor; selection viaCheckboxoverlay inAnimatedContainer.
AppFlagKeyChip
- Purpose: compact key display with copy.
- Props:
key,copyable(default true). - Rendering:
monotext insurfaceVariantchipR-sm+Icons.content_copy16; copy →Clipboard.setData+ snackbar "Copied" +selectionClickhaptic. - a11y:
Semantics(button, label: "flag key <key>, copy"); copy result live region. - Flutter:
InputChip-styleMaterial+InkWell.
AppFlagGroupHeader
- Purpose: module section header with count.
- Props:
module(label, e.g., "biometric"),count,onToggleEnabled?(module-level "show enabled only" —(proposed)). - Rendering:
AppSectionHeader(titleMedium) +AppBadgecount; Ungrouped bucket for nullmodulevalues (schema allows absentmodule,feature-flag.schema.ts:21-22). - Flutter:
Row+Text+Badge.
B. Editor & bulk
AppFlagEditorForm
- Purpose: the 5-field flag form (create/edit).
- Props:
initial: FlagModel?,createMode,onSave(UpdateFeatureFlagDto),saving,fieldErrors: Map<String,String>,moduleSuggestions: List<String>(from loaded data). - Field spec (exact DTO mirror,
update-feature-flag.dto.ts:4-26):key—AppTextField,mono, required@IsString(); editable only in create mode.enabled—AppSwitch, required boolean.label—AppTextField, optional string.description—AppTextFieldmultiline (2–3 lines), optional.module—AppTextField+AppChipssuggestions, optional.
- Warning banner (edit mode): description/module not persisted by server upsert
(
feature-flag.repository.ts:40) — see 06 §3. - a11y: labels linked; first invalid focus jump; errors announced.
- Flutter:
Form+TextFormFields +SwitchListTile;Autocompletefor module.
AppBulkUpdateSheet
- Purpose: apply one state to N flags.
- Props:
selected: List<FlagModel>,onApply(state, label?),applying,results?: Map<key, success|failure+reason>,onRetryFailed. - Rendering: header +
SegmentedButton(enable/disable) + optional labelAppTextFieldAppButtonapply; results list ofAppFlagRow-lite rows with ✓/✕.
- a11y: focus trap; progress live region.
- Flutter:
showModalBottomSheet+StatefulBuilder/cubit.
C. Affected-features map
AppAffectedMapCard
- Purpose: client-composed "what this flag gates" card (
(proposed)). - Props:
key,entries: List<{screen, icon, state}>(from gating registry),empty. - Rendering:
AppCardwithAppListTilerows — screen name + "hidden when off"AppBadge(outline); empty → "No app screens map to this key yet". - a11y: card
Semantics(header:); rows plain text. - Flutter:
Card+ListTiles.
D. Status & feedback
- AppFlagToggleBanner — post-toggle info banner: "Applies to apps within ~30 seconds"
(planned)(shown only once the Redis 30 s cache lands —CACHE_ARCHITECTURE.md:48; today propagation is immediate). Flagged behind a client constant; removed when server adds invalidation events (OQ-7). - Reuses
AppBanner,AppSnackbar,AppErrorState,AppEmptyState,AppOfflineBannerwith no module changes.
E. Only elements the API supports
- List endpoints return non-paginated arrays — no infinite scroll, no
LoadMore(feature-flags.service.ts:10-20; envelopemetaabsent —response-envelope.interceptor.ts:56-59). - No percentage/audience rollout controls (schema has no such fields,
feature-flag.schema.ts:9-22) — gradual rollout UI is(planned)(OQ-6). - No restore endpoint for soft-deleted flags — the affected map and delete dialog surface the re-create caveat (OQ-4).
F. Flutter widget map (module)
| Component | Flutter widgets |
|---|---|
| AppFlagRow | ListTile + Switch + Checkbox + MenuAnchor |
| AppFlagKeyChip | Material + InkWell + SelectableText |
| AppFlagGroupHeader | Row + Text + Badge |
| AppFlagEditorForm | Form + TextFormField + SwitchListTile + Autocomplete |
| AppBulkUpdateSheet | showModalBottomSheet + SegmentedButton |
| AppAffectedMapCard | Card + ListTiles |
| AppFlagToggleBanner | MaterialBanner |
G. Golden tests (base 00-shared/03 G)
Every module component: golden at phone/tablet/desktop + dark mode; state goldens for
AppFlagRow (on/off/pending/selected/disabled-perm), AppFlagEditorForm (create/edit/error
banner), AppBulkUpdateSheet (idle/applying/results).