Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Files] Allow option to disable delete action in mgt UI #155179

Merged
merged 19 commits into from
Apr 24, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
17 changes: 16 additions & 1 deletion examples/files_example/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,13 @@
import type { FileKind } from '@kbn/files-plugin/common';
import type { FileImageMetadata } from '@kbn/shared-ux-file-types';

export const PLUGIN_ID = 'filesExample';
export const PLUGIN_ID = 'filesExample' as const;
export const PLUGIN_NAME = 'Files example';
export const NO_MGT_LIST_FILE_KIND = `${PLUGIN_ID}NoMgtList` as const;
export const NO_MGT_DELETE_FILE_KIND = `${PLUGIN_ID}NoMgtDelete` as const;
export const FILE_KIND_IDS = [PLUGIN_ID, NO_MGT_LIST_FILE_KIND, NO_MGT_DELETE_FILE_KIND] as const;

export type FileTypeId = typeof FILE_KIND_IDS[number];

const httpTags = {
tags: [`access:${PLUGIN_ID}`],
Expand All @@ -30,4 +35,14 @@ export const exampleFileKind: FileKind = {
},
};

export const exampleFileKindNotListedInMangementUI: FileKind = {
...exampleFileKind,
id: NO_MGT_LIST_FILE_KIND,
};

export const exampleFileKindNotDeletableInMangementUI: FileKind = {
...exampleFileKind,
id: NO_MGT_DELETE_FILE_KIND,
};

export type MyImageMetadata = FileImageMetadata;
181 changes: 157 additions & 24 deletions examples/files_example/public/components/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
* Side Public License, v 1.
*/

