-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
[Cases] File user action in Case detail view #153957
[Cases] File user action in Case detail view #153957
Conversation
Registered FileType in attachment registry. Created FileDownloadButtonIcon. Created FileNameLink. Added filesContext to FilePreview. Updated file external reference metadata. Added missing translations. Added hideDefaultActions in attachment props. Created isValidFileExternalReferenceMetadata util. Added tests.
Pinging @elastic/response-ops (Team:ResponseOps) |
Pinging @elastic/response-ops-cases (Feature:Cases) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, I left a few questions
iconType={'download'} | ||
aria-label={i18n.DOWNLOAD} | ||
href={filesClient.getDownloadHref({ | ||
fileKind: constructFileKindIdByOwner(owner[0] as Owner), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah bummer that we need the cast, maybe constructFileKindIdByOwner
should just take a string 🤔
const fileId = props.externalReferenceId; | ||
|
||
// @ts-ignore | ||
const partialFileJSON = props.externalReferenceMetadata?.files[0] as Partial<FileJSON>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we create a subset type so we don't need to cast here?
Something like this:
type DownloadableFile = Pick<FileJSON, 'extension' | 'fileKind' | 'id' | 'mimeType' | 'name'>;
And use that throughout where we need it. That way we don't have to cast and we don't have to fake it being a FileJSON
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But won't we have to cast it instead to DownloadableFile
? I am not sure I understand your suggestion 😅
The problem here is that if I don't cast we get
Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'string | number | boolean | JsonObject | JsonArray'.
Property '0' does not exist on type 'string | number | boolean | JsonObject | JsonArray'
Weird that 0
does not exist on JsonArray
.
Still, this comes from CommentRequestExternalReferenceType['externalReferenceMetadata'];
where we can't really say what the metadata is going to be since it depends on the attachment type ™️.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moreover, I don't actually need a cast here the @ts-ignore is rather for the indexing error mentioned above 😫
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To remove the @ts-ignore
you can do
import { JsonArray } from '@kbn/utility-types';
const files = props.externalReferenceMetadata?.files as JsonArray;
const partialFileJSON = files[0] as Partial<FileJSON>;
I don't think we can avoid the casting as the framework has a bug with the types and cannot detect the correct types based on the registered type.
{!attachmentViewObject.hideDefaultActions && ( | ||
<RegisteredAttachmentsPropertyActions | ||
isLoading={isLoading} | ||
onDelete={() => handleDeleteComment(comment.id)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we allow deleting a file via the attachment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Next PR I will create the DeleteFileButton
component and I will use it here instead of the RegisteredAttachmentsPropertyActions
and in the files table.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the hideDefaultActions
attribute hides the delete action from files.
x-pack/plugins/cases/public/client/attachment_framework/types.ts
Outdated
Show resolved
Hide resolved
Fixed typechek. Addressed PR comments.
4579f5d
to
16d8b80
Compare
<EuiButtonIcon | ||
iconType={'download'} | ||
aria-label={i18n.DOWNLOAD} | ||
href={filesClient.getDownloadHref({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed through the reviews of the files PRs that we use this pattern a lot. Should we extract the logic of the construction of the href
to a util function? For example:
const getFileDownloadHref = ({ filesClient, fileId, owner }) => filesClient.getDownloadHref({
fileKind: constructFileKindIdByOwner(owner[0] as Owner),
id: fileId,
})
and use it like:
href={getFileDownloadHref( { filesClient, fileId, owner: owner[0] } )}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can but I don't see much difference between
filesClient.getDownloadHref(A, B)
and
newFunction(filesClient.getDownloadHref, A, B)
😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only difference is this Cases logic: fileKind: constructFileKindIdByOwner(owner[0] as Owner)
is replicated over and over again. If we need to change it in the future we have to go to all places to do it. I am fine with leaving it as it is. Not a big deal. I get your point.
const [isPreviewVisible, setIsPreviewVisible] = useState(false); | ||
|
||
const closePreview = () => setIsPreviewVisible(false); | ||
const showPreview = () => setIsPreviewVisible(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we use the same logic elsewhere (x-pack/plugins/cases/public/components/files/files_table.tsx
). What about creating a hook (inside the files folder) called useFilePreview
that encapsulates the previewing logic? For example:
const useFilePreview = () => {
const [isPreviewVisible, setIsPreviewVisible] = useState(false);
const closePreview = () => setIsPreviewVisible(false);
const showPreview = () => setIsPreviewVisible(true);
return { showPreview, closePreview }
}
// consume it as
const { showPreview, closePreview } = useFilePreview();
|
||
export const getFileType = (): ExternalReferenceAttachmentType => ({ | ||
id: FILE_ATTACHMENT_TYPE, | ||
icon: 'image', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This is reserved for future use. It means to be an icon to represent the registered attachment (all files). I think we should change it to document
as it is more generic.
expect(isValidFileExternalReferenceMetadata({ files: 'bar' })).toBeFalsy(); | ||
}); | ||
|
||
it('should return false if file.length !== 1', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I don't think that the function returns false because of the file.length !== 1
. I think it returns false because it is not an array of valid metadata. The check in the isValidFileExternalReferenceMetadata
is externalReferenceMetadata?.files?.length >= 1
. Maybe we can delete the test or we can change it to check that it validates an array of two valid metadata.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. This is a leftover from the previous implementation of isValidFileExternalReferenceMetadata
. I'll just remove it altogether.
let AttachmentElement: React.ReactElement; | ||
|
||
const renderCallback = (props: object) => { | ||
const attachmentViewObject = attachmentType.getAttachmentViewObject( | ||
props as ExternalReferenceAttachmentViewProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In combination with the comment here https://github.com/elastic/kibana/pull/153957/files#r1154257681 we should remove the casting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Also to avoid the extra call to getAttachmentViewObject
maybe we can pass the attachmentViewObject
as an argument to renderCallback
.
const props = { | ||
...getAttachmentViewProps(), | ||
caseData: { id: caseData.id, title: caseData.title }, | ||
}; | ||
const attachmentViewObject = attachmentType.getAttachmentViewObject( | ||
props as ExternalReferenceAttachmentViewProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
…nto cases-detail-file-user-activity
Removed test from files utils. Created useFilePreview hook. And tests. Removed unnecessary type casts.
💔 Build FailedFailed CI Steps
Metrics [docs]
History
To update your PR or re-run it, just comment with: cc @adcoelho |
Fixes #151595 ## Summary In this PR we will be merging a feature branch into `main`. This feature branch is a collection of several different PRs with file functionality for cases. - #152941 - #153957 - #154432 - #153853 Most of the code was already reviewed so this will mainly be used for testing. - Files tab in the case detail view. - Attach files to a case. - View a list of all files attached to a case (with pagination). - Preview image files attached to a case. - Search for files attached to a case by file name. - Download files attached to a case. - Users are now able to see file activity in the case detail view. - Image files have a different icon and a clickable file name to preview. - Other files have a standard "document" icon and the name is not clickable. - The file can be downloaded by clicking the download icon. ## Release notes Support file attachments in Cases. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
Fixes #152088
Summary
Users are now able to see file activity in the case detail view.
If the file has invalid metadata (not likely) we display the activity item below: