kit

Dialog

Built revision v17.10.0

React
Topics

Human support

Bring a missing role, layout, or state, or a rendering against the standard, to the session working on kit. Name the package and the entry.

On Home, List, Settings

Dialog, open

A form that arrives after the task opens.

Edit a name while it loads

Edit name

No name given

Save waits until the name has loaded.

A name that is ready to save with nothing to wait for.

Save a name that is already loaded

Save name

Garden Room

Save waits until the name has loaded.

A Dialog over the page's own work, its lead a Primary action.

Send the invoice

Send the invoice

Oakmere WI invoice

Sending this invoice emails it to Oakmere WI with its payment link.

Takes effect as soon as you confirm.

A task that sends an email, its preview after the form.

Invite a volunteer

Invite a volunteer

aisha@oakmereprimary.org

The invitation, as Aisha will get it

Channel
Email to aisha@oakmereprimary.org
Subject
Help run Oakmere Village Hall

Goes when you send it.

A refund and what it returns, before its amount and reason.

Refund the deposit

Refund the deposit

St Anne’s Scout Group deposit

Refunding £50.00 returns it to St Anne’s Scout Group’s card.

Takes effect as soon as you confirm.

A refund cannot be taken back.

£50.00
No reason given

A task that publishes a page, its preview before the actions.

Publish the autumn programme

Publish the autumn programme

The programme, as visitors will see it

Page
Autumn programme

Goes live when bookings open on 1 September.

A creation in steps, at its first step and at its last.

New event

New event

  1. Name
  2. Not startedOrganiser
  3. Not startedRoom
No name given

New event at its last step

New event

  1. CompleteName
  2. CompleteOrganiser
  3. Room
Room

A task with the subject it acts on, its consequence holding each amount it changes.

Split the charge

Split the charge

Autumn programme hire charge

Splitting the £180.00 charge across three payers changes what each one owes.

What each payer owes
PayerBeforeAfter
Oakmere Primary School
£180.00
£90.00
Oakmere WI
£0.00
£50.00
St Anne’s Scout Group
£0.00
£40.00

Takes effect as soon as you confirm.

To change the split later, split the charge again from its record.

Charge
Autumn programme hire
Amount
£180.00
£90.00
£50.00
£40.00

The same task while its form loads, the subject standing.

Split the charge while it loads

Split the charge

Charge
Autumn programme hire
Amount
£180.00
No share given
No share given

Split waits until the payers have loaded.

A decision whose consequence holds each amount it changes.

Cancel the charge

Cancel the charge

Autumn programme hire charge

Cancelling the £180.00 charge removes what each payer owes, and cannot be undone.

What each payer owes
PayerBeforeAfter
Oakmere Primary School
£90.00
£0.00
Oakmere WI
£50.00
£0.00
St Anne’s Scout Group
£40.00
£0.00

Takes effect as soon as you confirm.

To charge the payers again, add a new charge from the programme’s record.

No name typed

Type Autumn programme hire to cancel it.

A task whose agreement is signed with the actor’s full name.

Save a card to charge later

Save card

Aisha Khan

By saving this card, you allow Oakmere Village Hall to charge it for later invoices on their due date. You can remove the card from your account at any time.

Not signed

Source
module Loading = {
  @val external later: (unit => unit, int) => int = "setTimeout"
  @val external cancel: int => unit = "clearTimeout"

  @react.component
  let make = (~withControl=true) => {
    let dialogId = withControl ? "dialog-loading" : "dialog-loading-without-control"
    let title = withControl ? "Edit name" : "Save name"
    let (open_, setOpen) = React.useState(() => withControl)
    let (loaded, setLoaded) = React.useState(() => false)
    let (name, setName) = React.useState(() => withControl ? "" : "Garden Room")
    React.useEffect1(() => {
      if open_ {
        let timer = later(() => setLoaded(_ => true), 8000)
        Some(() => cancel(timer))
      } else {
        None
      }
    }, [open_])
    let waiting = loaded ? "Enter a name to save it." : "Save waits until the name has loaded."
    <details open_=withControl>
      <summary>{React.string(withControl ? "Edit a name while it loads" : "Save a name that is already loaded")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: dialogId ++ "-opener", type_: "button", onClick: _ => {
        setLoaded(_ => false)
        setOpen(_ => true)
      }}}>{React.string(title)}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus={dialogId ++ "-opener"}
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string(title)}
        headingProps={{id: dialogId ++ "-title"}}
        formGroupsLoading=?{loaded ? None : Some("The name had not loaded.")}
        formGroups={<form key={loaded ? "ready" : "loading"} id={dialogId ++ "-form"}>
          <KitReact.Roles.FormGroup id={dialogId ++ "-name-form-group"}
            label={React.string("Name")}
            labelProps={{htmlFor: dialogId ++ "-name"}}
            controlProps={{id: dialogId ++ "-name", name: "name", type_: "text", disabled: !withControl, onChange: event => setName(_ => ReactEvent.Form.target(event)["value"])}}
            controlValue=name
            printedValue={React.string(name == "" ? "No name given" : name)}
            kind=Text(Medium)
          />
        </form>}
        lead=PrimaryAction({children: React.string("Save name"), state: ?(!loaded || name == "" ? Some(Inert({id: dialogId ++ "-save", words: [KitReact.Words.Text(waiting)]})) : None), props: {id: dialogId ++ "-save", type_: "submit", form: dialogId ++ "-form"}})
        secondaryAction={{children: React.string("Cancel")}}
      />
    </details>
  }
}

module Stepped = {
  let names = ["Name", "Organiser", "Room"]

  let room = (value, label): KitReact.Roles.SelectionControl.Group.choice<KitReact.Roles.SelectionControl.Group.kind> => {
    label: React.string(label),
    kind: KitReact.Roles.SelectionControl.Group.Radio,
    checked: false,
    printedValue: [KitReact.Words.Text("Not chosen")],
    props: {},
    controlProps: {name: "room", value},
    outputProps: {},
    reasonProps: {},
  }

  @react.component
  let make = (~name, ~dialogId, ~start) => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    let (step, setStep) = React.useState(() => start)
    let opener = dialogId ++ "-opener"
    let formId = dialogId ++ "-form"
    let last = Array.length(names) - 1
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    let submit = event => {
      ReactEvent.Form.preventDefault(event)
      if step < last {
        setStep(at => at + 1)
      } else {
        setOpen(_ => false)
      }
    }
    let groups = switch step {
    | 0 =>
      <KitReact.Roles.FormGroup id={dialogId ++ "-name-form-group"}
        label={React.string("Event name")}
        labelProps={{htmlFor: dialogId ++ "-name"}}
        controlProps={{id: dialogId ++ "-name", name: "name", type_: "text"}}
        printedValue={React.string("No name given")}
        kind=Text(Medium)
      />
    | 1 =>
      <KitReact.Roles.FormGroup id={dialogId ++ "-organiser-form-group"}
        label={React.string("Organiser's email")}
        labelProps={{htmlFor: dialogId ++ "-organiser"}}
        controlProps={{id: dialogId ++ "-organiser", name: "organiser", type_: "email"}}
        printedValue={React.string("No email given")}
        kind=Text(Wide)
      />
    | _ =>
      <KitReact.Roles.SelectionControl.Group
        id={dialogId ++ "-rooms"}
        choices=[room("main-hall", "Main Hall"), room("garden-room", "Garden Room"), room("kitchen", "Kitchen")]
        legend={React.string("Room")}
      />
    }
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string(name)}</summary>
      <KitReact.Roles.SecondaryAction props={{id: opener, type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("New event")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus=opener
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string("New event")}
        headingProps={{id: dialogId ++ "-title"}}
        progress={names->Array.mapWithIndex((name, at): KitReact.Progress.step => {
          name,
          state: at < step ? Done : at == step ? Current : Upcoming,
        })}
        progressProps={{ariaLabel: "Steps"}}
        formGroups={<form key={Int.toString(step)} id=formId onSubmit=submit>groups</form>}
        continue={{props: {type_: "submit", form: formId}}}
        back={{props: {type_: "button", onClick: _ => setStep(at => at - 1)}}}
        lead=PrimaryAction({children: React.string("Create event"), props: {type_: "submit", form: formId}})
        secondaryAction={{children: React.string("Cancel")}}
      />
    </details>
  }
}

module Invite = {
  @react.component
  let make = () => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    let dialogId = "dialog-invite"
    let opener = dialogId ++ "-opener"
    let formId = dialogId ++ "-form"
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string("Invite a volunteer")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: opener, type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Invite a volunteer")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus=opener
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string("Invite a volunteer")}
        headingProps={{id: dialogId ++ "-title"}}
        formGroups={<form id=formId>
          <KitReact.Roles.FormGroup id={dialogId ++ "-email-form-group"}
            kind=Email
            label={React.string("Email")}
            labelProps={{htmlFor: dialogId ++ "-email"}}
            controlProps={{id: dialogId ++ "-email", name: "email"}}
            controlValue="aisha@oakmereprimary.org"
            printedValue={React.string("aisha@oakmereprimary.org")}
          />
        </form>}
        outboundPreviews={{
          heading: [KitReact.Words.Text("The invitation, as Aisha will get it")],
          previews: [{
            facts: {children: <>
              <KitReact.Roles.LabelledFact name={React.string("Channel")} value={Words([KitReact.Words.Text("Email to "), KitReact.Words.Email("aisha@oakmereprimary.org")])} />
              <KitReact.Roles.LabelledFact name={React.string("Subject")} value={Words([KitReact.Words.Text("Help run Oakmere Village Hall")])} />
            </>},
            body: Email({title: "Help run Oakmere Village Hall", document: "<!doctype html><html lang=en><head><style>body{margin:0;padding:24px;font-family:Georgia,serif;color:#222222;background:#ffffff}p{margin:0 0 12px;font-size:15px;line-height:1.5}</style></head><body><p>Hello Aisha,</p><p>You are invited to help run the bookings at Oakmere Village Hall.</p><p>You will be able to see the hall’s calendar, answer booking requests and help visitors find a room for their event.</p><p>Before your first shift, we will show you how to check a booking, arrange the room and leave the hall ready for the next group.</p><p>The main hall holds the larger gatherings. The Garden room is on the ground floor, through the main entrance and past the kitchen.</p><p>Keys are kept at the welcome desk. Collect them when you arrive and return them there before you leave.</p><p>If a visitor needs help with access or has a question you cannot answer, ask the volunteer at the welcome desk.</p><p>You can choose the shifts that suit you. Tell us before a shift if you can no longer come so another volunteer can take it.</p><p>Reply to this email if you would like to visit the hall before you begin. We will arrange a time to meet you and answer your questions.</p><p>Accept within 7 days.</p><p>Oakmere Village Hall</p></body></html>"}),
          }],
          time: React.string("Goes when you send it."),
        }}
        lead=PrimaryAction({children: React.string("Send invitation"), props: {type_: "submit", form: formId}})
        secondaryAction={{children: React.string("Cancel")}}
      />
    </details>
  }
}

module Refund = {
  @react.component
  let make = () => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    let dialogId = "dialog-refund"
    let opener = dialogId ++ "-opener"
    let formId = dialogId ++ "-form"
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string("Refund the deposit")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: opener, type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Refund the deposit")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus=opener
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string("Refund the deposit")}
        headingProps={{id: dialogId ++ "-title"}}
        formGroups={<form id=formId>
          <KitReact.Roles.FormGroup id={dialogId ++ "-amount-form-group"}
            label={React.string("Amount")}
            labelProps={{htmlFor: dialogId ++ "-amount"}}
            controlProps={{id: dialogId ++ "-amount", name: "amount", type_: "text"}}
            controlValue="£50.00"
            printedValue={React.string("£50.00")}
            kind=Text(Short)
          />
          <KitReact.Roles.FormGroup id={dialogId ++ "-reason-form-group"}
            label={React.string("Reason")}
            labelProps={{htmlFor: dialogId ++ "-reason"}}
            controlProps={{id: dialogId ++ "-reason", name: "reason", type_: "text"}}
            printedValue={React.string("No reason given")}
            kind=Text(Medium)
          />
        </form>}
        lead=PrimaryAction({children: React.string("Refund deposit"), props: {type_: "submit", form: formId}})
        secondaryAction={{children: React.string("Keep the deposit")}}
      >
        <KitReact.Roles.InlineConsequence
          region=KitReact.Roles.InlineConsequence.Consequence
          props={{ariaLabel: "Refund St Anne’s Scout Group deposit"}}
          subject={React.string("St Anne’s Scout Group deposit")}
          consequence={React.string("Refunding £50.00 returns it to St Anne’s Scout Group’s card.")}
          effectiveTime={React.string("Takes effect as soon as you confirm.")}
          correctionPath={React.string("A refund cannot be taken back.")}
        />
      </KitReact.Roles.Dialog>
    </details>
  }
}

module Publish = {
  @react.component
  let make = () => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    let dialogId = "dialog-publish"
    let opener = dialogId ++ "-opener"
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string("Publish the autumn programme")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: opener, type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Publish the autumn programme")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus=opener
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string("Publish the autumn programme")}
        headingProps={{id: dialogId ++ "-title"}}
        outboundPreviews={{
          heading: [KitReact.Words.Text("The programme, as visitors will see it")],
          previews: [{
            facts: {children: <KitReact.Roles.LabelledFact name={React.string("Page")} value={Words([KitReact.Words.Text("Autumn programme")])} />},
            body: Page({title: "Autumn programme", document: "<!doctype html><html lang=en><head><style>body{margin:0;padding:24px;font-family:Georgia,serif;color:#222222;background:#ffffff}h1{margin:0 0 16px;font-size:22px;font-weight:normal}ul{margin:0;padding:0;list-style:none}li{padding:12px 0;border-top:1px solid #dddddd;font-size:15px;line-height:1.5}b{display:block}</style></head><body><h1>Autumn programme</h1><ul><li><b>Pilates</b>Mondays at 7pm, 12 places</li><li><b>Chair yoga</b>Wednesdays at 10am, 8 places</li><li><b>Watercolour</b>Thursdays at 2pm, 10 places</li></ul></body></html>"}),
          }],
          time: React.string("Goes live when bookings open on 1 September."),
        }}
        lead=PrimaryAction({children: React.string("Publish when bookings open")})
        secondaryAction={{children: React.string("Cancel")}}
      />
    </details>
  }
}

module Split = {
  let share = (~dialogId, key, label, value) =>
    <KitReact.Roles.FormGroup id={dialogId ++ "-" ++ key ++ "-form-group"}
      label={React.string(label)}
      labelProps={{htmlFor: dialogId ++ "-" ++ key}}
      controlProps={{id: dialogId ++ "-" ++ key, name: key, type_: "text"}}
      controlValue=?value
      printedValue={React.string(value->Option.getOr("No share given"))}
      kind=Text(Short)
    />

  @react.component
  let make = (~loading=false) => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    let dialogId = loading ? "dialog-split-loading" : "dialog-split"
    let opener = dialogId ++ "-opener"
    let formId = dialogId ++ "-form"
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string(loading ? "Split the charge while it loads" : "Split the charge")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: opener, type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Split the charge")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus=opener
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id=dialogId
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: dialogId ++ "-title"}}
        heading={React.string("Split the charge")}
        headingProps={{id: dialogId ++ "-title"}}
        context={{children: <>
          <KitReact.Roles.LabelledFact name={React.string("Charge")} value={Words([KitReact.Words.Text("Autumn programme hire")])} />
          <KitReact.Roles.LabelledFact name={React.string("Amount")} value={Words([KitReact.Words.Amount(KitReact.Amount.money(Specimen_Reader.reader, ~amount="180.00", ~currency="GBP"))])} />
        </>}}
        formGroupsLoading=?{loading ? Some("The payers had not loaded.") : None}
        formGroups={<form id=formId>
          {loading
            ? <>
                {share(~dialogId, "first", "First payer", None)}
                {share(~dialogId, "second", "Second payer", None)}
              </>
            : <>
                {share(~dialogId, "school", "Oakmere Primary School", Some("£90.00"))}
                {share(~dialogId, "wi", "Oakmere WI", Some("£50.00"))}
                {share(~dialogId, "scouts", "St Anne’s Scout Group", Some("£40.00"))}
              </>}
        </form>}
        lead=PrimaryAction({
          children: React.string("Split charge"),
          state: ?(loading ? Some(Inert({id: dialogId ++ "-split", words: [KitReact.Words.Text("Split waits until the payers have loaded.")]})) : None),
          props: {type_: "submit", form: formId},
        })
        secondaryAction={{children: React.string("Keep one charge")}}
      >
        {loading
          ? React.null
          : <KitReact.Roles.InlineConsequence
              region=KitReact.Roles.InlineConsequence.Consequence
              props={{ariaLabel: "Split the Autumn programme hire charge"}}
              subject={React.string("Autumn programme hire charge")}
              consequence={React.string("Splitting the £180.00 charge across three payers changes what each one owes.")}
              amounts={Role_InlineConsequence.amounts(~caption="What each payer owes", ~heading="Payer", Role_InlineConsequence.payers)}
              effectiveTime={React.string("Takes effect as soon as you confirm.")}
              correctionPath={React.string("To change the split later, split the charge again from its record.")}
            />}
      </KitReact.Roles.Dialog>
    </details>
  }
}

module Signed = {
  @react.component
  let make = () => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string("Save a card to charge later")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: "dialog-signed-opener", type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Save card")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus="dialog-signed-opener"
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id="dialog-signed"
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: "dialog-signed-title"}}
        heading={React.string("Save card")}
        headingProps={{id: "dialog-signed-title"}}
        formGroups={<form id="dialog-signed-card" action="#dialog-signed" method="get">
          <KitReact.Roles.FormGroup id="dialog-signed-name-form-group"
            label={React.string("Name on card")}
            labelProps={{htmlFor: "dialog-signed-name"}}
            controlProps={{id: "dialog-signed-name", name: "name", autoComplete: "cc-name"}}
            controlValue="Aisha Khan"
            printedValue={React.string("Aisha Khan")}
            kind=Text(Medium)
          />
        </form>}
        signature={{
          id: "dialog-signed-signature-form-group", kind: Text(Medium),
          label: React.string("Your full name, to sign"),
          labelProps: {htmlFor: "dialog-signed-signature"},
          controlProps: {id: "dialog-signed-signature", name: "signature", autoComplete: "name", form: "dialog-signed-card"},
          printedValue: React.string("Not signed"),
        }}
        lead={PrimaryAction({
          children: React.string("Save card"),
          agreement: {
            text: React.string("By saving this card, you allow Oakmere Village Hall to charge it for later invoices on their due date. You can remove the card from your account at any time."),
            props: {},
            textProps: {},
            actionProps: {},
          },
          props: {type_: "submit", form: "dialog-signed-card"},
        })}
        secondaryAction={{children: React.string("Keep current card"), props: {type_: "button"}}}
      />
    </details>
  }
}

@react.component
let make = () =>
  <>
    <p className="specimen-case-line">{React.string("A form that arrives after the task opens.")}</p>
    <Loading />
    <p className="specimen-case-line">{React.string("A name that is ready to save with nothing to wait for.")}</p>
    <Loading withControl=false />
    <p className="specimen-case-line">{React.string("A Dialog over the page's own work, its lead a Primary action.")}</p>
    <Role_DestructiveAction.Case
      name="Send the invoice"
      opener="dialog-invoice-opener"
      dialogId="dialog-invoice"
      consequence={<KitReact.Roles.InlineConsequence
        region=KitReact.Roles.InlineConsequence.Consequence
        props={{ariaLabel: "Send the Oakmere WI invoice"}}
        subject={React.string("Oakmere WI invoice")}
        consequence={React.string("Sending this invoice emails it to Oakmere WI with its payment link.")}
        effectiveTime={React.string("Takes effect as soon as you confirm.")}
        correctionPath={<></>}
      />}
      lead=PrimaryAction({children: React.string("Send invoice")})
      secondary="Keep editing"
    />
    <p className="specimen-case-line">{React.string("A task that sends an email, its preview after the form.")}</p>
    <Invite />
    <p className="specimen-case-line">{React.string("A refund and what it returns, before its amount and reason.")}</p>
    <Refund />
    <p className="specimen-case-line">{React.string("A task that publishes a page, its preview before the actions.")}</p>
    <Publish />
    <p className="specimen-case-line">{React.string("A creation in steps, at its first step and at its last.")}</p>
    <Stepped name="New event" dialogId="dialog-event" start=0 />
    <Stepped name="New event at its last step" dialogId="dialog-event-room" start=2 />
    <p className="specimen-case-line">{React.string("A task with the subject it acts on, its consequence holding each amount it changes.")}</p>
    <Split />
    <p className="specimen-case-line">{React.string("The same task while its form loads, the subject standing.")}</p>
    <Split loading=true />
    <p className="specimen-case-line">{React.string("A decision whose consequence holds each amount it changes.")}</p>
    <Role_DestructiveAction.Case
      name="Cancel the charge"
      opener="dialog-cancel-charge-opener"
      dialogId="dialog-cancel-charge"
      consequence={<KitReact.Roles.InlineConsequence
        region=KitReact.Roles.InlineConsequence.Consequence
        props={{ariaLabel: "Cancel the Autumn programme hire charge"}}
        subject={React.string("Autumn programme hire charge")}
        consequence={React.string("Cancelling the £180.00 charge removes what each payer owes, and cannot be undone.")}
        amounts={Role_InlineConsequence.amounts(~caption="What each payer owes", ~heading="Payer", [("Oakmere Primary School", "90.00", "0.00"), ("Oakmere WI", "50.00", "0.00"), ("St Anne’s Scout Group", "40.00", "0.00")])}
        effectiveTime={React.string("Takes effect as soon as you confirm.")}
        correctionPath={React.string("To charge the payers again, add a new charge from the programme’s record.")}
      />}
      lead=DestructiveAction({
        icon: KitReact.Icons.iconTrash,
        children: React.string("Cancel the charge"),
        state: Confirmation({
          name: "Autumn programme hire",
          id: "dialog-cancel-charge-cancel",
          words: [KitReact.Words.Text("Type Autumn programme hire to cancel it.")],
          formGroups: <KitReact.Roles.FormGroup id="dialog-cancel-charge-name-form-group"
            label={React.string("Charge name")}
            labelProps={{htmlFor: "dialog-cancel-charge-name"}}
            controlProps={{id: "dialog-cancel-charge-name", name: "name", type_: "text"}}
            printedValue={React.string("No name typed")}
            kind=Text(Medium)
          />,
        }),
      })
      secondary="Keep the charge"
    />
    <p className="specimen-case-line">{React.string("A task whose agreement is signed with the actor’s full name.")}</p>
    <Signed />
  </>

Dialog, unsaved

Add a room

Add a room

Garden Room
40

Not saved

A long room form with changes not yet saved.

Add a room with more details

Add a room

Garden Room
40
Community centre
Ground floor
Aisha Khan
aisha@example.org
01632 960123
12 Garden Street
Oakmere
OX1 1AA
Step-free entrance
Key held at reception

Not saved

A long room form whose last submission was refused.

Add a room after a refusal

Add a room

Garden Room
40
Community centre
Ground floor
Aisha Khan
aisha@example.org
01632 960123
12 Garden Street
Oakmere
OX1 1AA
Step-free entrance
Key held at reception

Not saved

A long room form with its entry saved and its actions in place.

Add a room with its entry saved

Add a room

Garden Room
40
Community centre
Ground floor
Aisha Khan
aisha@example.org
01632 960123
12 Garden Street
Oakmere
OX1 1AA
Step-free entrance
Key held at reception

Source
module Long = {
  let fields: array<(string, string, string, KitReact.Roles.FormGroup.width)> = [
    ("name", "Room name", "Garden Room", Medium),
    ("seats", "Seats", "40", Narrow),
    ("building", "Building", "Community centre", Medium),
    ("floor", "Floor", "Ground floor", Short),
    ("contact", "Contact name", "Aisha Khan", Medium),
    ("email", "Contact email", "aisha@example.org", Wide),
    ("telephone", "Telephone", "01632 960123", Medium),
    ("street", "Street address", "12 Garden Street", Wide),
    ("town", "Town", "Oakmere", Medium),
    ("postcode", "Postcode", "OX1 1AA", Narrow),
    ("access", "Access", "Step-free entrance", Wide),
    ("notes", "Notes", "Key held at reception", Column),
  ]

  @react.component
  let make = (~id, ~line, ~summary, ~refused=false, ~saved=false) => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <>
      <p className="specimen-case-line">{React.string(line)}</p>
      <details ref={ReactDOM.Ref.domRef(caseRef)}>
        <summary>{React.string(summary)}</summary>
        <KitReact.Roles.SecondaryAction props={{id: id ++ "-opener", type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Add a room")}</KitReact.Roles.SecondaryAction>
        <KitReact.Roles.Dialog
          id
          open_
          returnFocus={id ++ "-opener"}
          onOpen={() => setOpen(_ => true)}
          onClose={() => setOpen(_ => false)}
          props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: id ++ "-title"}}
          heading={React.string("Add a room")}
          headingProps={{id: id ++ "-title"}}
          formGroups={<form id={id ++ "-form"}>
            {React.array(fields->Array.map(((name, label, value, width)) => {
              let control = id ++ "-" ++ name
              <KitReact.Roles.FormGroup
                key=name
                id={control ++ "-form-group"}
                label={React.string(label)}
                labelProps={{htmlFor: control}}
                controlProps={{id: control, name, type_: "text"}}
                controlValue=value
                printedValue={React.string(value)}
                kind=Text(width)
              />
            }))}
          </form>}
          refusal=?{refused ? Some({
            id: id ++ "-refusal",
            line: React.string("Room not added. A room with this name already exists."),
            causes: [{key: "name", href: "#" ++ id ++ "-name", label: React.string("Choose another room name")}],
          }) : None}
          lead=PrimaryAction({children: React.string("Add room"), props: {type_: "submit", form: id ++ "-form"}})
          secondaryAction={{children: React.string("Cancel")}}
          unsaved=?{saved ? None : Some([KitReact.Words.Text("Not saved")])}
        />
      </details>
    </>
  }
}

