forked from elastic/kibana
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Files] Create files example plugin (elastic#139019)
* created files example stub * implementing whimsical design * added base64d image * updated files client and example plugin to work end-to-end with browser * added file deletion funcitonality * added codeowners entry * refactor downloadSrc to getDownloadHref * react-query -> @tanstack/react-query Co-authored-by: Kibana Machine <[email protected]>
- Loading branch information
Showing
24 changed files
with
620 additions
and
10 deletions.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"prefix": "filesExample", | ||
"paths": { | ||
"filesExample": "." | ||
}, | ||
"translations": ["translations/ja-JP.json"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# filesExample | ||
|
||
An example plugin that integrates with the Kibana "files" plugin. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import { FileKind } from '@kbn/files-plugin/common'; | ||
|
||
export const PLUGIN_ID = 'filesExample'; | ||
export const PLUGIN_NAME = 'filesExample'; | ||
|
||
const httpTags = { | ||
tags: [`access:${PLUGIN_ID}`], | ||
}; | ||
|
||
export const exampleFileKind: FileKind = { | ||
id: 'filesExample', | ||
http: { | ||
create: httpTags, | ||
delete: httpTags, | ||
download: httpTags, | ||
getById: httpTags, | ||
list: httpTags, | ||
share: httpTags, | ||
update: httpTags, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"id": "filesExample", | ||
"version": "1.0.0", | ||
"kibanaVersion": "kibana", | ||
"owner": { | ||
"name": "kibana-app-services", | ||
"githubTeam": "kibana-app-services" | ||
}, | ||
"description": "Example plugin integrating with files plugin", | ||
"server": true, | ||
"ui": true, | ||
"requiredPlugins": ["files"], | ||
"optionalPlugins": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React from 'react'; | ||
import ReactDOM from 'react-dom'; | ||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; | ||
import { AppMountParameters, CoreStart } from '@kbn/core/public'; | ||
import { AppPluginStartDependencies } from './types'; | ||
import { FilesExampleApp } from './components/app'; | ||
|
||
const queryClient = new QueryClient(); | ||
|
||
export const renderApp = ( | ||
{ notifications }: CoreStart, | ||
{ files }: AppPluginStartDependencies, | ||
{ element }: AppMountParameters | ||
) => { | ||
ReactDOM.render( | ||
<QueryClientProvider client={queryClient}> | ||
<FilesExampleApp files={files} notifications={notifications} /> | ||
</QueryClientProvider>, | ||
element | ||
); | ||
|
||
return () => ReactDOM.unmountComponentAtNode(element); | ||
}; |
Large diffs are not rendered by default.
Oops, something went wrong.
169 changes: 169 additions & 0 deletions
169
x-pack/examples/files_example/public/components/app.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React, { useState } from 'react'; | ||
import { useQuery } from '@tanstack/react-query'; | ||
import type { FileJSON } from '@kbn/files-plugin/common'; | ||
import type { FilesClientResponses } from '@kbn/files-plugin/public'; | ||
|
||
const names = ['foo', 'bar', 'baz']; | ||
|
||
import { | ||
EuiPageTemplate, | ||
EuiInMemoryTable, | ||
EuiInMemoryTableProps, | ||
EuiButton, | ||
EuiIcon, | ||
EuiButtonIcon, | ||
EuiLink, | ||
} from '@elastic/eui'; | ||
|
||
import { CoreStart } from '@kbn/core/public'; | ||
import { DetailsFlyout } from './details_flyout'; | ||
import type { FileClients } from '../types'; | ||
import { ConfirmButtonIcon } from './confirm_button'; | ||
// @ts-ignore | ||
import imageBase64 from '!!raw-loader!../assets/image.png.base64'; | ||
|
||
interface FilesExampleAppDeps { | ||
files: FileClients; | ||
notifications: CoreStart['notifications']; | ||
} | ||
|
||
type ListResponse = FilesClientResponses['list']; | ||
|
||
export const FilesExampleApp = ({ files, notifications }: FilesExampleAppDeps) => { | ||
const { data, isLoading, error, refetch } = useQuery<ListResponse>(['files'], () => | ||
files.example.list() | ||
); | ||
const [isUploadingImage, setIsUploadingImage] = useState(false); | ||
const [isDeletingFile, setIsDeletingFile] = useState(false); | ||
const [selectedItem, setSelectedItem] = useState<undefined | FileJSON>(); | ||
|
||
const uploadImage = async () => { | ||
try { | ||
setIsUploadingImage(true); | ||
const { file } = await files.example.create({ | ||
name: names[Math.floor(Math.random() * names.length)], | ||
alt: 'My image', | ||
meta: { myValue: 'test' }, | ||
mimeType: 'image/png', | ||
}); | ||
await refetch(); | ||
const blob = new Blob([Uint8Array.from(atob(imageBase64), (c) => c.charCodeAt(0))], { | ||
type: 'image/png', | ||
}); | ||
await files.example.upload({ id: file.id, body: blob }); | ||
await refetch(); | ||
notifications.toasts.addSuccess('Sucessfully uploaded image'); | ||
} finally { | ||
setIsUploadingImage(false); | ||
} | ||
}; | ||
|
||
const renderToolsRight = () => { | ||
return [ | ||
<EuiButton | ||
onClick={uploadImage} | ||
isDisabled={isUploadingImage || isLoading || isDeletingFile} | ||
isLoading={isUploadingImage} | ||
iconType="exportAction" | ||
> | ||
Upload image | ||
</EuiButton>, | ||
]; | ||
}; | ||
|
||
const items = [...(data?.files ?? [])].reverse(); | ||
|
||
const columns: EuiInMemoryTableProps<FileJSON>['columns'] = [ | ||
{ | ||
field: 'name', | ||
name: 'Name', | ||
render: (name, item) => <EuiLink onClick={() => setSelectedItem(item)}>{name}</EuiLink>, | ||
}, | ||
{ | ||
field: 'status', | ||
name: 'Status', | ||
render: (status: FileJSON['status']) => | ||
status === 'READY' ? ( | ||
<EuiIcon color="success" type="checkInCircleFilled" aria-label={status} /> | ||
) : status === 'AWAITING_UPLOAD' ? ( | ||
<EuiIcon type="clock" aria-label={status} /> | ||
) : ( | ||
<EuiIcon color="danger" type="alert" arial-label={status} /> | ||
), | ||
}, | ||
{ | ||
name: 'Actions', | ||
actions: [ | ||
{ | ||
name: 'View', | ||
description: 'View file', | ||
isPrimary: true, | ||
render: (item) => ( | ||
<EuiButtonIcon | ||
aria-label="View file details" | ||
iconType="eye" | ||
onClick={() => setSelectedItem(item)} | ||
/> | ||
), | ||
}, | ||
{ | ||
name: 'Delete', | ||
description: 'Delete this file', | ||
render: (item) => ( | ||
<ConfirmButtonIcon | ||
disabled={isDeletingFile} | ||
label="Delete this file" | ||
confirmationText="Are you sure you want to delete this file?" | ||
onConfirm={async () => { | ||
try { | ||
setIsDeletingFile(true); | ||
await files.example.delete({ id: item.id }); | ||
await refetch(); | ||
} finally { | ||
setIsDeletingFile(false); | ||
} | ||
}} | ||
/> | ||
), | ||
}, | ||
], | ||
}, | ||
]; | ||
|
||
return ( | ||
<> | ||
<EuiPageTemplate | ||
pageHeader={{ | ||
pageTitle: 'Files example', | ||
}} | ||
> | ||
<EuiInMemoryTable | ||
columns={columns} | ||
items={items} | ||
itemId="id" | ||
loading={isLoading || isDeletingFile} | ||
error={error ? JSON.stringify(error) : undefined} | ||
sorting | ||
search={{ | ||
toolsRight: renderToolsRight(), | ||
}} | ||
pagination | ||
/> | ||
</EuiPageTemplate> | ||
{selectedItem && ( | ||
<DetailsFlyout | ||
files={files} | ||
file={selectedItem} | ||
onDismiss={() => setSelectedItem(undefined)} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; |
49 changes: 49 additions & 0 deletions
49
x-pack/examples/files_example/public/components/confirm_button.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0; you may not use this file except in compliance with the Elastic License | ||
* 2.0. | ||
*/ | ||
|
||
import React, { useState, FunctionComponent } from 'react'; | ||
import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; | ||
|
||
interface Props { | ||
label: string; | ||
confirmationText: string; | ||
onConfirm: () => void; | ||
disabled: boolean; | ||
} | ||
|
||
export const ConfirmButtonIcon: FunctionComponent<Props> = ({ | ||
label, | ||
confirmationText, | ||
onConfirm, | ||
disabled, | ||
}) => { | ||
const [showConfirm, setShowConfirm] = useState(false); | ||
|
||
return showConfirm ? ( | ||
<EuiToolTip content={confirmationText}> | ||
<EuiButtonIcon | ||
disabled={disabled} | ||
aria-label={confirmationText} | ||
color="warning" | ||
iconType="alert" | ||
onClick={onConfirm} | ||
/> | ||
</EuiToolTip> | ||
) : ( | ||
<EuiButtonIcon | ||
aria-label={label} | ||
disabled={disabled} | ||
color="danger" | ||
iconType="trash" | ||
onClick={(e: { stopPropagation: () => void }) => { | ||
e.stopPropagation(); | ||
setShowConfirm(true); | ||
setTimeout(() => setShowConfirm(false), 3000); | ||
}} | ||
/> | ||
); | ||
}; |
Oops, something went wrong.