textarea column type

Shows as multiline edit in edit/insert dialog
This commit is contained in:
Simon Willison 2026-06-13 22:18:45 -07:00
commit 5490c7b794
6 changed files with 29 additions and 8 deletions

View file

@ -76,6 +76,12 @@ class JsonColumnType(ColumnType):
return None
class TextareaColumnType(ColumnType):
name = "textarea"
description = "Multiline text"
sqlite_types = (SQLiteType.TEXT,)
@hookimpl
def register_column_types(datasette):
return [UrlColumnType, EmailColumnType, JsonColumnType]
return [UrlColumnType, EmailColumnType, JsonColumnType, TextareaColumnType]

View file

@ -792,12 +792,15 @@ function valueToEditText(value) {
return String(value);
}
function shouldUseTextarea(value) {
function shouldUseTextarea(value, columnType) {
if (columnType && columnType.type === "textarea") {
return true;
}
if (value && typeof value === "object") {
return true;
}
var text = valueToEditText(value);
return text.length > 80 || text.indexOf("\n") !== -1;
return text.length > 80 || /[\r\n]/.test(text);
}
function rowEditValueType(value) {
@ -835,7 +838,7 @@ function createRowEditField(column, value, isPk, columnType, index, options) {
var controlWrap = document.createElement("div");
controlWrap.className = "row-edit-control-wrap";
var control = shouldUseTextarea(value)
var control = shouldUseTextarea(value, columnType)
? document.createElement("textarea")
: document.createElement("input");
control.className = "row-edit-input";

View file

@ -1102,9 +1102,9 @@ These configure :ref:`full-text search <full_text_search>` for a table or view.
``column_types``
^^^^^^^^^^^^^^^^
You can assign semantic column types to columns, which affect how values are rendered, validated, and transformed. Built-in column types include ``url``, ``email``, and ``json``. Plugins can register additional column types using the :ref:`register_column_types <plugin_register_column_types>` plugin hook.
You can assign semantic column types to columns, which affect how values are rendered, validated, transformed, and edited. Built-in column types include ``url``, ``email``, ``json``, and ``textarea``. Plugins can register additional column types using the :ref:`register_column_types <plugin_register_column_types>` plugin hook.
Column types can optionally declare which SQLite column types they apply to using ``sqlite_types``. Datasette will reject incompatible assignments. The built-in ``url``, ``email``, and ``json`` column types are all restricted to ``TEXT`` columns.
Column types can optionally declare which SQLite column types they apply to using ``sqlite_types``. Datasette will reject incompatible assignments. The built-in ``url``, ``email``, ``json``, and ``textarea`` column types are all restricted to ``TEXT`` columns.
The simplest form maps column names to type name strings:
@ -1119,6 +1119,7 @@ The simplest form maps column names to type name strings:
website: url
contact: email
extra_data: json
notes: textarea
""").strip()
)
.. ]]]
@ -1135,6 +1136,7 @@ The simplest form maps column names to type name strings:
website: url
contact: email
extra_data: json
notes: textarea
.. tab:: datasette.json
@ -1148,7 +1150,8 @@ The simplest form maps column names to type name strings:
"column_types": {
"website": "url",
"contact": "email",
"extra_data": "json"
"extra_data": "json",
"notes": "textarea"
}
}
}

View file

@ -1092,7 +1092,7 @@ Column types are assigned to columns via the :ref:`column_types <table_configura
config:
format: rgb
Datasette includes three built-in column types: ``url``, ``email``, and ``json``.
Datasette includes four built-in column types: ``url``, ``email``, ``json``, and ``textarea``. The ``textarea`` type is an editing hint that causes Datasette's insert/edit forms to use a multiline ``<textarea>`` control for that column.
.. _plugin_asgi_wrapper:

View file

@ -494,6 +494,7 @@ async def test_builtin_column_types_registered(ds_ct):
assert "url" in ds_ct._column_types
assert "email" in ds_ct._column_types
assert "json" in ds_ct._column_types
assert "textarea" in ds_ct._column_types
assert "nonexistent" not in ds_ct._column_types
@ -510,6 +511,10 @@ async def test_column_type_class_attributes(ds_ct):
assert email_cls.sqlite_types == (SQLiteType.TEXT,)
json_cls = ds_ct._column_types["json"]
assert json_cls.sqlite_types == (SQLiteType.TEXT,)
textarea_cls = ds_ct._column_types["textarea"]
assert textarea_cls.name == "textarea"
assert textarea_cls.description == "Multiline text"
assert textarea_cls.sqlite_types == (SQLiteType.TEXT,)
def test_sqlite_type_from_declared_type():
@ -941,6 +946,7 @@ async def test_set_column_type_ui_data_includes_applicable_types(
"options": [
{"name": "email", "description": "Email address"},
{"name": "json", "description": "JSON data"},
{"name": "textarea", "description": "Multiline text"},
{"name": "url", "description": "URL"},
],
}
@ -949,6 +955,7 @@ async def test_set_column_type_ui_data_includes_applicable_types(
"options": [
{"name": "email", "description": "Email address"},
{"name": "json", "description": "JSON data"},
{"name": "textarea", "description": "Multiline text"},
{"name": "url", "description": "URL"},
],
}

View file

@ -942,6 +942,7 @@ async def test_table_insert_action_button_and_data():
"permissions": {
"insert-row": {"id": "root"},
},
"column_types": {"body": "textarea"},
},
},
},
@ -993,6 +994,7 @@ async def test_table_insert_action_button_and_data():
assert created["default"] == "datetime('now')"
assert created["has_default"]
assert body["value_type"] == "string"
assert body["column_type"] == {"type": "textarea", "config": None}
finally:
ds.close()