@react.component
let make = () => {
  let caseRef = React.useRef(Nullable.null)
  let dialogRef = React.useRef(Nullable.null)
  let (open_, setOpen) = React.useState(() => false)
  React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
  | (Some(details), Some(dialog)) =>
    let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
    Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
    Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
  | _ => None
  })
  <>
  <details ref={ReactDOM.Ref.domRef(caseRef)}>
    <summary>{React.string("Add a room")}</summary>
    <KitReact.Roles.SecondaryAction props={{id: "dialog-unsaved-opener", type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("Add a room")}</KitReact.Roles.SecondaryAction>
    <KitReact.Roles.Dialog
      open_
      returnFocus="dialog-unsaved-opener"
      onOpen={() => setOpen(_ => true)}
      onClose={() => setOpen(_ => false)}
      id="dialog-unsaved"
      props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: "dialog-unsaved-title"}}
      heading={React.string("Add a room")}
      headingProps={{id: "dialog-unsaved-title"}}
      keep={{key: "aisha-khan/rooms/new", version: "1"}}
      formGroupsProps={{id: "dialog-unsaved-form-groups"}}
      formGroups={<form id="dialog-unsaved-form">
        <KitReact.Roles.FormGroup id="dialog-unsaved-name-form-group"
          label={React.string("Room name")}
          labelProps={{htmlFor: "dialog-unsaved-name"}}
          controlProps={{id: "dialog-unsaved-name", name: "name", type_: "text"}}
          controlValue="Garden Room"
          printedValue={React.string("Garden Room")}
          kind=Text(Medium)
        />
        <KitReact.Roles.FormGroup id="dialog-unsaved-seats-form-group"
          label={React.string("Seats")}
          labelProps={{htmlFor: "dialog-unsaved-seats"}}
          controlProps={{id: "dialog-unsaved-seats", name: "seats", type_: "text", inputMode: "numeric"}}
          controlValue="40"
          printedValue={React.string("40")}
          kind=Text(Narrow)
        />
      </form>}
      lead=PrimaryAction({children: React.string("Add room"), props: {type_: "submit", form: "dialog-unsaved-form"}})
      secondaryAction={{children: React.string("Cancel")}}
      unsaved=[KitReact.Words.Text("Not saved")]
    />
  </details>
  <Long id="dialog-unsaved-long" line="A long room form with changes not yet saved." summary="Add a room with more details" />
  <Long id="dialog-unsaved-refused" line="A long room form whose last submission was refused." summary="Add a room after a refusal" refused=true />
  <Long id="dialog-unsaved-saved" line="A long room form with its entry saved and its actions in place." summary="Add a room with its entry saved" saved=true />
  </>
}

Dialog, inert

A Dialog whose Primary lead waits.

Send the invoice

Send the invoice

Oakmere WI invoice

Sending this invoice emails it to Oakmere WI with its payment link.

Takes effect as soon as you confirm.

Send waits until the invoice has been prepared.

A Dialog whose Destructive lead waits.

Delete the booking

Delete the booking

Oakmere WI booking

Deleting this booking frees Tuesday in the Main Hall and removes its invoice.

Takes effect as soon as you confirm.

Delete waits until the booking’s invoice has been checked.

A creation in steps whose Continue waits.

New event

New event

  1. Name
  2. Not startedOrganiser
  3. Not startedRoom
Autumn programme hire

Continue waits until the event name has been checked.

