mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Files] Allow option to disable delete action in mgt UI (#155179)
This commit is contained in:
parent
3d78370aa5
commit
29a10fddc9
18 changed files with 294 additions and 21 deletions
|
@ -8,5 +8,5 @@
|
|||
|
||||
export { TableListView, TableListViewProvider, TableListViewKibanaProvider } from './src';
|
||||
|
||||
export type { UserContentCommonSchema } from './src';
|
||||
export type { UserContentCommonSchema, RowActions } from './src';
|
||||
export type { TableListViewKibanaDependencies } from './src/services';
|
||||
|
|
|
@ -17,7 +17,9 @@ import {
|
|||
SearchFilterConfig,
|
||||
Direction,
|
||||
Query,
|
||||
type EuiTableSelectionType,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useServices } from '../services';
|
||||
import type { Action } from '../actions';
|
||||
|
@ -26,6 +28,7 @@ import type {
|
|||
Props as TableListViewProps,
|
||||
UserContentCommonSchema,
|
||||
} from '../table_list_view';
|
||||
import type { TableItemsRowActions } from '../types';
|
||||
import { TableSortSelect } from './table_sort_select';
|
||||
import { TagFilterPanel } from './tag_filter_panel';
|
||||
import { useTagFilterPanel } from './use_tag_filter_panel';
|
||||
|
@ -51,6 +54,7 @@ interface Props<T extends UserContentCommonSchema> extends State<T>, TagManageme
|
|||
tableColumns: Array<EuiBasicTableColumn<T>>;
|
||||
hasUpdatedAtMetadata: boolean;
|
||||
deleteItems: TableListViewProps<T>['deleteItems'];
|
||||
tableItemsRowActions: TableItemsRowActions;
|
||||
onSortChange: (column: SortColumnField, direction: Direction) => void;
|
||||
onTableChange: (criteria: CriteriaWithPagination<T>) => void;
|
||||
onTableSearchChange: (arg: { query: Query | null; queryText: string }) => void;
|
||||
|
@ -70,6 +74,7 @@ export function Table<T extends UserContentCommonSchema>({
|
|||
entityName,
|
||||
entityNamePlural,
|
||||
tagsToTableItemMap,
|
||||
tableItemsRowActions,
|
||||
deleteItems,
|
||||
tableCaption,
|
||||
onTableChange,
|
||||
|
@ -105,13 +110,32 @@ export function Table<T extends UserContentCommonSchema>({
|
|||
);
|
||||
}, [deleteItems, dispatch, entityName, entityNamePlural, selectedIds.length]);
|
||||
|
||||
const selection = deleteItems
|
||||
? {
|
||||
const selection = useMemo<EuiTableSelectionType<T> | undefined>(() => {
|
||||
if (deleteItems) {
|
||||
return {
|
||||
onSelectionChange: (obj: T[]) => {
|
||||
dispatch({ type: 'onSelectionChange', data: obj });
|
||||
},
|
||||
}
|
||||
: undefined;
|
||||
selectable: (obj) => {
|
||||
const actions = tableItemsRowActions[obj.id];
|
||||
return actions?.delete?.enabled !== false;
|
||||
},
|
||||
selectableMessage: (selectable, obj) => {
|
||||
if (!selectable) {
|
||||
const actions = tableItemsRowActions[obj.id];
|
||||
return (
|
||||
actions?.delete?.reason ??
|
||||
i18n.translate('contentManagement.tableList.actionsDisabledLabel', {
|
||||
defaultMessage: 'Actions disabled for this item',
|
||||
})
|
||||
);
|
||||
}
|
||||
return '';
|
||||
},
|
||||
initialSelected: [],
|
||||
};
|
||||
}
|
||||
}, [deleteItems, dispatch, tableItemsRowActions]);
|
||||
|
||||
const {
|
||||
isPopoverOpen,
|
||||
|
@ -214,6 +238,7 @@ export function Table<T extends UserContentCommonSchema>({
|
|||
data-test-subj="itemsInMemTable"
|
||||
rowHeader="attributes.title"
|
||||
tableCaption={tableCaption}
|
||||
isSelectable
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -15,3 +15,5 @@ export type {
|
|||
} from './table_list_view';
|
||||
|
||||
export { TableListViewProvider, TableListViewKibanaProvider } from './services';
|
||||
|
||||
export type { RowActions } from './types';
|
||||
|
|
|
@ -1067,4 +1067,109 @@ describe('TableListView', () => {
|
|||
expect(router?.history.location?.search).toBe('?sort=title&sortdir=desc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('row item actions', () => {
|
||||
const hits: UserContentCommonSchema[] = [
|
||||
{
|
||||
id: '123',
|
||||
updatedAt: twoDaysAgo.toISOString(),
|
||||
type: 'dashboard',
|
||||
attributes: {
|
||||
title: 'Item 1',
|
||||
description: 'Item 1 description',
|
||||
},
|
||||
references: [],
|
||||
},
|
||||
{
|
||||
id: '456',
|
||||
updatedAt: yesterday.toISOString(),
|
||||
type: 'dashboard',
|
||||
attributes: {
|
||||
title: 'Item 2',
|
||||
description: 'Item 2 description',
|
||||
},
|
||||
references: [],
|
||||
},
|
||||
];
|
||||
|
||||
const setupTest = async (props?: Partial<TableListViewProps>) => {
|
||||
let testBed: TestBed | undefined;
|
||||
const deleteItems = jest.fn();
|
||||
await act(async () => {
|
||||
testBed = await setup({
|
||||
findItems: jest.fn().mockResolvedValue({ total: hits.length, hits }),
|
||||
deleteItems,
|
||||
...props,
|
||||
});
|
||||
});
|
||||
|
||||
testBed!.component.update();
|
||||
return { testBed: testBed!, deleteItems };
|
||||
};
|
||||
|
||||
test('should allow select items to be deleted', async () => {
|
||||
const {
|
||||
testBed: { table, find, exists, component, form },
|
||||
deleteItems,
|
||||
} = await setupTest();
|
||||
|
||||
const { tableCellsValues } = table.getMetaData('itemsInMemTable');
|
||||
|
||||
expect(tableCellsValues).toEqual([
|
||||
['', 'Item 2Item 2 description', yesterdayToString], // First empty col is the "checkbox"
|
||||
['', 'Item 1Item 1 description', twoDaysAgoToString],
|
||||
]);
|
||||
|
||||
const selectedHit = hits[1];
|
||||
|
||||
expect(exists('deleteSelectedItems')).toBe(false);
|
||||
act(() => {
|
||||
// Select the second item
|
||||
form.selectCheckBox(`checkboxSelectRow-${selectedHit.id}`);
|
||||
});
|
||||
component.update();
|
||||
// Delete button is now visible
|
||||
expect(exists('deleteSelectedItems')).toBe(true);
|
||||
|
||||
// Click delete and validate that confirm modal opens
|
||||
expect(component.exists('.euiModal--confirmation')).toBe(false);
|
||||
act(() => {
|
||||
find('deleteSelectedItems').simulate('click');
|
||||
});
|
||||
component.update();
|
||||
expect(component.exists('.euiModal--confirmation')).toBe(true);
|
||||
|
||||
await act(async () => {
|
||||
find('confirmModalConfirmButton').simulate('click');
|
||||
});
|
||||
expect(deleteItems).toHaveBeenCalledWith([selectedHit]);
|
||||
});
|
||||
|
||||
test('should allow to disable the "delete" action for a row', async () => {
|
||||
const reasonMessage = 'This file cannot be deleted.';
|
||||
|
||||
const {
|
||||
testBed: { find },
|
||||
} = await setupTest({
|
||||
rowItemActions: (obj) => {
|
||||
if (obj.id === hits[1].id) {
|
||||
return {
|
||||
delete: {
|
||||
enabled: false,
|
||||
reason: reasonMessage,
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const firstCheckBox = find(`checkboxSelectRow-${hits[0].id}`);
|
||||
const secondCheckBox = find(`checkboxSelectRow-${hits[1].id}`);
|
||||
|
||||
expect(firstCheckBox.props().disabled).toBe(false);
|
||||
expect(secondCheckBox.props().disabled).toBe(true);
|
||||
// EUI changes the check "title" from "Select this row" to the reason to disable the checkbox
|
||||
expect(secondCheckBox.props().title).toBe(reasonMessage);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -42,6 +42,7 @@ import { getReducer } from './reducer';
|
|||
import type { SortColumnField } from './components';
|
||||
import { useTags } from './use_tags';
|
||||
import { useInRouterContext, useUrlState } from './use_url_state';
|
||||
import { RowActions, TableItemsRowActions } from './types';
|
||||
|
||||
interface ContentEditorConfig
|
||||
extends Pick<OpenContentEditorParams, 'isReadonly' | 'onSave' | 'customValidators'> {
|
||||
|
@ -67,6 +68,11 @@ export interface Props<T extends UserContentCommonSchema = UserContentCommonSche
|
|||
headingId?: string;
|
||||
/** An optional id for the listing. Used to generate unique data-test-subj. Default: "userContent" */
|
||||
id?: string;
|
||||
/**
|
||||
* Configuration of the table row item actions. Disable specific action for a table row item.
|
||||
* Currently only the "delete" ite action can be disabled.
|
||||
*/
|
||||
rowItemActions?: (obj: T) => RowActions | undefined;
|
||||
children?: ReactNode | undefined;
|
||||
findItems(
|
||||
searchQuery: string,
|
||||
|
@ -241,6 +247,7 @@ function TableListViewComp<T extends UserContentCommonSchema>({
|
|||
urlStateEnabled = true,
|
||||
customTableColumn,
|
||||
emptyPrompt,
|
||||
rowItemActions,
|
||||
findItems,
|
||||
createItem,
|
||||
editItem,
|
||||
|
@ -580,6 +587,15 @@ function TableListViewComp<T extends UserContentCommonSchema>({
|
|||
return selectedIds.map((selectedId) => itemsById[selectedId]);
|
||||
}, [selectedIds, itemsById]);
|
||||
|
||||
const tableItemsRowActions = useMemo(() => {
|
||||
return items.reduce<TableItemsRowActions>((acc, item) => {
|
||||
return {
|
||||
...acc,
|
||||
[item.id]: rowItemActions ? rowItemActions(item) : undefined,
|
||||
};
|
||||
}, {});
|
||||
}, [items, rowItemActions]);
|
||||
|
||||
// ------------
|
||||
// Callbacks
|
||||
// ------------
|
||||
|
@ -854,6 +870,20 @@ function TableListViewComp<T extends UserContentCommonSchema>({
|
|||
};
|
||||
}, []);
|
||||
|
||||
const PageTemplate = useMemo<typeof KibanaPageTemplate>(() => {
|
||||
return withoutPageTemplateWrapper
|
||||
? ((({
|
||||
children: _children,
|
||||
'data-test-subj': dataTestSubj,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
['data-test-subj']?: string;
|
||||
}) => (
|
||||
<div data-test-subj={dataTestSubj}>{_children}</div>
|
||||
)) as unknown as typeof KibanaPageTemplate)
|
||||
: KibanaPageTemplate;
|
||||
}, [withoutPageTemplateWrapper]);
|
||||
|
||||
// ------------
|
||||
// Render
|
||||
// ------------
|
||||
|
@ -861,10 +891,6 @@ function TableListViewComp<T extends UserContentCommonSchema>({
|
|||
return null;
|
||||
}
|
||||
|
||||
const PageTemplate = withoutPageTemplateWrapper
|
||||
? (React.Fragment as unknown as typeof KibanaPageTemplate)
|
||||
: KibanaPageTemplate;
|
||||
|
||||
if (!showFetchError && hasNoItems) {
|
||||
return (
|
||||
<PageTemplate panelled isEmptyState={true} data-test-subj={pageDataTestSubject}>
|
||||
|
@ -929,6 +955,7 @@ function TableListViewComp<T extends UserContentCommonSchema>({
|
|||
tagsToTableItemMap={tagsToTableItemMap}
|
||||
deleteItems={deleteItems}
|
||||
tableCaption={tableListTitle}
|
||||
tableItemsRowActions={tableItemsRowActions}
|
||||
onTableChange={onTableChange}
|
||||
onTableSearchChange={onTableSearchChange}
|
||||
onSortChange={onSortChange}
|
||||
|
|
|
@ -12,3 +12,16 @@ export interface Tag {
|
|||
description: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export type TableRowAction = 'delete';
|
||||
|
||||
export type RowActions = {
|
||||
[action in TableRowAction]?: {
|
||||
enabled: boolean;
|
||||
reason?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export interface TableItemsRowActions {
|
||||
[id: string]: RowActions | undefined;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ export interface BaseFilesClient<M = unknown> {
|
|||
find: (
|
||||
args: {
|
||||
kind?: string | string[];
|
||||
kindToExclude?: string | string[];
|
||||
status?: string | string[];
|
||||
extension?: string | string[];
|
||||
name?: string | string[];
|
||||
|
|
|
@ -250,6 +250,22 @@ export interface FileKindBrowser extends FileKindBase {
|
|||
* @default 4MiB
|
||||
*/
|
||||
maxSizeBytes?: number;
|
||||
/**
|
||||
* Allowed actions that can be done in the File Management UI. If not provided, all actions are allowed
|
||||
*
|
||||
*/
|
||||
managementUiActions?: {
|
||||
/** Allow files to be listed in management UI */
|
||||
list?: {
|
||||
enabled: boolean;
|
||||
};
|
||||
/** Allow files to be deleted in management UI */
|
||||
delete?: {
|
||||
enabled: boolean;
|
||||
/** If delete is not enabled in management UI, specify the reason (will appear in a tooltip). */
|
||||
reason?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -35,7 +35,10 @@ export interface FilesSetup {
|
|||
registerFileKind(fileKind: FileKindBrowser): void;
|
||||
}
|
||||
|
||||
export type FilesStart = Pick<FilesSetup, 'filesClientFactory'>;
|
||||
export type FilesStart = Pick<FilesSetup, 'filesClientFactory'> & {
|
||||
getFileKindDefinition: (id: string) => FileKindBrowser;
|
||||
getAllFindKindDefinitions: () => FileKindBrowser[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Bringing files to Kibana
|
||||
|
@ -77,6 +80,12 @@ export class FilesPlugin implements Plugin<FilesSetup, FilesStart> {
|
|||
start(core: CoreStart): FilesStart {
|
||||
return {
|
||||
filesClientFactory: this.filesClientFactory!,
|
||||
getFileKindDefinition: (id: string): FileKindBrowser => {
|
||||
return this.registry.get(id);
|
||||
},
|
||||
getAllFindKindDefinitions: (): FileKindBrowser[] => {
|
||||
return this.registry.getAll();
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ export function filterArgsToKuery({
|
|||
extension,
|
||||
mimeType,
|
||||
kind,
|
||||
kindToExclude,
|
||||
meta,
|
||||
name,
|
||||
status,
|
||||
|
@ -50,12 +51,27 @@ export function filterArgsToKuery({
|
|||
}
|
||||
};
|
||||
|
||||
const addExcludeFilters = (fieldName: keyof FileMetadata | string, values: string[] = []) => {
|
||||
if (values.length) {
|
||||
const andExpressions = values
|
||||
.filter(Boolean)
|
||||
.map((value) =>
|
||||
nodeTypes.function.buildNode(
|
||||
'not',
|
||||
nodeBuilder.is(`${attrPrefix}.${fieldName}`, escapeKuery(value))
|
||||
)
|
||||
);
|
||||
kueryExpressions.push(nodeBuilder.and(andExpressions));
|
||||
}
|
||||
};
|
||||
|
||||
addFilters('name', name, true);
|
||||
addFilters('FileKind', kind);
|
||||
addFilters('Status', status);
|
||||
addFilters('extension', extension);
|
||||
addFilters('mime_type', mimeType);
|
||||
addFilters('user.id', user);
|
||||
addExcludeFilters('FileKind', kindToExclude);
|
||||
|
||||
if (meta) {
|
||||
const addMetaFilters = pipe(
|
||||
|
|
|
@ -82,6 +82,10 @@ export interface FindFileArgs extends Pagination {
|
|||
* File kind(s), see {@link FileKind}.
|
||||
*/
|
||||
kind?: string[];
|
||||
/**
|
||||
* File kind(s) to exclude from search, see {@link FileKind}.
|
||||
*/
|
||||
kindToExclude?: string[];
|
||||
/**
|
||||
* File name(s).
|
||||
*/
|
||||
|
|
|
@ -157,26 +157,39 @@ describe('FileService', () => {
|
|||
createDisposableFile({ fileKind, name: 'foo-2' }),
|
||||
createDisposableFile({ fileKind, name: 'foo-3' }),
|
||||
createDisposableFile({ fileKind, name: 'test-3' }),
|
||||
createDisposableFile({ fileKind: fileKindNonDefault, name: 'foo-1' }),
|
||||
]);
|
||||
{
|
||||
const { files, total } = await fileService.find({
|
||||
kind: [fileKind],
|
||||
kind: [fileKind, fileKindNonDefault],
|
||||
name: ['foo*'],
|
||||
perPage: 2,
|
||||
page: 1,
|
||||
});
|
||||
expect(files.length).toBe(2);
|
||||
expect(total).toBe(3);
|
||||
expect(total).toBe(4);
|
||||
}
|
||||
|
||||
{
|
||||
const { files, total } = await fileService.find({
|
||||
kind: [fileKind],
|
||||
kind: [fileKind, fileKindNonDefault],
|
||||
name: ['foo*'],
|
||||
perPage: 2,
|
||||
page: 2,
|
||||
});
|
||||
expect(files.length).toBe(1);
|
||||
expect(files.length).toBe(2);
|
||||
expect(total).toBe(4);
|
||||
}
|
||||
|
||||
// Filter out fileKind
|
||||
{
|
||||
const { files, total } = await fileService.find({
|
||||
kindToExclude: [fileKindNonDefault],
|
||||
name: ['foo*'],
|
||||
perPage: 10,
|
||||
page: 1,
|
||||
});
|
||||
expect(files.length).toBe(3); // foo-1 from fileKindNonDefault not returned
|
||||
expect(total).toBe(3);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -30,6 +30,7 @@ export function toArrayOrUndefined(val?: string | string[]): undefined | string[
|
|||
const rt = {
|
||||
body: schema.object({
|
||||
kind: schema.maybe(stringOrArrayOfStrings),
|
||||
kindToExclude: schema.maybe(stringOrArrayOfStrings),
|
||||
status: schema.maybe(stringOrArrayOfStrings),
|
||||
extension: schema.maybe(stringOrArrayOfStrings),
|
||||
name: schema.maybe(nameStringOrArrayOfNameStrings),
|
||||
|
@ -50,12 +51,13 @@ export type Endpoint = CreateRouteDefinition<
|
|||
const handler: CreateHandler<Endpoint> = async ({ files }, req, res) => {
|
||||
const { fileService } = await files;
|
||||
const {
|
||||
body: { meta, extension, kind, name, status },
|
||||
body: { meta, extension, kind, name, status, kindToExclude },
|
||||
query,
|
||||
} = req;
|
||||
|
||||
const { files: results, total } = await fileService.asCurrentUser().find({
|
||||
kind: toArrayOrUndefined(kind),
|
||||
kindToExclude: toArrayOrUndefined(kindToExclude),
|
||||
name: toArrayOrUndefined(name),
|
||||
status: toArrayOrUndefined(status),
|
||||
extension: toArrayOrUndefined(extension),
|
||||
|
|
|
@ -12,20 +12,33 @@ import { EuiButtonEmpty } from '@elastic/eui';
|
|||
import { TableListView, UserContentCommonSchema } from '@kbn/content-management-table-list';
|
||||
import numeral from '@elastic/numeral';
|
||||
import type { FileJSON } from '@kbn/files-plugin/common';
|
||||
|
||||
import { useFilesManagementContext } from './context';
|
||||
import { i18nTexts } from './i18n_texts';
|
||||
import { EmptyPrompt, DiagnosticsFlyout, FileFlyout } from './components';
|
||||
|
||||
type FilesUserContentSchema = UserContentCommonSchema;
|
||||
type FilesUserContentSchema = Omit<UserContentCommonSchema, 'attributes'> & {
|
||||
attributes: {
|
||||
title: string;
|
||||
description?: string;
|
||||
fileKind: string;
|
||||
};
|
||||
};
|
||||
|
||||
function naivelyFuzzify(query: string): string {
|
||||
return query.includes('*') ? query : `*${query}*`;
|
||||
}
|
||||
|
||||
export const App: FunctionComponent = () => {
|
||||
const { filesClient } = useFilesManagementContext();
|
||||
const { filesClient, getFileKindDefinition, getAllFindKindDefinitions } =
|
||||
useFilesManagementContext();
|
||||
const [showDiagnosticsFlyout, setShowDiagnosticsFlyout] = useState<boolean>(false);
|
||||
const [selectedFile, setSelectedFile] = useState<undefined | FileJSON>(undefined);
|
||||
|
||||
const kindToExcludeFromSearch = getAllFindKindDefinitions()
|
||||
.filter(({ managementUiActions }) => managementUiActions?.list?.enabled === false)
|
||||
.map(({ id }) => id);
|
||||
|
||||
return (
|
||||
<div data-test-subj="filesManagementApp">
|
||||
<TableListView<FilesUserContentSchema>
|
||||
|
@ -37,7 +50,10 @@ export const App: FunctionComponent = () => {
|
|||
entityNamePlural={i18nTexts.entityNamePlural}
|
||||
findItems={(searchQuery) =>
|
||||
filesClient
|
||||
.find({ name: searchQuery ? naivelyFuzzify(searchQuery) : undefined })
|
||||
.find({
|
||||
name: searchQuery ? naivelyFuzzify(searchQuery) : undefined,
|
||||
kindToExclude: kindToExcludeFromSearch,
|
||||
})
|
||||
.then(({ files, total }) => ({
|
||||
hits: files.map((file) => ({
|
||||
id: file.id,
|
||||
|
@ -71,6 +87,12 @@ export const App: FunctionComponent = () => {
|
|||
{i18nTexts.diagnosticsFlyoutTitle}
|
||||
</EuiButtonEmpty>,
|
||||
]}
|
||||
rowItemActions={({ attributes }) => {
|
||||
const definition = getFileKindDefinition(attributes.fileKind);
|
||||
return {
|
||||
delete: definition?.managementUiActions?.delete,
|
||||
};
|
||||
}}
|
||||
/>
|
||||
{showDiagnosticsFlyout && (
|
||||
<DiagnosticsFlyout onClose={() => setShowDiagnosticsFlyout(false)} />
|
||||
|
|
|
@ -12,9 +12,16 @@ import type { AppContext } from './types';
|
|||
|
||||
const FilesManagementAppContext = createContext<AppContext>(null as unknown as AppContext);
|
||||
|
||||
export const FilesManagementAppContextProvider: FC<AppContext> = ({ children, filesClient }) => {
|
||||
export const FilesManagementAppContextProvider: FC<AppContext> = ({
|
||||
children,
|
||||
filesClient,
|
||||
getFileKindDefinition,
|
||||
getAllFindKindDefinitions,
|
||||
}) => {
|
||||
return (
|
||||
<FilesManagementAppContext.Provider value={{ filesClient }}>
|
||||
<FilesManagementAppContext.Provider
|
||||
value={{ filesClient, getFileKindDefinition, getAllFindKindDefinitions }}
|
||||
>
|
||||
{children}
|
||||
</FilesManagementAppContext.Provider>
|
||||
);
|
||||
|
|
|
@ -101,4 +101,7 @@ export const i18nTexts = {
|
|||
defaultMessage: 'Upload error',
|
||||
}),
|
||||
} as Record<FileStatus, string>,
|
||||
rowCheckboxDisabled: i18n.translate('filesManagement.table.checkBoxDisabledLabel', {
|
||||
defaultMessage: 'This file cannot be deleted.',
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -30,6 +30,10 @@ export const mountManagementSection = (
|
|||
startDeps: StartDependencies,
|
||||
{ element, history }: ManagementAppMountParams
|
||||
) => {
|
||||
const {
|
||||
files: { filesClientFactory, getAllFindKindDefinitions, getFileKindDefinition },
|
||||
} = startDeps;
|
||||
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
|
@ -41,7 +45,9 @@ export const mountManagementSection = (
|
|||
}}
|
||||
>
|
||||
<FilesManagementAppContextProvider
|
||||
filesClient={startDeps.files.filesClientFactory.asUnscoped()}
|
||||
filesClient={filesClientFactory.asUnscoped()}
|
||||
getFileKindDefinition={getFileKindDefinition}
|
||||
getAllFindKindDefinitions={getAllFindKindDefinitions}
|
||||
>
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App} />
|
||||
|
|
|
@ -11,6 +11,8 @@ import { ManagementSetup } from '@kbn/management-plugin/public';
|
|||
|
||||
export interface AppContext {
|
||||
filesClient: FilesClient;
|
||||
getFileKindDefinition: FilesStart['getFileKindDefinition'];
|
||||
getAllFindKindDefinitions: FilesStart['getAllFindKindDefinitions'];
|
||||
}
|
||||
|
||||
export interface SetupDependencies {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue