> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Boolean Edit Mode

> Guide for adopting the new boolean edit mode for editable DataTable cells.

export const VersionStatus = ({version}) => {
  const isUnreleased = version === "unreleased";
  return <Badge color={isUnreleased ? "orange" : "green"}>
      {isUnreleased ? "Unreleased" : `v${version}`}
    </Badge>;
};

<VersionStatus version="2.4.0" />

<Note>
  This guide covers changes to the **beta** DataTable component. These APIs may continue to evolve before the stable release.
</Note>

## Overview

This guide continues the work started in [Column Types and EditConfig](/docs/web/components/data-table/beta-changes/column-types), which introduced the `editConfig` property. The boolean edit mode is a new addition that provides a purpose-built editing experience for boolean columns.

The DataTable now supports a dedicated **boolean** edit mode for inline editing of boolean cell values:

* **`editConfig.mode: "boolean"`** - Renders a dropdown menu with predefined true/false options
* **Optional null support** - Include a null option for nullable boolean columns
* **Configurable labels** - Customize the display text for `true`, `false`, and `null` values

Previously, editable text cells silently coerced boolean values to strings. The new boolean edit mode preserves the boolean type and provides a constrained selection experience.

## Design Rationale

### Why a dedicated boolean mode?

The text edit mode previously accepted boolean values by converting them to strings:

```tsx theme={null}
// Before: Boolean values silently coerced to strings
createColumn("active", {
  headerLabel: "Active",
  editConfig: {
    mode: "text",
    onChange: (value, rowId) => {
      // value is a string — requires manual parsing
      updateActive(value === "true", rowId);
    },
  },
});
```

This approach had several problems:

* Values lost their boolean type, requiring manual parsing in `onChange`
* Users could type arbitrary text, not just "true" or "false"
* No constrained selection UX for a binary choice

The new boolean mode addresses all of these:

```tsx theme={null}
// After: Purpose-built boolean editing
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    trueLabel: "Yes",
    falseLabel: "No",
    onChange: (value, rowId) => {
      // value is boolean | null — no parsing needed
      updateActive(value, rowId);
    },
  },
});
```

### Relationship between `type` and `editConfig`

The `type` property controls how a value is **displayed** when the cell is not being edited. The `editConfig` controls the **editing** behavior. These are independent concerns:

```tsx theme={null}
// Display as boolean text, edit with a dropdown
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",                 // Formats display as "True" / "False"
  editConfig: {
    mode: "boolean",               // Edits with a dropdown
    trueLabel: "Enabled",
    falseLabel: "Disabled",
    onChange: handleChange,
  },
});
```

### Label inheritance from `type.options`

When `type` is configured with `BooleanFormatterOptions` (i.e. `{ type: "boolean", options: { trueLabel, falseLabel } }`), the boolean edit mode will automatically use those labels as defaults for the edit dropdown. This avoids duplicating labels across `type.options` and `editConfig`:

```tsx theme={null}
// "Yes" / "No" labels are used for both display AND the edit dropdown
createColumn("active", {
  headerLabel: "Active",
  type: { type: "boolean", options: { trueLabel: "Yes", falseLabel: "No" } },
  editConfig: {
    mode: "boolean",
    onChange: handleChange,
  },
});
```

The full priority order for label resolution is:

1. `editConfig.trueLabel` / `editConfig.falseLabel` (explicit override)
2. `type.options.trueLabel` / `type.options.falseLabel` (inherited from formatter config)
3. `"True"` / `"False"` (hardcoded defaults)

This means you can still override individual labels in `editConfig` when you need different text for the edit dropdown than for the display formatter.

## Boolean Edit Options Reference

Boolean-specific options are set directly on the `editConfig` object alongside `mode` and `onChange`. All options are optional.

| Option       | Type      | Default   | Description                                                          |
| ------------ | --------- | --------- | -------------------------------------------------------------------- |
| `trueLabel`  | `string`  | `"True"`  | The label displayed for the `true` option                            |
| `falseLabel` | `string`  | `"False"` | The label displayed for the `false` option                           |
| `allowNull`  | `boolean` | `false`   | Whether to include a null option in the dropdown                     |
| `nullLabel`  | `string`  | `"Unset"` | The label displayed for the `null` option (when `allowNull` is true) |

### Basic boolean editing

```tsx theme={null}
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    onChange: (value, rowId) => updateActive(value, rowId),
  },
});
```

### Custom labels

```tsx theme={null}
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    trueLabel: "Enabled",
    falseLabel: "Disabled",
    onChange: (value, rowId) => updateActive(value, rowId),
  },
});
```

### Nullable boolean with custom labels

```tsx theme={null}
createColumn("approved", {
  headerLabel: "Approved",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    trueLabel: "Approved",
    falseLabel: "Rejected",
    allowNull: true,
    nullLabel: "Pending",
    onChange: (value, rowId) => updateApproval(value, rowId),
  },
});
```

## Migration Guide

### Boolean columns using text edit mode

If you were using `editMode: "text"` or `editConfig.mode: "text"` for boolean data, switch to boolean mode:

```tsx theme={null}
// Before: Text mode with manual boolean handling
createColumn("active", {
  headerLabel: "Active",
  editMode: "text",
  onChange: (value, rowId) => {
    updateActive(value === "true", rowId);
  },
});

// After: Boolean mode with native boolean handling
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    onChange: (value, rowId) => updateActive(value ?? false, rowId),
  },
});
```

### Boolean columns using editConfig text mode

```tsx theme={null}
// Before: editConfig text mode for booleans
createColumn("active", {
  headerLabel: "Active",
  editConfig: {
    mode: "text",
    onChange: (value, rowId) => updateActive(value === "true", rowId),
  },
});

// After: editConfig boolean mode
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    onChange: (value, rowId) => updateActive(value ?? false, rowId),
  },
});
```

### Boolean columns using select edit mode

If you were using `mode: "select"` to simulate boolean editing with manual options:

```tsx theme={null}
// Before: Select mode with manual boolean options
createColumn("active", {
  headerLabel: "Active",
  editConfig: {
    mode: "select",
    options: [
      { value: "true", label: "Yes" },
      { value: "false", label: "No" },
    ],
    onChange: (value, rowId) => updateActive(value === "true", rowId),
  },
});

// After: Boolean mode with configurable labels
createColumn("active", {
  headerLabel: "Active",
  type: "boolean",
  editConfig: {
    mode: "boolean",
    trueLabel: "Yes",
    falseLabel: "No",
    onChange: (value, rowId) => updateActive(value ?? false, rowId),
  },
});
```

## Breaking Changes

<Warning>
  The text edit mode no longer coerces boolean values to strings. Columns with boolean data using text edit mode will render as read-only and log a warning.
</Warning>

The following change is **breaking**:

* **Text edit mode no longer accepts `boolean` values** - Previously, a cell with `mode: "text"` and a boolean value would silently convert the boolean to a string. Now, the cell falls back to a read-only state and logs a console warning directing you to use `mode: "boolean"` instead.

### Why breaking?

Since DataTable is a beta component, we opted to remove the implicit coercion rather than maintain a deprecation path. The coercion masked type mismatches and produced a suboptimal editing experience for boolean data. The new boolean edit mode is the correct replacement.

## New Exports

The following types are now exported from `@servicetitan/anvil2`:

* `BooleanEditConfig` - Edit configuration type for `mode: "boolean"` columns
