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

[Cases] Detail View Attachments Table #152941

Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6095d8a
[Cases] Initial commit.
adcoelho Feb 28, 2023
1948296
Include file context. Create Add file modal.
adcoelho Mar 6, 2023
45a60d3
List attachments.
adcoelho Mar 7, 2023
2c55946
Rebase to use new file kinds in cases.
adcoelho Mar 8, 2023
c14555e
Addressing PR comments.
adcoelho Mar 10, 2023
fa72cd9
Addressing PR comments 2.
adcoelho Mar 10, 2023
fef9094
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Mar 10, 2023
9a225e4
Merge remote-tracking branch 'upstream/cases-detail-view-files-tab' i…
adcoelho Mar 20, 2023
a5a0095
Passed owner in file metadata.
adcoelho Mar 20, 2023
6222a84
Merge remote-tracking branch 'upstream/cases-detail-view-files-tab' i…
adcoelho Mar 20, 2023
c2a4499
Fix type error on observability plugin.
adcoelho Mar 20, 2023
b48f33b
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Mar 20, 2023
9df97e3
Fixed translation issue
adcoelho Mar 20, 2023
6df7064
Created the FilesUtilityBar.
adcoelho Mar 21, 2023
32434d0
Updated mock in test providers.
adcoelho Mar 22, 2023
714d501
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Mar 22, 2023
1c03756
Include showDanger in useCasesToast.
adcoelho Mar 23, 2023
af1f15f
Fix AddFile button in empty FilesTable.
adcoelho Mar 23, 2023
3f1d76f
Use isPreviousData in FilesTable pagination.
adcoelho Mar 24, 2023
df1702c
Merge remote-tracking branch 'upstream/cases-detail-view-files-tab' i…
adcoelho Mar 24, 2023
95926fc
Change files count information in files table.
adcoelho Mar 24, 2023
81b41d1
Add FilePreview tests.
adcoelho Mar 24, 2023
68c3047
Changed implementation of isRegisteredOwner.
adcoelho Mar 27, 2023
e2ad7a5
Fixed failing jest tests.
adcoelho Mar 27, 2023
9fefef4
Add pagination tests for case view files.
adcoelho Mar 27, 2023
bccf790
AddFile disabled if user has no create permission.
adcoelho Mar 27, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { context } from './context';
/**
* An object representing an uploaded file
*/
interface UploadedFile<Meta = unknown> {
export interface UploadedFile<Meta = unknown> {
/**
* The ID that was generated for the uploaded file
*/
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/cases/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@ export type SnakeToCamelCase<T> = T extends Record<string, unknown>
export enum CASE_VIEW_PAGE_TABS {
ALERTS = 'alerts',
ACTIVITY = 'activity',
FILES = 'files',
}
21 changes: 15 additions & 6 deletions x-pack/plugins/cases/public/application.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Router } from 'react-router-dom';

import { I18nProvider } from '@kbn/i18n-react';
import { EuiErrorBoundary } from '@elastic/eui';

import { I18nProvider } from '@kbn/i18n-react';
import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common';
import {
KibanaContextProvider,
KibanaThemeProvider,
useUiSetting$,
} from '@kbn/kibana-react-plugin/public';
import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-react-plugin/common';
import type { RenderAppProps } from './types';
import { CasesApp } from './components/app';

import type { ScopedFilesClient } from '@kbn/files-plugin/public';
import type { ExternalReferenceAttachmentTypeRegistry } from './client/attachment_framework/external_reference_registry';
import type { PersistableStateAttachmentTypeRegistry } from './client/attachment_framework/persistable_state_registry';
import type { RenderAppProps } from './types';

import { CasesApp } from './components/app';

export const renderApp = (deps: RenderAppProps) => {
const { mountParams } = deps;
Expand All @@ -37,17 +39,23 @@ export const renderApp = (deps: RenderAppProps) => {
interface CasesAppWithContextProps {
externalReferenceAttachmentTypeRegistry: ExternalReferenceAttachmentTypeRegistry;
persistableStateAttachmentTypeRegistry: PersistableStateAttachmentTypeRegistry;
getFilesClient: (scope: string) => ScopedFilesClient;
}

const CasesAppWithContext: React.FC<CasesAppWithContextProps> = React.memo(
({ externalReferenceAttachmentTypeRegistry, persistableStateAttachmentTypeRegistry }) => {
({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
}) => {
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');

return (
<StyledComponentsThemeProvider darkMode={darkMode}>
<CasesApp
externalReferenceAttachmentTypeRegistry={externalReferenceAttachmentTypeRegistry}
persistableStateAttachmentTypeRegistry={persistableStateAttachmentTypeRegistry}
getFilesClient={getFilesClient}
/>
</StyledComponentsThemeProvider>
);
Expand Down Expand Up @@ -78,6 +86,7 @@ export const App: React.FC<{ deps: RenderAppProps }> = ({ deps }) => {
deps.externalReferenceAttachmentTypeRegistry
}
persistableStateAttachmentTypeRegistry={deps.persistableStateAttachmentTypeRegistry}
getFilesClient={pluginsStart.files.filesClientFactory.asScoped}
/>
</Router>
</KibanaContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context';
type GetAllCasesSelectorModalPropsInternal = AllCasesSelectorModalProps & CasesContextProps;
export type GetAllCasesSelectorModalProps = Omit<
GetAllCasesSelectorModalPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const AllCasesSelectorModalLazy: React.FC<AllCasesSelectorModalProps> = lazy(
Expand All @@ -23,6 +25,7 @@ const AllCasesSelectorModalLazy: React.FC<AllCasesSelectorModalProps> = lazy(
export const getAllCasesSelectorModalLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be missing the picture here, Why do we need getFilesClient separately in selector modal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if sometimes it isn't used (like in the selector modal that doesn't show the case detail view where the files tab is)
the CasesContext wraps its children in the FilesContextProvider that unfortunately needs the files client.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh okay 👍

owner,
permissions,
hiddenStatuses,
Expand All @@ -33,6 +36,7 @@ export const getAllCasesSelectorModalLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
}}
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/cases/public/client/ui/get_cases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import { CasesProvider } from '../../components/cases_context';
type GetCasesPropsInternal = CasesProps & CasesContextProps;
export type GetCasesProps = Omit<
GetCasesPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const CasesRoutesLazy: React.FC<CasesProps> = lazy(() => import('../../components/app/routes'));

export const getCasesLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
basePath,
Expand All @@ -39,6 +42,7 @@ export const getCasesLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
basePath,
Expand Down
12 changes: 10 additions & 2 deletions x-pack/plugins/cases/public/client/ui/get_cases_context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import type { CasesContextProps } from '../../components/cases_context';
export type GetCasesContextPropsInternal = CasesContextProps;
export type GetCasesContextProps = Omit<
CasesContextProps,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const CasesProviderLazy: React.FC<{ value: GetCasesContextPropsInternal }> = lazy(
Expand All @@ -28,6 +30,7 @@ const CasesProviderLazyWrapper = ({
features,
children,
releasePhase,
getFilesClient,
}: GetCasesContextPropsInternal & { children: ReactNode }) => {
return (
<Suspense fallback={<EuiLoadingSpinner />}>
Expand All @@ -39,6 +42,7 @@ const CasesProviderLazyWrapper = ({
permissions,
features,
releasePhase,
getFilesClient,
}}
>
{children}
Expand All @@ -52,9 +56,12 @@ CasesProviderLazyWrapper.displayName = 'CasesProviderLazyWrapper';
export const getCasesContextLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
}: Pick<
GetCasesContextPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>): (() => React.FC<GetCasesContextProps>) => {
const CasesProviderLazyWrapperWithRegistry: React.FC<GetCasesContextProps> = ({
children,
Expand All @@ -64,6 +71,7 @@ export const getCasesContextLazy = ({
{...props}
externalReferenceAttachmentTypeRegistry={externalReferenceAttachmentTypeRegistry}
persistableStateAttachmentTypeRegistry={persistableStateAttachmentTypeRegistry}
getFilesClient={getFilesClient}
>
{children}
</CasesProviderLazyWrapper>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { CasesProvider } from '../../components/cases_context';
type GetCreateCaseFlyoutPropsInternal = CreateCaseFlyoutProps & CasesContextProps;
export type GetCreateCaseFlyoutProps = Omit<
GetCreateCaseFlyoutPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

export const CreateCaseFlyoutLazy: React.FC<CreateCaseFlyoutProps> = lazy(
Expand All @@ -23,6 +25,7 @@ export const CreateCaseFlyoutLazy: React.FC<CreateCaseFlyoutProps> = lazy(
export const getCreateCaseFlyoutLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
features,
Expand All @@ -35,6 +38,7 @@ export const getCreateCaseFlyoutLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
features,
Expand Down
6 changes: 5 additions & 1 deletion x-pack/plugins/cases/public/client/ui/get_recent_cases.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import type { RecentCasesProps } from '../../components/recent_cases';
type GetRecentCasesPropsInternal = RecentCasesProps & CasesContextProps;
export type GetRecentCasesProps = Omit<
GetRecentCasesPropsInternal,
'externalReferenceAttachmentTypeRegistry' | 'persistableStateAttachmentTypeRegistry'
| 'externalReferenceAttachmentTypeRegistry'
| 'persistableStateAttachmentTypeRegistry'
| 'getFilesClient'
>;

const RecentCasesLazy: React.FC<RecentCasesProps> = lazy(
Expand All @@ -23,6 +25,7 @@ const RecentCasesLazy: React.FC<RecentCasesProps> = lazy(
export const getRecentCasesLazy = ({
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
maxCasesToShow,
Expand All @@ -31,6 +34,7 @@ export const getRecentCasesLazy = ({
value={{
externalReferenceAttachmentTypeRegistry,
persistableStateAttachmentTypeRegistry,
getFilesClient,
owner,
permissions,
}}
Expand Down
45 changes: 35 additions & 10 deletions x-pack/plugins/cases/public/common/mock/test_providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,29 @@

import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { ThemeProvider } from 'styled-components';

import type { RenderOptions, RenderResult } from '@testing-library/react';
import type { ILicense } from '@kbn/licensing-plugin/public';
import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import type { ScopedFilesClient } from '@kbn/files-plugin/public';

import { euiDarkVars } from '@kbn/ui-theme';
import { I18nProvider } from '@kbn/i18n-react';
import { ThemeProvider } from 'styled-components';
import { createMockFilesClient } from '@kbn/shared-ux-file-mocks';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

import type { RenderOptions, RenderResult } from '@testing-library/react';
import { render as reactRender } from '@testing-library/react';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
import type { ILicense } from '@kbn/licensing-plugin/public';
import type { FieldHook } from '@kbn/es-ui-shared-plugin/static/forms/hook_form_lib';
import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { FilesContext } from '@kbn/shared-ux-file-context';

import type { CasesFeatures, CasesPermissions } from '../../../common/ui/types';
import { CasesProvider } from '../../components/cases_context';
import { createStartServicesMock } from '../lib/kibana/kibana_react.mock';
import type { StartServices } from '../../types';
import type { ReleasePhase } from '../../components/types';

import { SECURITY_SOLUTION_OWNER } from '../../../common/constants';
import { CasesProvider } from '../../components/cases_context';
import { createStartServicesMock } from '../lib/kibana/kibana_react.mock';
import { ExternalReferenceAttachmentTypeRegistry } from '../../client/attachment_framework/external_reference_registry';
import { PersistableStateAttachmentTypeRegistry } from '../../client/attachment_framework/persistable_state_registry';
import { allCasesPermissions } from './permissions';
Expand All @@ -37,21 +44,36 @@ interface TestProviderProps {
releasePhase?: ReleasePhase;
externalReferenceAttachmentTypeRegistry?: ExternalReferenceAttachmentTypeRegistry;
persistableStateAttachmentTypeRegistry?: PersistableStateAttachmentTypeRegistry;
getFilesClient?: (scope: string) => ScopedFilesClient;
license?: ILicense;
}
type UiRender = (ui: React.ReactElement, options?: RenderOptions) => RenderResult;

window.scrollTo = jest.fn();

export const mockedFilesClient = createMockFilesClient() as unknown as ScopedFilesClient<unknown>;

// @ts-ignore
mockedFilesClient.getFileKind.mockImplementation(() => ({
id: 'test',
maxSizeBytes: 10000,
http: {},
}));

const mockGetFilesClient = () => mockedFilesClient;

export const mockedTestProvidersOwner = [SECURITY_SOLUTION_OWNER];

/** A utility for wrapping children in the providers required to run most tests */
const TestProvidersComponent: React.FC<TestProviderProps> = ({
children,
features,
owner = [SECURITY_SOLUTION_OWNER],
owner = mockedTestProvidersOwner,
permissions = allCasesPermissions(),
releasePhase = 'ga',
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(),
getFilesClient = mockGetFilesClient,
license,
}) => {
const queryClient = new QueryClient({
Expand Down Expand Up @@ -82,9 +104,10 @@ const TestProvidersComponent: React.FC<TestProviderProps> = ({
features,
owner,
permissions,
getFilesClient,
}}
>
{children}
<FilesContext client={createMockFilesClient()}>{children}</FilesContext>
</CasesProvider>
</MemoryRouter>
</QueryClientProvider>
Expand Down Expand Up @@ -125,11 +148,12 @@ export const testQueryClient = new QueryClient({

export const createAppMockRenderer = ({
features,
owner = [SECURITY_SOLUTION_OWNER],
owner = mockedTestProvidersOwner,
permissions = allCasesPermissions(),
releasePhase = 'ga',
externalReferenceAttachmentTypeRegistry = new ExternalReferenceAttachmentTypeRegistry(),
persistableStateAttachmentTypeRegistry = new PersistableStateAttachmentTypeRegistry(),
getFilesClient = mockGetFilesClient,
license,
}: Omit<TestProviderProps, 'children'> = {}): AppMockRenderer => {
const services = createStartServicesMock({ license });
Expand Down Expand Up @@ -161,6 +185,7 @@ export const createAppMockRenderer = ({
owner,
permissions,
releasePhase,
getFilesClient,
}}
>
{children}
Expand Down
19 changes: 19 additions & 0 deletions x-pack/plugins/cases/public/common/use_cases_toast.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const useKibanaMock = useKibana as jest.Mocked<typeof useKibana>;
describe('Use cases toast hook', () => {
const successMock = jest.fn();
const errorMock = jest.fn();
const dangerMock = jest.fn();
const getUrlForApp = jest.fn().mockReturnValue(`/app/cases/${mockCase.id}`);
const navigateToUrl = jest.fn();

Expand Down Expand Up @@ -54,6 +55,7 @@ describe('Use cases toast hook', () => {
return {
addSuccess: successMock,
addError: errorMock,
addDanger: dangerMock,
};
});

Expand Down Expand Up @@ -352,4 +354,21 @@ describe('Use cases toast hook', () => {
});
});
});

describe('showDangerToast', () => {
it('should show a danger toast', () => {
const { result } = renderHook(
() => {
return useCasesToast();
},
{ wrapper: TestProviders }
);

result.current.showDangerToast('my danger toast');

expect(dangerMock).toHaveBeenCalledWith({
title: 'my danger toast',
});
});
});
});
3 changes: 3 additions & 0 deletions x-pack/plugins/cases/public/common/use_cases_toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ export const useCasesToast = () => {
showSuccessToast: (title: string) => {
toasts.addSuccess({ title, className: 'eui-textBreakWord' });
},
showDangerToast: (title: string) => {
toasts.addDanger({ title });
},
};
};

Expand Down
Loading