import React, { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import React, { FC, useState } from 'react';
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import type { FileJSON } from '@kbn/files-plugin/common';
import type { FilesClientResponses } from '@kbn/files-plugin/public';

Expand All @@ -19,11 +19,14 @@ import {
EuiIcon,
EuiButtonIcon,
EuiLink,
EuiTitle,
EuiSpacer,
EuiText,
} from '@elastic/eui';

import { CoreStart } from '@kbn/core/public';
import { MyFilePicker } from './file_picker';
import type { MyImageMetadata } from '../../common';
import type { FileTypeId, MyImageMetadata } from '../../common';
import type { FileClients } from '../types';
import { DetailsFlyout } from './details_flyout';
import { ConfirmButtonIcon } from './confirm_button';
Expand All @@ -36,16 +39,31 @@ interface FilesExampleAppDeps {

type ListResponse = FilesClientResponses<MyImageMetadata>['list'];

export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) => {
const { data, isLoading, error, refetch } = useQuery<ListResponse>(
['files'],
() => files.example.list(),
{ refetchOnWindowFocus: false }
);
const [showUploadModal, setShowUploadModal] = useState(false);
const [showFilePickerModal, setShowFilePickerModal] = useState(false);
interface FilesTableProps extends FilesExampleAppDeps {
title: string;
description?: string | React.ReactNode;
data?: ListResponse;
isLoading: boolean;
error: unknown;
refetch: () => Promise<unknown>;
setShowUploadModal: (value: boolean) => void;
setShowFilePickerModal: (value: boolean) => void;
setSelectedItem: (value: FileJSON<MyImageMetadata>) => void;
}

const FilesTable: FC<FilesTableProps> = ({
title,
description,
files,
data,
isLoading,
error,
refetch,
setSelectedItem,
setShowFilePickerModal,
setShowUploadModal,
}) => {
const [isDeletingFile, setIsDeletingFile] = useState(false);
const [selectedItem, setSelectedItem] = useState<undefined | FileJSON<MyImageMetadata>>();

const renderToolsRight = () => {
return [
Expand Down Expand Up @@ -130,22 +148,135 @@ export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) =
},
];

return (
<>
<EuiTitle size="s">
<h2>{title}</h2>
</EuiTitle>
<EuiSpacer size="s" />
{!!description && (
<>
<EuiSpacer size="s" />
<EuiText>{description}</EuiText>
</>
)}
<EuiInMemoryTable
columns={columns}
items={items}
itemId="id"
loading={isLoading || isDeletingFile}
error={error ? JSON.stringify(error) : undefined}
sorting
search={{
toolsRight: renderToolsRight(),
}}
pagination
/>
</>
);
};

export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) => {
sebelga marked this conversation as resolved.
Show resolved Hide resolved
const exampleFilesQuery = useQuery<ListResponse>(['files'], () => files.example.list(), {
refetchOnWindowFocus: false,
});

const exampleFilesNotListedQuery = useQuery<ListResponse>(
['filesNotListed'],
() => files.exampleNotListed.list(),
{
refetchOnWindowFocus: false,
}
);

const exampleFilesNotDeletableQuery = useQuery<ListResponse>(
['filesNotDeletable'],
() => files.exampleNotDeletable.list(),
{
refetchOnWindowFocus: false,
}
);

const [showUploadModal, setShowUploadModal] = useState(false);
const [showFilePickerModal, setShowFilePickerModal] = useState(false);
const [activeFileTypeId, setActiveFileTypeId] = useState<FileTypeId>('filesExample');
const [selectedItem, setSelectedItem] = useState<undefined | FileJSON<MyImageMetadata>>();

const commonProps = {
files,
notifications,
setSelectedItem,
};

const getOpenModalHandlers = (fileTypeId: FileTypeId) => ({
setShowFilePickerModal: (value: boolean) => {
setActiveFileTypeId(fileTypeId);
setShowFilePickerModal(value);
},
setShowUploadModal: (value: boolean) => {
setActiveFileTypeId(fileTypeId);
setShowUploadModal(value);
},
});

const getUseQueryResult = (fileTypeId: FileTypeId): UseQueryResult => {
switch (fileTypeId) {
case 'filesExampleNoMgtList':
return exampleFilesNotListedQuery;
case 'filesExampleNoMgtDelete':
return exampleFilesNotDeletableQuery;
default:
return exampleFilesQuery;
}
};

return (
<>
<EuiPageTemplate restrictWidth>
<EuiPageTemplate.Header pageTitle="Files example" />
<EuiPageTemplate.Section>
<EuiInMemoryTable
columns={columns}
items={items}
itemId="id"
loading={isLoading || isDeletingFile}
error={error ? JSON.stringify(error) : undefined}
sorting
search={{
toolsRight: renderToolsRight(),
{/* Table of files with ALL UI actions in Management UI */}
<FilesTable
{...{
...commonProps,
...exampleFilesQuery,
...getOpenModalHandlers('filesExample'),
title: 'All UI actions in management UI',
}}
/>

<EuiSpacer size="xl" />
{/* Table of files that are not listed in the Management UI */}
<FilesTable
{...{
...commonProps,
...getOpenModalHandlers('filesExampleNoMgtList'),
...exampleFilesNotListedQuery,
title: 'Files not listed in management UI',
description: (
<span>
The files uploaded in this table are not listed in the Management {'>'} Kibana{' '}
{'>'} Files UI
</span>
),
}}
/>

<EuiSpacer size="xl" />
{/* Table of files that are not deletable in the Management UI */}
<FilesTable
{...{
...commonProps,
...getOpenModalHandlers('filesExampleNoMgtDelete'),
...exampleFilesNotDeletableQuery,
title: 'Files not deletable in management UI',
description: (
<span>
The files uploaded in this table are not deletable in the Management {'>'} Kibana{' '}
{'>'} Files UI
</span>
),
}}
pagination
/>
</EuiPageTemplate.Section>
</EuiPageTemplate>
Expand All @@ -159,22 +290,24 @@ export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) =
{showUploadModal && (
<Modal
client={files.unscoped}
fileKind={activeFileTypeId}
onDismiss={() => setShowUploadModal(false)}
onUploaded={() => {
notifications.toasts.addSuccess('Uploaded file!');
refetch();
getUseQueryResult(activeFileTypeId).refetch();
setShowUploadModal(false);
}}
/>
)}
{showFilePickerModal && (
<MyFilePicker
onClose={() => setShowFilePickerModal(false)}
fileKind={activeFileTypeId}
onUpload={() => {
notifications.toasts.addSuccess({
title: 'Uploaded files',
});
refetch();
getUseQueryResult(activeFileTypeId).refetch();
}}
onDone={(ids) => {
notifications.toasts.addSuccess({
Expand Down
7 changes: 3 additions & 4 deletions examples/files_example/public/components/file_picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@
import React from 'react';
import type { FunctionComponent } from 'react';

import { exampleFileKind } from '../../common';

import { FilePicker } from '../imports';

interface Props {
fileKind: string;
onClose: () => void;
onUpload: (ids: string[]) => void;
onDone: (ids: string[]) => void;
}

export const MyFilePicker: FunctionComponent<Props> = ({ onClose, onDone, onUpload }) => {
export const MyFilePicker: FunctionComponent<Props> = ({ fileKind, onClose, onDone, onUpload }) => {
return (
<FilePicker
kind={exampleFileKind.id}
kind={fileKind}
onClose={onClose}
onDone={(files) => onDone(files.map((f) => f.id))}
onUpload={(n) => onUpload(n.map(({ id }) => id))}
Expand Down
12 changes: 4 additions & 8 deletions examples/files_example/public/components/modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
import type { FunctionComponent } from 'react';
import React from 'react';
import { EuiModal, EuiModalHeader, EuiModalBody, EuiText } from '@elastic/eui';
import { exampleFileKind, MyImageMetadata } from '../../common';
import type { MyImageMetadata } from '../../common';
import { FilesClient, FileUpload } from '../imports';

interface Props {
client: FilesClient<MyImageMetadata>;
fileKind: string;
onDismiss: () => void;
onUploaded: () => void;
}

export const Modal: FunctionComponent<Props> = ({ onDismiss, onUploaded, client }) => {
export const Modal: FunctionComponent<Props> = ({ fileKind, onDismiss, onUploaded, client }) => {
return (
<EuiModal onClose={onDismiss}>
<EuiModalHeader>
Expand All @@ -27,12 +28,7 @@ export const Modal: FunctionComponent<Props> = ({ onDismiss, onUploaded, client
</EuiText>
</EuiModalHeader>
<EuiModalBody>
<FileUpload
multiple
kind={exampleFileKind.id}
onDone={onUploaded}
meta={{ custom: 'meta' }}
/>
<FileUpload multiple kind={fileKind} onDone={onUploaded} meta={{ custom: 'meta' }} />
</EuiModalBody>
</EuiModal>
);
Expand Down
34 changes: 33 additions & 1 deletion examples/files_example/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@

import { AppNavLinkStatus } from '@kbn/core-application-browser';
import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { PLUGIN_ID, PLUGIN_NAME, exampleFileKind, MyImageMetadata } from '../common';
import {
PLUGIN_ID,
PLUGIN_NAME,
exampleFileKind,
MyImageMetadata,
exampleFileKindNotListedInMangementUI,
exampleFileKindNotDeletableInMangementUI,
} from '../common';
import { FilesExamplePluginsStart, FilesExamplePluginsSetup } from './types';

export class FilesExamplePlugin
Expand All @@ -22,6 +29,25 @@ export class FilesExamplePlugin
id: exampleFileKind.id,
allowedMimeTypes: exampleFileKind.allowedMimeTypes,
});
files.registerFileKind({
id: exampleFileKindNotListedInMangementUI.id,
allowedMimeTypes: exampleFileKindNotListedInMangementUI.allowedMimeTypes,
managementUiActions: {
list: {
enabled: false,
},
},
});
files.registerFileKind({
id: exampleFileKindNotDeletableInMangementUI.id,
allowedMimeTypes: exampleFileKindNotDeletableInMangementUI.allowedMimeTypes,
managementUiActions: {
delete: {
enabled: false,
reason: 'This file is too cool to be deleted.',
},
},
});

developerExamples.register({
appId: PLUGIN_ID,
Expand All @@ -45,6 +71,12 @@ export class FilesExamplePlugin
files: {
unscoped: deps.files.filesClientFactory.asUnscoped<MyImageMetadata>(),
example: deps.files.filesClientFactory.asScoped<MyImageMetadata>(exampleFileKind.id),
exampleNotListed: deps.files.filesClientFactory.asScoped<MyImageMetadata>(
exampleFileKindNotListedInMangementUI.id
),
exampleNotDeletable: deps.files.filesClientFactory.asScoped<MyImageMetadata>(
exampleFileKindNotDeletableInMangementUI.id
),
},
},
params
Expand Down
4 changes: 4 additions & 0 deletions examples/files_example/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export interface FileClients {
unscoped: FilesClient<MyImageMetadata>;
// Example file kind
example: ScopedFilesClient<MyImageMetadata>;
// Example file kind not listed in the management UI
exampleNotListed: ScopedFilesClient<MyImageMetadata>;
// Example file kind not deletable in the management UI
exampleNotDeletable: ScopedFilesClient<MyImageMetadata>;
}

export interface AppPluginStartDependencies {
Expand Down
Loading