Components

Questionnaire

An assistant’s questions, answered in the chat and sent as one Message.

variant
actions
scheme
direction

All at once #

A few questions before I plan

How busy should the days be?

What do you enjoy?
export const QuestionnaireStyle = forSlots(QuestionnaireSlots)(Recipes.Questionnaire({}))

export const TRIP_QUESTIONS: ReadonlyArray<Questionnaire.Question> = [
  Questionnaire.single({
    id: 'pace',
    prompt: 'How busy should the days be?',
    options: [
      { value: 'slow', label: 'Slow', description: 'One thing a day' },
      { value: 'full', label: 'Full', description: 'Morning to night' },
    ],
    other: 'Something else',
  }),
  Questionnaire.multiple({
    id: 'likes',
    prompt: 'What do you enjoy?',
    options: [
      { value: 'food', label: 'Food' },
      { value: 'museums', label: 'Museums' },
      { value: 'walks', label: 'Walks' },
    ],
  }),
  Questionnaire.text({ id: 'avoid', prompt: 'Anything to avoid?', optional: true }),
]

/**
 * Its Model from `Questionnaire.init({ id: 'preview-trip', questions:
 * TRIP_QUESTIONS })`, folded by `Questionnaire.update`, whose
 * `OutMessage.Answered` carries every answer once the form is sent.
 */
export const questionnaire = <Message>(
  style: NamedStyle<typeof QuestionnaireSlots>,
  model: Questionnaire.Model,
  onMessage: (message: Questionnaire.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  h.div(
    [h.Style({ inlineSize: 'min(28rem, 100%)' })],
    [
      Questionnaire.view(
        {
          model,
          onMessage,
          title: 'A few questions before I plan',
          style,
          radioGroup: RadioGroupStyle,
          checkboxGroup: CheckboxGroupStyle,
          input: InputStyle,
          textarea: TextareaStyle,
          submitButton: ButtonStyle,
        },
        h,
      ),
    ],
  )

One at a time, in a card #

Question 1 of 3

How busy should the days be?

export const QuestionnaireCardStyle = forSlots(QuestionnaireSlots)(
  Recipes.Questionnaire({ variant: 'card' }),
)
const QuestionBackStyle = forSlots(ButtonSlots)(
  Recipes.Button({ tone: 'neutral', variant: 'outline' }),
)

/**
 * One question at a time: its Model from `Questionnaire.init({ id:
 * 'preview-trip-paged', questions: TRIP_QUESTIONS, layout: 'paged' })`.
 * Next refuses a missing answer and moves the focus to the next question.
 */
export const questionnairePaged = <Message>(
  style: NamedStyle<typeof QuestionnaireSlots>,
  model: Questionnaire.Model,
  onMessage: (message: Questionnaire.Message) => Message,
  h: HtmlBuilder<Message>,
): Html =>
  h.div(
    [h.Style({ inlineSize: 'min(28rem, 100%)' })],
    [
      Questionnaire.view(
        {
          model,
          onMessage,
          style,
          radioGroup: RadioGroupStyle,
          checkboxGroup: CheckboxGroupStyle,
          input: InputStyle,
          button: QuestionBackStyle,
          submitButton: ButtonStyle,
        },
        h,
      ),
    ],
  )

Usage

Each example above names the function that drew it, over the theme module Create a theme writes. The preview passes it the style picked above instead of its default.

The recipe ↗This code ↗