All files / web/src/components/studio StudioNotice.tsx

100% Statements 67/67
100% Branches 4/4
100% Functions 1/1
100% Lines 67/67

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 681x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 1x 10x 10x 10x 1x 10x 10x 10x 10x  
'use client'
 
// StudioNotice — the studio's one way of saying something went sideways
// (Gitea #43). A tone is a 3 px left bar on a tinted wash, NOT an outlined box:
// the rails already carry cards, controls and a mapping table, and a fifth
// bordered rectangle on top of those reads as more chrome rather than as an
// interruption. The bar is the interruption; the wash is the scope.
//
// Use this rather than the `notice()` fragment when the message has a title, or
// actions, or both — the fragment alone is for one-liners.
//
// `role` defaults by tone: ok/info/warn are `status` (polite — a warn notice
// stays up for as long as a setting is wrong and often re-renders as a slider
// moves, so it must never interrupt), danger is `alert` (assertive — a one-shot
// failure). Callers override either way: a danger box that recomputes with the
// design (a solver refusal) passes `role="status"`.
 
import type { ReactNode } from 'react'
import { notice, STUDIO, type StudioTone } from './theme'
 
export interface StudioNoticeProps {
  tone: StudioTone
  children: ReactNode
  title?: ReactNode
  /** buttons — use `button('fix')` so they read as neutral on the tone wash */
  actions?: ReactNode
  dataElement?: string
  /** extra `data-*` attributes (e.g. `data-variant`) for tests and tooling */
  dataAttrs?: Record<`data-${string}`, string | undefined>
  role?: string
}
 
const DEFAULT_ROLE: Record<StudioTone, string> = {
  ok: 'status',
  info: 'status',
  warn: 'status',
  danger: 'alert',
}
 
export function StudioNotice({
  tone,
  children,
  title,
  actions,
  dataElement = 'studio-notice',
  dataAttrs,
  role,
}: StudioNoticeProps) {
  return (
    <div
      {...dataAttrs}
      data-component="studio-notice"
      data-element={dataElement}
      data-tone={tone}
      role={role ?? DEFAULT_ROLE[tone]}
      style={notice(tone)}
    >
      {title != null && (
        <div style={{ ...STUDIO.type.strong, color: 'inherit', marginBottom: 4 }}>{title}</div>
      )}
      <div>{children}</div>
      {actions != null && (
        <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 8 }}>{actions}</div>
      )}
    </div>
  )
}