+
+ {intl.formatMessage(messages.title)}
+
+
+
setSearchQuery(e.target.value)}
+ className="w-full rounded-button border border-border bg-surface px-3 py-2 text-sm text-text placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-primary"
+ />
+
+ {!hasPresets && (
+
+
+ {intl.formatMessage(messages.empty)}
+
+
+ )}
+
+ {hasPresets && !hasResults && (
+
+
+ {intl.formatMessage(messages.noResults)}
+
+
+ )}
+
+ {hasPresets && hasResults && (
+
+ )}
+
+ );
+}
diff --git a/tests/fixtures/categories/README.md b/tests/fixtures/categories/README.md
new file mode 100644
index 00000000..7a694ba2
--- /dev/null
+++ b/tests/fixtures/categories/README.md
@@ -0,0 +1,56 @@
+# Default Category Fixtures
+
+Representative fixtures vendored from
+[digidem/comapeo-default-categories](https://github.com/digidem/comapeo-default-categories).
+
+**Upstream version**: v1.1.2
+**Upstream commit**: `35fbc145e172dba8dc3e2af5e0598a753f36cae0` (2026-06-29)
+**Vendored date**: 2026-07-22
+
+## What's included
+
+5 representative category JSON files chosen to cover a range of field
+references, tag structures, and geometry types:
+
+- `animal.json` — nature category with select fields
+- `plant.json` — nature category with minimal fields
+- `body-of-water.json` — nature category with many field refs
+- `community.json` — infrastructure category
+- `cultural-site.json` — culture category with selectMultiple fields
+
+Plus `metadata.json` from the upstream package root.
+
+These are NOT all upstream categories — they are a representative subset for
+schema validation testing.
+
+## Source format
+
+Each category JSON follows the upstream `.comapeocat` import format:
+
+```json
+{
+ "name": "Animal",
+ "icon": "animal",
+ "color": "#9E2C54",
+ "fields": ["name", "animal-type"],
+ "appliesTo": ["observation"],
+ "tags": { "type": "nature", "nature": "wildlife", "wildlife": "animal" }
+}
+```
+
+Field JSON follows:
+
+```json
+{
+ "tagKey": "animal-type",
+ "type": "selectOne",
+ "label": "Animal type",
+ "helperText": "What kind of animal?",
+ "options": [{ "label": "Mammal", "value": "mammal" }]
+}
+```
+
+## Updating
+
+When upstream releases a new version, update the files in this directory and
+bump the version/commit above.
diff --git a/tests/fixtures/categories/access.json b/tests/fixtures/categories/access.json
new file mode 100644
index 00000000..9446f9bb
--- /dev/null
+++ b/tests/fixtures/categories/access.json
@@ -0,0 +1,11 @@
+{
+ "tagKey": "access",
+ "type": "selectOne",
+ "label": "Access",
+ "helperText": "How accessible is this location?",
+ "options": [
+ { "label": "Open access", "value": "open-access" },
+ { "label": "Restricted access", "value": "restricted-access" },
+ { "label": "No access", "value": "no-access" }
+ ]
+}
diff --git a/tests/fixtures/categories/animal-type.json b/tests/fixtures/categories/animal-type.json
new file mode 100644
index 00000000..85244684
--- /dev/null
+++ b/tests/fixtures/categories/animal-type.json
@@ -0,0 +1,13 @@
+{
+ "tagKey": "animal-type",
+ "type": "selectOne",
+ "label": "Animal type",
+ "helperText": "What kind of animal?",
+ "options": [
+ { "label": "Mammal", "value": "mammal" },
+ { "label": "Bird", "value": "bird" },
+ { "label": "Reptile/Amphibian", "value": "reptile-amphibian" },
+ { "label": "Fish", "value": "fish" },
+ { "label": "Other", "value": "other" }
+ ]
+}
diff --git a/tests/fixtures/categories/animal.json b/tests/fixtures/categories/animal.json
new file mode 100644
index 00000000..e07bdb3d
--- /dev/null
+++ b/tests/fixtures/categories/animal.json
@@ -0,0 +1,18 @@
+{
+ "name": "Animal",
+ "icon": "animal",
+ "color": "#9E2C54",
+ "fields": [
+ "name",
+ "animal-type",
+ "ecological-status",
+ "conditions",
+ "natural-resource-type"
+ ],
+ "appliesTo": ["observation"],
+ "tags": {
+ "type": "nature",
+ "nature": "wildlife",
+ "wildlife": "animal"
+ }
+}
diff --git a/tests/fixtures/categories/body-of-water-type.json b/tests/fixtures/categories/body-of-water-type.json
new file mode 100644
index 00000000..2a440d3d
--- /dev/null
+++ b/tests/fixtures/categories/body-of-water-type.json
@@ -0,0 +1,20 @@
+{
+ "tagKey": "body-of-water-type",
+ "type": "selectOne",
+ "label": "Type of body of water",
+ "helperText": "The type of body of water",
+ "options": [
+ { "label": "Headwaters", "value": "headwaters" },
+ { "label": "Stream", "value": "stream" },
+ { "label": "River", "value": "river" },
+ { "label": "Rapids", "value": "rapids" },
+ { "label": "Waterfall", "value": "waterfall" },
+ { "label": "Lake", "value": "lake" },
+ { "label": "Pond", "value": "pond" },
+ { "label": "Delta", "value": "delta" },
+ { "label": "Subsurface water table", "value": "subsurface-water-table" },
+ { "label": "Glacier", "value": "glacier" },
+ { "label": "Cold spring", "value": "cold-spring" },
+ { "label": "Hotspring", "value": "hotspring" }
+ ]
+}
diff --git a/tests/fixtures/categories/body-of-water.json b/tests/fixtures/categories/body-of-water.json
new file mode 100644
index 00000000..b7b6b54f
--- /dev/null
+++ b/tests/fixtures/categories/body-of-water.json
@@ -0,0 +1,18 @@
+{
+ "name": "Body of water",
+ "icon": "body-of-water",
+ "color": "#3399ff",
+ "fields": [
+ "name",
+ "body-of-water-type",
+ "conditions",
+ "natural-resource-type",
+ "cultural-name"
+ ],
+ "appliesTo": ["observation"],
+ "tags": {
+ "type": "nature",
+ "nature": "water",
+ "water": "body-of-water"
+ }
+}
diff --git a/tests/fixtures/categories/community.json b/tests/fixtures/categories/community.json
new file mode 100644
index 00000000..1e393c47
--- /dev/null
+++ b/tests/fixtures/categories/community.json
@@ -0,0 +1,11 @@
+{
+ "name": "Community",
+ "icon": "community",
+ "color": "#CC00CC",
+ "fields": ["access"],
+ "appliesTo": ["observation"],
+ "tags": {
+ "type": "infrastructure",
+ "infrastructure": "community"
+ }
+}
diff --git a/tests/fixtures/categories/conditions.json b/tests/fixtures/categories/conditions.json
new file mode 100644
index 00000000..38587be6
--- /dev/null
+++ b/tests/fixtures/categories/conditions.json
@@ -0,0 +1,10 @@
+{
+ "tagKey": "conditions",
+ "type": "selectOne",
+ "label": "Conditions",
+ "helperText": "In what condition this was encountered?",
+ "options": [
+ { "label": "Expected", "value": "expected" },
+ { "label": "Unusual", "value": "unusual" }
+ ]
+}
diff --git a/tests/fixtures/categories/cultural-category.json b/tests/fixtures/categories/cultural-category.json
new file mode 100644
index 00000000..2f9fedda
--- /dev/null
+++ b/tests/fixtures/categories/cultural-category.json
@@ -0,0 +1,14 @@
+{
+ "tagKey": "cultural-category",
+ "type": "selectMultiple",
+ "label": "Cultural activity",
+ "helperText": "What type of cultural activity?",
+ "options": [
+ { "label": "Archeology", "value": "archeology" },
+ { "label": "History", "value": "history" },
+ { "label": "Mythology", "value": "mythology" },
+ { "label": "Sacred site", "value": "sacred-site" },
+ { "label": "Gathering site", "value": "gathering-site" },
+ { "label": "Story", "value": "story" }
+ ]
+}
diff --git a/tests/fixtures/categories/cultural-details.json b/tests/fixtures/categories/cultural-details.json
new file mode 100644
index 00000000..2a8de4f5
--- /dev/null
+++ b/tests/fixtures/categories/cultural-details.json
@@ -0,0 +1,6 @@
+{
+ "tagKey": "cultural-details",
+ "type": "text",
+ "label": "Cultural details",
+ "helperText": "Additional cultural details or significance"
+}
diff --git a/tests/fixtures/categories/cultural-name.json b/tests/fixtures/categories/cultural-name.json
new file mode 100644
index 00000000..bd822849
--- /dev/null
+++ b/tests/fixtures/categories/cultural-name.json
@@ -0,0 +1,6 @@
+{
+ "tagKey": "cultural-name",
+ "type": "text",
+ "label": "Cultural name",
+ "helperText": "Local or traditional name for this place"
+}
diff --git a/tests/fixtures/categories/cultural-site.json b/tests/fixtures/categories/cultural-site.json
new file mode 100644
index 00000000..7bbd64af
--- /dev/null
+++ b/tests/fixtures/categories/cultural-site.json
@@ -0,0 +1,16 @@
+{
+ "name": "Cultural site",
+ "icon": "cultural-site",
+ "color": "#FFCC00",
+ "fields": [
+ "cultural-name",
+ "cultural-category",
+ "cultural-details",
+ "access"
+ ],
+ "appliesTo": ["observation"],
+ "tags": {
+ "type": "culture",
+ "culture": "site"
+ }
+}
diff --git a/tests/fixtures/categories/ecological-status.json b/tests/fixtures/categories/ecological-status.json
new file mode 100644
index 00000000..6438c244
--- /dev/null
+++ b/tests/fixtures/categories/ecological-status.json
@@ -0,0 +1,11 @@
+{
+ "tagKey": "ecological-status",
+ "type": "selectOne",
+ "label": "Ecological status",
+ "helperText": "What is the ecological status of this area?",
+ "options": [
+ { "label": "Pristine", "value": "pristine" },
+ { "label": "Degraded", "value": "degraded" },
+ { "label": "Restored", "value": "restored" }
+ ]
+}
diff --git a/tests/fixtures/categories/index.ts b/tests/fixtures/categories/index.ts
new file mode 100644
index 00000000..5059fcf5
--- /dev/null
+++ b/tests/fixtures/categories/index.ts
@@ -0,0 +1,69 @@
+import accessField from './access.json';
+import animalTypeField from './animal-type.json';
+import animalCategory from './animal.json';
+import bodyOfWaterTypeField from './body-of-water-type.json';
+import bodyOfWaterCategory from './body-of-water.json';
+import communityCategory from './community.json';
+import conditionsField from './conditions.json';
+import culturalCategoryField from './cultural-category.json';
+import culturalDetailsField from './cultural-details.json';
+import culturalNameField from './cultural-name.json';
+import culturalSiteCategory from './cultural-site.json';
+import ecologicalStatusField from './ecological-status.json';
+import metadata from './metadata.json';
+import nameField from './name.json';
+import naturalResourceTypeField from './natural-resource-type.json';
+import plantCategory from './plant.json';
+
+export type CategoryFixture = {
+ name: string;
+ icon: string;
+ color: string;
+ fields: string[];
+ appliesTo: string[];
+ tags: Record