Repository: h2oai/wave
File: ui/src/dialog.tsx (TypeScript)

Code before:
import React from 'react'
import { bond, qd, S, B } from './qd'
import * as Fluent from '@fluentui/react'
import { XComponents, Component } from './form'

/**
 * A dialog box (Dialog) is a temporary pop-up that takes focus from the page or app
 * and requires people to interact with it. It’s primarily used for confirming actions,
 * such as deleting a file, or asking people to make a choice.
 */
export interface Dialog {
  /** An identifying name for this component. */
  name: S
  /** The title of the dialog. */
  title: S
  /** The form components in the dialog. */
  items: Component[]
  /** True if the dialog submit button should be disabled. */
  disabled?: B
  /** Width of the dialog. E.g. '400px', defaults to '600px'. */
  width?: S
  /** True if the dialog should have closing 'X' button in top right corner. */
  has_x?: B
  /** Text displayed on submit button. Defaults to 'Submit'. */
  submitText?: S
  /** Text displayed on cancel button. Defaults to 'Cancel'. */
  cancelText?: S
}

export default bond(() => {
  const
    { dialogB } = qd,
    toggleDialog = () => dialogB(null),
    submit = () => {
      qd.args[dialogB()!.name] = true
      qd.sync()
      toggleDialog()
    },
    render = () => {
      const dialog = dialogB()
      if (!dialog) return <></>
      const
        { title, cancelText = 'Cancel', submitText = 'Submit', width = '600px', items, disabled, has_x } = dialog,
        dialogContentProps: Fluent.IDialogContentProps = { title, type: has_x ? Fluent.DialogType.close : undefined }

      return (
        <Fluent.Dialog hidden={!dialog} dialogContentProps={dialogContentProps} minWidth={width} maxWidth={width} onDismiss={toggleDialog}>
          <XComponents items={items} />
          <Fluent.DialogFooter>
            <Fluent.DefaultButton onClick={toggleDialog} text={cancelText} />

Review comment (suggestion):
Not all dialog usages boil down to Submit/Cancel. We need to be able to handle cases like:
- OK only
- Yes/No 
- 2+ actions

How about this:
Check if the final component is a `buttons` or `button`. If so, use those inside the dialog footer, and submit values similar to how regular buttons behave (true/false or their `value` if specified).


Code after:
import * as Fluent from '@fluentui/react'
import React from 'react'
import { XButtons } from './button'
import { Component, XComponents } from './form'
import { B, bond, qd, S } from './qd'

/**
 * A dialog box (Dialog) is a temporary pop-up that takes focus from the page or app
 * and requires people to interact with it. It’s primarily used for confirming actions,
 * such as deleting a file, or asking people to make a choice.
 */
export interface Dialog {
  /** The dialog's title. */
  title: S
  /** The components displayed in this dialog. */
  items: Component[]
  /** The width of the dialog, e.g. '400px', defaults to '600px'. */
  width?: S
  /** True if the dialog should have a closing 'X' button at the top right corner. */
  closable?: B
  /** True to disable all actions and commands behind the dialog. Blocking dialogs should be used very sparingly, only when it is critical that the user makes a choice or provides information before they can proceed. Blocking dialogs are generally used for irreversible or potentially destructive tasks. Defaults to false. */
  blocking?: B
  /** Dialog with large header banner, mutually exclusive with `closable` prop. Defaults to false. */
  primary?: B
}

export default bond(() => {
  const
    { dialogB } = qd,
    render = () => {
      const
        { title, width = '600px', items = [], closable, primary, blocking } = dialogB() || {},
        dialogContentProps: Fluent.IDialogContentProps = {
          title,
          type: closable
            ? Fluent.DialogType.close
            : primary
              ? Fluent.DialogType.largeHeader
              : Fluent.DialogType.normal
        },
        lastComponent = items.pop()

      return (
        <Fluent.Dialog hidden={!dialogB()} dialogContentProps={dialogContentProps} modalProps={{ isBlocking: blocking }} minWidth={width} maxWidth={width}>
          <XComponents items={items} />
          {lastComponent?.buttons && <Fluent.DialogFooter><XButtons model={lastComponent.buttons} /></Fluent.DialogFooter>}
        </Fluent.Dialog>
      )
    }
  return { render, dialogB }