Skip to content

Commit 3b857bb

Browse files
[content-list] Move content editor wiring to features.contentEditor (elastic#270043)
1 parent 31f7e19 commit 3b857bb

41 files changed

Lines changed: 769 additions & 748 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/platform/packages/shared/content-management/content_editor/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
export { ContentEditorProvider, ContentEditorKibanaProvider, useOpenContentEditor } from './src';
1111
export type {
12+
ContentEditorKibanaDependencies,
1213
OpenContentEditorParams,
1314
ContentEditorItem,
1415
ContentEditorCustomValidators,

src/platform/packages/shared/content-management/content_editor/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
export { ContentEditorProvider, ContentEditorKibanaProvider } from './services';
11+
export type { ContentEditorKibanaDependencies } from './services';
1112
export { useOpenContentEditor } from './open_content_editor';
1213
export type { OpenContentEditorParams } from './open_content_editor';
1314
export type { Item as ContentEditorItem } from './types';

src/platform/packages/shared/content-management/content_editor/src/services.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export interface Services {
6565
openSystemFlyout(node: ReactNode, options?: OverlaySystemFlyoutOpenOptions): OverlayRef;
6666
notifyError: NotifyFn;
6767
TagList?: FC<{ tagIds: string[] }>;
68-
TagSelector?: React.FC<TagSelectorProps>;
68+
TagSelector?: React.ComponentType<any>;
6969
}
7070

7171
const ContentEditorContext = React.createContext<Services | null>(null);
@@ -133,7 +133,7 @@ export interface ContentEditorKibanaDependencies {
133133
managed: boolean;
134134
}) => void;
135135
}>;
136-
SavedObjectSaveModalTagSelector: React.FC<TagSelectorProps>;
136+
SavedObjectSaveModalTagSelector: React.ComponentType<any>;
137137
};
138138
};
139139
};

src/platform/packages/shared/content-management/content_list/kbn-content-list-docs/src/dashboard_listing.stories.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
createMockStoryFindItems,
3333
createMockTagFacetProvider,
3434
createMockUserProfileFacetProvider,
35-
useInspectFlyout,
35+
useContentEditorFlyout,
3636
} from './stories_helpers';
3737

3838
const { Section } = KibanaContentListPage;
@@ -54,11 +54,9 @@ const labels = {
5454
} as const;
5555

5656
const useDashboardProviderProps = ({
57-
onInspect,
58-
includeInspect = false,
57+
openContentEditor,
5958
}: {
60-
onInspect?: (item: ContentListItem) => void;
61-
includeInspect?: boolean;
59+
openContentEditor?: (item: ContentListItem) => void;
6260
}) => {
6361
const favoritesClient = useMemo(
6462
() => createMockFavoritesClient(['dashboard-001', 'dashboard-003', 'dashboard-007']),
@@ -89,8 +87,10 @@ const useDashboardProviderProps = ({
8987
tags: createMockTagFacetProvider(MOCK_DASHBOARDS),
9088
starred: true as const,
9189
userProfiles: createMockUserProfileFacetProvider(MOCK_DASHBOARDS),
90+
// Original story leaves this undefined, which makes `<Action.ContentEditor />` self-skip.
91+
...(openContentEditor ? { contentEditor: { open: openContentEditor } } : {}),
9292
}),
93-
[]
93+
[openContentEditor]
9494
);
9595

9696
const item = useMemo(
@@ -107,10 +107,9 @@ const useDashboardProviderProps = ({
107107
await wait(250);
108108
},
109109
},
110-
...(includeInspect && onInspect ? { inspect: { onItemAction: onInspect } } : {}),
111110
},
112111
}),
113-
[includeInspect, onInspect]
112+
[]
114113
);
115114

116115
return { dataSource, favoritesClient, features, item };
@@ -166,10 +165,9 @@ const OriginalStory = () => {
166165
};
167166

168167
const ProposalStory = () => {
169-
const { onInspect, flyout } = useInspectFlyout();
168+
const { open: openContentEditor, flyout } = useContentEditorFlyout();
170169
const { favoritesClient, ...providerProps } = useDashboardProviderProps({
171-
onInspect,
172-
includeInspect: true,
170+
openContentEditor,
173171
});
174172

175173
const pageElement = useMemo(
@@ -199,7 +197,7 @@ const ProposalStory = () => {
199197
<Column.CreatedBy />
200198
<Column.UpdatedAt />
201199
<Column.Actions>
202-
<Action.Inspect />
200+
<Action.ContentEditor />
203201
<Action.Edit />
204202
<Action.Delete />
205203
</Column.Actions>

src/platform/packages/shared/content-management/content_list/kbn-content-list-docs/src/files_management.stories.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
ListingPageHeaderMock,
3333
StateDiagnosticPanel,
3434
createMockStoryFindItems,
35-
useInspectFlyout,
35+
useContentEditorFlyout,
3636
} from './stories_helpers';
3737

3838
const meta: Meta = {
@@ -74,7 +74,7 @@ const emptyState = (
7474
/>
7575
);
7676

77-
const useFilesProviderProps = (onInspect?: (item: ContentListItem) => void) => {
77+
const useFilesProviderProps = (openContentEditor?: (item: ContentListItem) => void) => {
7878
const dataSource = useMemo(
7979
() => ({
8080
debounceMs: 0,
@@ -101,8 +101,10 @@ const useFilesProviderProps = (onInspect?: (item: ContentListItem) => void) => {
101101
],
102102
},
103103
pagination: { initialPageSize: 50 },
104+
// `<Action.ContentEditor />` self-skips when this is undefined.
105+
...(openContentEditor ? { contentEditor: { open: openContentEditor } } : {}),
104106
}),
105-
[]
107+
[openContentEditor]
106108
);
107109

108110
const item = useMemo(
@@ -113,18 +115,17 @@ const useFilesProviderProps = (onInspect?: (item: ContentListItem) => void) => {
113115
await wait(250);
114116
},
115117
},
116-
...(onInspect ? { inspect: { onItemAction: onInspect } } : {}),
117118
},
118119
}),
119-
[onInspect]
120+
[]
120121
);
121122

122123
return { dataSource, features, item };
123124
};
124125

125126
const OriginalStory = () => {
126-
const { onInspect, flyout } = useInspectFlyout();
127-
const providerProps = useFilesProviderProps(onInspect);
127+
const { open: openContentEditor, flyout } = useContentEditorFlyout();
128+
const providerProps = useFilesProviderProps(openContentEditor);
128129

129130
const pageElement = useMemo(
130131
() => (
@@ -149,7 +150,7 @@ const OriginalStory = () => {
149150
sortable
150151
render={(item) => (
151152
<>
152-
<EuiLink onClick={() => onInspect(item)}>{item.title}</EuiLink>
153+
<EuiLink onClick={() => openContentEditor(item)}>{item.title}</EuiLink>
153154
{item.description ? (
154155
<EuiText size="s" color="subdued">
155156
<p>{item.description}</p>
@@ -178,7 +179,7 @@ const OriginalStory = () => {
178179
{flyout}
179180
</>
180181
),
181-
[flyout, onInspect]
182+
[flyout, openContentEditor]
182183
);
183184

184185
return (
@@ -193,8 +194,8 @@ const OriginalStory = () => {
193194
};
194195

195196
const ProposalStory = () => {
196-
const { onInspect, flyout } = useInspectFlyout();
197-
const providerProps = useFilesProviderProps(onInspect);
197+
const { open: openContentEditor, flyout } = useContentEditorFlyout();
198+
const providerProps = useFilesProviderProps(openContentEditor);
198199

199200
const pageElement = useMemo(
200201
() => (
@@ -242,7 +243,7 @@ const ProposalStory = () => {
242243
/>
243244
<Column.UpdatedAt />
244245
<Column.Actions>
245-
<Action.Inspect />
246+
<Action.ContentEditor />
246247
<Action.Delete />
247248
</Column.Actions>
248249
</ContentListTable>

src/platform/packages/shared/content-management/content_list/kbn-content-list-docs/src/maps.stories.tsx

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
import React, { useMemo } from 'react';
10+
import React, { useMemo, type ReactNode } from 'react';
1111
import type { Meta, StoryObj } from '@storybook/react';
1212
import { EuiButton, EuiSpacer } from '@elastic/eui';
1313
import type { ContentListItem } from '@kbn/content-list-provider';
@@ -17,6 +17,7 @@ import {
1717
ContentListTable,
1818
ContentListFooter,
1919
ContentListToolbar,
20+
useContentListConfig,
2021
} from '@kbn/content-list';
2122
import { KibanaContentListPage } from '@kbn/content-list-page';
2223
import {
@@ -29,7 +30,7 @@ import {
2930
createMockStoryFindItems,
3031
createMockTagFacetProvider,
3132
createMockUserProfileFacetProvider,
32-
useInspectFlyout,
33+
useContentEditorFlyout,
3334
} from './stories_helpers';
3435

3536
const meta: Meta = {
@@ -49,10 +50,10 @@ const labels = {
4950
} as const;
5051

5152
const useMapsProviderProps = ({
52-
onInspect,
53+
openContentEditor,
5354
includeCreatedBy = false,
5455
}: {
55-
onInspect?: (item: ContentListItem) => void;
56+
openContentEditor?: (item: ContentListItem) => void;
5657
includeCreatedBy?: boolean;
5758
}) => {
5859
const dataSource = useMemo(
@@ -75,8 +76,10 @@ const useMapsProviderProps = ({
7576
pagination: { initialPageSize: 20 },
7677
tags: createMockTagFacetProvider(MOCK_MAPS),
7778
...(includeCreatedBy ? { userProfiles: createMockUserProfileFacetProvider(MOCK_MAPS) } : {}),
79+
// Mirrors how `ContentListClientProvider` wires `useContentEditorOpen()`.
80+
...(openContentEditor ? { contentEditor: { open: openContentEditor } } : {}),
7881
}),
79-
[includeCreatedBy]
82+
[includeCreatedBy, openContentEditor]
8083
);
8184

8285
const item = useMemo(
@@ -88,10 +91,9 @@ const useMapsProviderProps = ({
8891
await wait(250);
8992
},
9093
},
91-
...(onInspect ? { inspect: { onItemAction: onInspect } } : {}),
9294
},
9395
}),
94-
[onInspect]
96+
[]
9597
);
9698

9799
return { dataSource, features, item };
@@ -147,9 +149,43 @@ const OriginalStory = () => {
147149
);
148150
};
149151

152+
/**
153+
* Mirrors `x-pack/.../maps/public/components/map_list`:
154+
* `<Action.ContentEditor />` is unconditional and self-skips when
155+
* `features.contentEditor.open` isn't wired.
156+
*/
157+
const MapsProposalListBody = ({ flyout }: { flyout: ReactNode }) => {
158+
const { isReadOnly } = useContentListConfig();
159+
160+
return (
161+
<ContentList>
162+
<ContentListToolbar>
163+
<Filters>
164+
<Filters.Tags />
165+
<Filters.CreatedBy />
166+
<Filters.Sort />
167+
</Filters>
168+
</ContentListToolbar>
169+
<ContentListTable title="Maps">
170+
<Column.Name showDescription showTags />
171+
<Column.CreatedBy />
172+
<Column.UpdatedAt />
173+
{!isReadOnly && (
174+
<Column.Actions>
175+
<Action.ContentEditor />
176+
<Action.Delete />
177+
</Column.Actions>
178+
)}
179+
</ContentListTable>
180+
<ContentListFooter />
181+
{flyout}
182+
</ContentList>
183+
);
184+
};
185+
150186
const ProposalStory = () => {
151-
const { onInspect, flyout } = useInspectFlyout();
152-
const providerProps = useMapsProviderProps({ onInspect, includeCreatedBy: true });
187+
const { open: openContentEditor, flyout } = useContentEditorFlyout();
188+
const providerProps = useMapsProviderProps({ openContentEditor, includeCreatedBy: true });
153189

154190
const pageElement = useMemo(
155191
() => (
@@ -163,26 +199,7 @@ const ProposalStory = () => {
163199
}
164200
/>
165201
<KibanaContentListPage.Section>
166-
<ContentList>
167-
<ContentListToolbar>
168-
<Filters>
169-
<Filters.Tags />
170-
<Filters.CreatedBy />
171-
<Filters.Sort />
172-
</Filters>
173-
</ContentListToolbar>
174-
<ContentListTable title="Maps">
175-
<Column.Name showDescription showTags />
176-
<Column.CreatedBy />
177-
<Column.UpdatedAt />
178-
<Column.Actions>
179-
<Action.Inspect />
180-
<Action.Delete />
181-
</Column.Actions>
182-
</ContentListTable>
183-
<ContentListFooter />
184-
{flyout}
185-
</ContentList>
202+
<MapsProposalListBody flyout={flyout} />
186203
</KibanaContentListPage.Section>
187204
</KibanaContentListPage>
188205
),

src/platform/packages/shared/content-management/content_list/kbn-content-list-docs/src/playground/builder_panel.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,6 @@ export const BuilderPanel = ({ state, dispatch }: BuilderPanelProps) => {
259259
onChange={(v) => dispatch({ type: 'SET_ITEM_PROP', key: 'onDelete', value: v })}
260260
/>
261261
</JsxPropDisplay>
262-
<JsxPropDisplay name="actions.inspect">
263-
<InlineCheckbox
264-
id={`${idPrefix}-onInspect`}
265-
checked={item.onInspect}
266-
onChange={(v) => dispatch({ type: 'SET_ITEM_PROP', key: 'onInspect', value: v })}
267-
/>
268-
</JsxPropDisplay>
269262
</JsxPropBlock>
270263

271264
{/* features prop */}
@@ -312,6 +305,13 @@ export const BuilderPanel = ({ state, dispatch }: BuilderPanelProps) => {
312305
onChange={(v) => dispatch({ type: 'SET_FEATURE', key: 'userProfiles', value: v })}
313306
/>
314307
</JsxPropDisplay>
308+
<JsxPropDisplay name="contentEditor.open">
309+
<InlineCheckbox
310+
id={`${idPrefix}-contentEditor`}
311+
checked={features.contentEditor}
312+
onChange={(v) => dispatch({ type: 'SET_FEATURE', key: 'contentEditor', value: v })}
313+
/>
314+
</JsxPropDisplay>
315315
</JsxPropBlock>
316316
</JsxTag>
317317

@@ -400,9 +400,11 @@ export const BuilderPanel = ({ state, dispatch }: BuilderPanelProps) => {
400400
col.type === 'actions' &&
401401
!item.onEdit &&
402402
!item.onDelete &&
403-
!item.onInspect &&
404-
!col.actions.some((a) => !['edit', 'delete', 'inspect'].includes(a.type))
405-
? 'This column is hidden because no item actions (actions.edit, actions.delete, actions.inspect) are configured on the provider and no custom actions are present.'
403+
!features.contentEditor &&
404+
!col.actions.some(
405+
(a) => !['edit', 'delete', 'contentEditor'].includes(a.type)
406+
)
407+
? 'This column is hidden because no row entry points are wired (actions.edit, actions.delete, features.contentEditor.open) and no custom actions are present.'
406408
: undefined
407409
}
408410
/>

src/platform/packages/shared/content-management/content_list/kbn-content-list-docs/src/playground/instructions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ All three child components read their state from the provider via context, so th
1919

2020
The JSX-shaped tree on the left mirrors the component hierarchy you are building. Each node has inline controls:
2121

22-
- **Provider props** — toggle features like sorting, pagination, and search; set entity labels; enable item-level navigation (`getHref`) and action handlers (`actions.edit`, `actions.delete`, `actions.inspect`).
22+
- **Provider props** — toggle features like sorting, pagination, search, and the content editor flyout (`features.contentEditor.open`); set entity labels; enable item-level navigation (`getHref`) and action handlers (`actions.edit`, `actions.delete`).
2323
- **Columns** — drag to reorder, click the arrow to edit props (`width`, `columnTitle`, etc.), or press ✕ to remove.
2424
- **Actions** — nested inside `Column.Actions`; drag to reorder or remove.
2525
- **Filters** — toolbar filter components like `Filters.Sort`.
@@ -44,6 +44,7 @@ Actions are children of `Column.Actions`:
4444
| Component | Description |
4545
|---|---|
4646
| `Action.Edit` | Opens the item for editing (requires `actions.edit.onItemAction`). |
47+
| `Action.ContentEditor` | Opens the Kibana content editor flyout (requires `features.contentEditor.open`). The action self-skips when the open callback isn't wired. |
4748
| `Action.Delete` | Triggers a delete-confirmation modal (requires `actions.delete.onBulkAction`). |
4849
| `Action (Export)` | Custom action example — demonstrates the base `Action` API. Behavior is supplied via `actions.export.onItemAction` on the provider's item config. |
4950

0 commit comments

Comments
 (0)