Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/studio-searchable-toggle.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
---
"@object-ui/app-shell": minor
---

feat(studio): surface the `enable.searchable` toggle in ObjectSettingsPanel (#2800)

`enable.searchable` was corrected to LIVE during framework#2377 (the Global
Search executor gates on it — explicit `false` opts the object out of
cross-object search), making it the only live `enable.*` flag the Studio
settings panel did not expose. It now renders as an opt-out toggle (default
on) alongside feeds/activities/clone, with en + zh labels that point
field-level match configuration at `searchableFields` (ADR-0061) to avoid
conflating the two.
6 changes: 6 additions & 0 deletions packages/app-shell/src/views/metadata-admin/i18n.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -1356,6 +1356,9 @@ const ENGINE_STRINGS_EN: Record<string, string> = {
'engine.studio.settings.capActivities': 'Activity timeline (activities)',
'engine.studio.settings.capActivitiesDesc':
'Mirrors create/update/delete into the record timeline. Off = no timeline rows for this object (the compliance audit log is unaffected).',
'engine.studio.settings.capSearchable': 'Global search (searchable)',
'engine.studio.settings.capSearchableDesc':
"Include this object's records in cross-object global search. Off = records no longer appear in global search results. Which fields match is configured per-object via searchableFields, not here.",
'engine.studio.settings.capClone': 'Cloning (clone)',
'engine.studio.settings.capCloneDesc':
'Allow duplicating records from the record page / API. Off = the clone endpoint returns 403 for this object.',
Expand DownExpand Up@@ -2887,6 +2890,9 @@ const ENGINE_STRINGS_ZH: Record<string, string> = {
'engine.studio.settings.capActivities': '活动时间线(activities)',
'engine.studio.settings.capActivitiesDesc':
'将创建/更新/删除镜像到记录时间线。关闭后该对象不再产生时间线记录(合规审计日志不受影响)。',
'engine.studio.settings.capSearchable': '全局搜索(searchable)',
'engine.studio.settings.capSearchableDesc':
'将该对象的记录纳入跨对象全局搜索。关闭后记录不再出现在全局搜索结果中。匹配哪些字段由对象的 searchableFields 配置,与此开关无关。',
'engine.studio.settings.capClone': '克隆(clone)',
'engine.studio.settings.capCloneDesc':
'允许从记录页/API 复制记录。关闭后克隆接口对该对象返回 403。',
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,19 +104,19 @@ describe('ObjectSettingsPanel — capabilities (enable.*, framework#2707/#2727)'
// …opt-out flags default ON.
expect((screen.getByTestId('cap-feeds') as HTMLInputElement).checked).toBe(true);
expect((screen.getByTestId('cap-activities') as HTMLInputElement).checked).toBe(true);
expect((screen.getByTestId('cap-searchable') as HTMLInputElement).checked).toBe(true);
expect((screen.getByTestId('cap-clone') as HTMLInputElement).checked).toBe(true);
// trash/mru no longer exist in the spec (removed, framework#2377 — a
// rendered toggle would author a key the strict schema rejects);
// searchable is live but not yet surfaced here.
expect(screen.queryByTestId('cap-searchable')).toBeNull();
// rendered toggle would author a key the strict schema rejects).
expect(screen.queryByTestId('cap-trash')).toBeNull();
expect(screen.queryByTestId('cap-mru')).toBeNull();
});

it('reflects authored values (explicit false on an opt-out, true on an opt-in)', () => {
renderPanel({ ...baseDraft, enable: { feeds: false, files: true } });
renderPanel({ ...baseDraft, enable: { feeds: false, files: true, searchable: false } });
expect((screen.getByTestId('cap-feeds') as HTMLInputElement).checked).toBe(false);
expect((screen.getByTestId('cap-files') as HTMLInputElement).checked).toBe(true);
expect((screen.getByTestId('cap-searchable') as HTMLInputElement).checked).toBe(false);
// Untouched flags keep their effective defaults.
expect((screen.getByTestId('cap-activities') as HTMLInputElement).checked).toBe(true);
});
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -31,8 +31,7 @@
* are opt-IN (spec default false), feeds/activities/clone are opt-OUT
* (spec default true, explicit false disables). `trash`/`mru` were
* REMOVED from the spec (framework#2377 — ObjectCapabilities is now
* .strict(), authoring them is a parse error); `searchable` is LIVE
* (global-search opt-out) but not yet surfaced here.
* .strict(), authoring them is a parse error).
*/

import React from 'react';
Expand DownExpand Up@@ -87,15 +86,16 @@ export function ObjectSettingsPanel({
// Capabilities (`enable.*`, framework#2707/#2727). Checked = the flag's
// EFFECTIVE runtime value; toggling writes an explicit boolean into the
// enable block (preserving sibling keys). trackHistory/files are opt-in
// (=== true enables); feeds/activities/clone are opt-out (only an explicit
// false disables).
// (=== true enables); searchable/feeds/activities/clone are opt-out (only
// an explicit false disables).
const enable = (draft.enable && typeof draft.enable === 'object' ? draft.enable : {}) as Record<string, unknown>;
const patchEnable = (key: string, value: boolean) => onPatch({ enable: { ...enable, [key]: value } });
const CAPABILITIES: Array<{ key: string; optIn: boolean; labelKey: string; descKey: string }> = [
{ key: 'trackHistory', optIn: true, labelKey: 'engine.studio.settings.capTrackHistory', descKey: 'engine.studio.settings.capTrackHistoryDesc' },
{ key: 'files', optIn: true, labelKey: 'engine.studio.settings.capFiles', descKey: 'engine.studio.settings.capFilesDesc' },
{ key: 'feeds', optIn: false, labelKey: 'engine.studio.settings.capFeeds', descKey: 'engine.studio.settings.capFeedsDesc' },
{ key: 'activities', optIn: false, labelKey: 'engine.studio.settings.capActivities', descKey: 'engine.studio.settings.capActivitiesDesc' },
{ key: 'searchable', optIn: false, labelKey: 'engine.studio.settings.capSearchable', descKey: 'engine.studio.settings.capSearchableDesc' },
{ key: 'clone', optIn: false, labelKey: 'engine.studio.settings.capClone', descKey: 'engine.studio.settings.capCloneDesc' },
];

Expand Down
Loading