Source
module Event = {
  @react.component
  let make = () => {
    let caseRef = React.useRef(Nullable.null)
    let dialogRef = React.useRef(Nullable.null)
    let (open_, setOpen) = React.useState(() => false)
    React.useEffect0(() => switch (Nullable.toOption(caseRef.current), Nullable.toOption(dialogRef.current)) {
    | (Some(details), Some(dialog)) =>
      let discloseCase = _ => details->Role_DestructiveAction.disclose(true)
      Role_DestructiveAction.listen(dialog, "beforetoggle", discloseCase)
      Some(() => Role_DestructiveAction.unlisten(dialog, "beforetoggle", discloseCase))
    | _ => None
    })
    <details ref={ReactDOM.Ref.domRef(caseRef)}>
      <summary>{React.string("New event")}</summary>
      <KitReact.Roles.SecondaryAction props={{id: "dialog-inert-event-opener", type_: "button", onClick: _ => setOpen(_ => true)}}>{React.string("New event")}</KitReact.Roles.SecondaryAction>
      <KitReact.Roles.Dialog
        open_
        returnFocus="dialog-inert-event-opener"
        onOpen={() => setOpen(_ => true)}
        onClose={() => setOpen(_ => false)}
        id="dialog-inert-event"
        props={{ref: ReactDOM.Ref.domRef(dialogRef), ariaLabelledby: "dialog-inert-event-title"}}
        heading={React.string("New event")}
        headingProps={{id: "dialog-inert-event-title"}}
        progress={[
          {name: "Name", state: Current},
          {name: "Organiser", state: Upcoming},
          {name: "Room", state: Upcoming},
        ]}
        progressProps={{ariaLabel: "Steps"}}
        formGroups={<form id="dialog-inert-event-form">
          <KitReact.Roles.FormGroup id="dialog-inert-event-name-form-group"
            label={React.string("Event name")}
            labelProps={{htmlFor: "dialog-inert-event-name"}}
            controlProps={{id: "dialog-inert-event-name", name: "name", type_: "text"}}
            controlValue="Autumn programme hire"
            printedValue={React.string("Autumn programme hire")}
            kind=Text(Medium)
          />
        </form>}
        continue={{
          state: Inert({id: "dialog-inert-event-continue", words: [KitReact.Words.Text("Continue waits until the event name has been checked.")]}),
          props: {type_: "submit", form: "dialog-inert-event-form"},
        }}
        back={{props: {type_: "button"}}}
        lead=PrimaryAction({children: React.string("Create event"), props: {type_: "submit", form: "dialog-inert-event-form"}})
        secondaryAction={{children: React.string("Cancel")}}
      />
    </details>
  }
}

@react.component
let make = () =>
  <>
    <p className="specimen-case-line">{React.string("A Dialog whose Primary lead waits.")}</p>
    <Role_DestructiveAction.Case
      name="Send the invoice"
      opener="dialog-inert-invoice-opener"
      dialogId="dialog-inert-invoice"
      consequence={<KitReact.Roles.InlineConsequence
        region=KitReact.Roles.InlineConsequence.Consequence
        props={{ariaLabel: "Send the Oakmere WI invoice"}}
        subject={React.string("Oakmere WI invoice")}
        consequence={React.string("Sending this invoice emails it to Oakmere WI with its payment link.")}
        effectiveTime={React.string("Takes effect as soon as you confirm.")}
        correctionPath={<></>}
      />}
      lead=PrimaryAction({
        children: React.string("Send invoice"),
        state: Inert({id: "dialog-inert-invoice-send", words: [KitReact.Words.Text("Send waits until the invoice has been prepared.")]}),
      })
      secondary="Keep editing"
    />
    <p className="specimen-case-line">{React.string("A Dialog whose Destructive lead waits.")}</p>
    <Role_DestructiveAction.Case
      name="Delete the booking"
      opener="dialog-inert-delete-opener"
      dialogId="dialog-inert-delete"
      consequence={<KitReact.Roles.InlineConsequence
        region=KitReact.Roles.InlineConsequence.Consequence
        props={{ariaLabel: "Delete the Oakmere WI booking"}}
        subject={React.string("Oakmere WI booking")}
        consequence={React.string("Deleting this booking frees Tuesday in the Main Hall and removes its invoice.")}
        effectiveTime={React.string("Takes effect as soon as you confirm.")}
        correctionPath={<></>}
      />}
      lead=DestructiveAction({
        icon: KitReact.Icons.iconTrash,
        children: React.string("Delete booking"),
        state: Inert({id: "dialog-inert-delete-delete", words: [KitReact.Words.Text("Delete waits until the booking’s invoice has been checked.")]}),
      })
      secondary="Keep the booking"
    />
    <p className="specimen-case-line">{React.string("A creation in steps whose Continue waits.")}</p>
    <Event />
  </>