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

100% Statements 82/82
100% Branches 11/11
100% Functions 2/2
100% Lines 82/82

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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 831x 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 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 18x 1x 1x 1x 18x 18x 18x 18x  
'use client'
 
// StudioCheckbox — the studio's on/off box (Gitea #43). Same props as the toy
// debug panel's `DebugCheckbox` (which the rails borrowed, indigo and all), plus
// an optional `description`, styled from theme.ts.
//
// The label is label-type but in `text2`, not `muted`: a checkbox's text is the
// CHOICE, not a caption for something else, so it reads a step brighter than the
// labels that sit above selects and sliders. The description, when there is one,
// is note-type under the row and indented to the label's column, and it is tied
// to the input with `aria-describedby` rather than being swept into the
// accessible name — a screen reader should hear "Slow first layer, checkbox",
// then the reason, not one run-on sentence.
 
import { type ReactNode, useId } from 'react'
import { STUDIO } from './theme'
 
export interface StudioCheckboxProps {
  label: string
  checked: boolean
  onChange: (checked: boolean) => void
  dataElement?: string
  dataAction?: string
  disabled?: boolean
  /** one-line reason under the row */
  description?: ReactNode
}
 
export function StudioCheckbox({
  label,
  checked,
  onChange,
  dataElement = 'debug-checkbox',
  dataAction,
  disabled = false,
  description,
}: StudioCheckboxProps) {
  const descriptionId = useId()
  return (
    <div
      data-component="studio-checkbox"
      data-element={dataElement}
      style={{
        display: 'flex',
        flexDirection: 'column',
        gap: 4,
        opacity: disabled ? 0.55 : 1,
      }}
    >
      <label
        style={{
          display: 'flex',
          alignItems: 'center',
          gap: 8,
          cursor: disabled ? 'default' : 'pointer',
          userSelect: 'none',
        }}
      >
        <input
          type="checkbox"
          checked={checked}
          disabled={disabled}
          data-action={dataAction}
          aria-describedby={description != null ? descriptionId : undefined}
          onChange={(e) => onChange(e.target.checked)}
          style={{
            accentColor: STUDIO.color.accent,
            width: 14,
            height: 14,
            cursor: disabled ? 'default' : 'pointer',
          }}
        />
        <span style={{ ...STUDIO.type.label, color: STUDIO.color.text2 }}>{label}</span>
      </label>
      {description != null && (
        <span id={descriptionId} style={{ ...STUDIO.type.note, paddingLeft: 22 }}>
          {description}
        </span>
      )}
    </div>
  )
}