[kibana_react] Clean up any in public non-test files (#111261)

* first pass at any cleanup

* fix types on TableListView

* change generic constraint

* fix lint

* fix TS in no data card

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Jean-Louis Leysens 2021-09-21 10:16:29 +02:00 committed by GitHub
parent 6980064e5e
commit 0af821aaf9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 129 additions and 117 deletions

View file

@ -7,7 +7,7 @@
*/
import { FormattedMessage } from '@kbn/i18n/react';
import { EuiLink, EuiButton, EuiEmptyPrompt } from '@elastic/eui';
import { EuiLink, EuiButton, EuiEmptyPrompt, EuiBasicTableColumn } from '@elastic/eui';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { attemptLoadDashboardByTitle } from '../lib';
import { DashboardAppServices, DashboardRedirect } from '../../types';
@ -231,7 +231,7 @@ const getTableColumns = (
sortable: true,
},
...(savedObjectsTagging ? [savedObjectsTagging.ui.getTableColumnDefinition()] : []),
];
] as unknown as Array<EuiBasicTableColumn<Record<string, unknown>>>;
};
const getNoItemsMessage = (

View file

@ -75,20 +75,6 @@ const Demo = ({ kibana }) => {
export default withKibana(Demo);
```
Using `<UseKibana>` render prop.
```tsx
import { UseKibana } from 'kibana-react';
const Demo = () => {
return (
<UseKibana>
{(kibana) => <div>{kibana.services.uiSettings.get('theme:darkMode') ? 'dark' : 'light'}</div>}
</UseKibana>
);
};
```
## `uiSettings` service
Wrappers around Core's `uiSettings` service.

View file

@ -9,9 +9,9 @@ import React, { useEffect, KeyboardEventHandler } from 'react';
import { monaco } from '@kbn/monaco';
function createEditorInstance() {
const keyDownListeners: any[] = [];
const didShowListeners: any[] = [];
const didHideListeners: any[] = [];
const keyDownListeners: Array<(e?: unknown) => void> = [];
const didShowListeners: Array<(e?: unknown) => void> = [];
const didHideListeners: Array<(e?: unknown) => void> = [];
let areSuggestionsVisible = false;
const editorInstance = {
@ -69,7 +69,10 @@ type MockedEditor = ReturnType<typeof createEditorInstance>;
export const mockedEditorInstance: MockedEditor = createEditorInstance();
// <MonacoEditor /> mock
const mockMonacoEditor = ({ editorWillMount, editorDidMount }: any) => {
const mockMonacoEditor = ({
editorWillMount,
editorDidMount,
}: Record<string, (...args: unknown[]) => void>) => {
editorWillMount(monaco);
useEffect(() => {

View file

@ -135,7 +135,9 @@ export const CodeEditor: React.FC<Props> = ({
const MonacoEditor: typeof ReactMonacoEditor = useMemo(() => {
const isMockedComponent =
typeof ReactMonacoEditor === 'function' && ReactMonacoEditor.name === 'JestMockEditor';
return isMockedComponent ? (ReactMonacoEditor as any)() : ReactMonacoEditor;
return isMockedComponent
? (ReactMonacoEditor as unknown as () => typeof ReactMonacoEditor)()
: ReactMonacoEditor;
}, []);
const isReadOnly = options?.readOnly ?? false;

View file

@ -26,7 +26,7 @@ export const useKibana = <Extra extends object = {}>(): KibanaReactContextValue<
> =>
useContext(context as unknown as React.Context<KibanaReactContextValue<KibanaServices & Extra>>);
export const withKibana = <Props extends { kibana: KibanaReactContextValue<any> }>(
export const withKibana = <Props extends { kibana: KibanaReactContextValue<{}> }>(
type: React.ComponentType<Props>
): React.FC<Omit<Props, 'kibana'>> => {
const EnhancedType: React.FC<Omit<Props, 'kibana'>> = (props: Omit<Props, 'kibana'>) => {
@ -36,10 +36,6 @@ export const withKibana = <Props extends { kibana: KibanaReactContextValue<any>
return EnhancedType;
};
export const UseKibana: React.FC<{
children: (kibana: KibanaReactContextValue<any>) => React.ReactNode;
}> = ({ children }) => <>{children(useKibana())}</>;
export const createKibanaReactContext = <Services extends KibanaServices>(
services: Services
): KibanaReactContext<Services> => {
@ -58,7 +54,7 @@ export const createKibanaReactContext = <Services extends KibanaServices>(
() => createKibanaReactContext({ ...services, ...oldValue.services, ...newServices }),
[services, oldValue, newServices]
);
return createElement(context.Provider as React.ComponentType<any>, {
return createElement(context.Provider, {
value: newValue,
children,
});

View file

@ -12,6 +12,5 @@ export {
KibanaContextProvider,
useKibana,
withKibana,
UseKibana,
} from './context';
export { KibanaReactContext, KibanaReactContextValue, KibanaServices } from './types';

View file

@ -26,6 +26,7 @@ export const ElasticAgentCard: FunctionComponent<ElasticAgentCardProps> = ({
title,
href,
button,
layout,
...cardRest
}) => {
const {
@ -58,7 +59,8 @@ export const ElasticAgentCard: FunctionComponent<ElasticAgentCardProps> = ({
image={addBasePath(`${basePathUrl}elastic_agent_card.svg`)}
betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined}
footer={footer}
{...(cardRest as any)}
layout={layout as 'vertical' | undefined}
{...cardRest}
/>
);
};

View file

@ -23,6 +23,7 @@ export const ElasticBeatsCard: FunctionComponent<ElasticBeatsCardProps> = ({
button,
href,
solution, // unused for now
layout,
...cardRest
}) => {
const {
@ -58,7 +59,8 @@ export const ElasticBeatsCard: FunctionComponent<ElasticBeatsCardProps> = ({
)}
betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined}
footer={footer}
{...(cardRest as any)}
layout={layout as 'vertical' | undefined}
{...cardRest}
/>
);
};

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { i18n } from '@kbn/i18n';
import React, { FunctionComponent } from 'react';
import { EuiButton, EuiCard, EuiCardProps } from '@elastic/eui';
import { NoDataPageActions, NO_DATA_RECOMMENDED } from '../no_data_page';
@ -17,6 +18,7 @@ export const NoDataCard: FunctionComponent<NoDataPageActions> = ({
recommended,
title,
button,
layout,
...cardRest
}) => {
const footer =
@ -25,10 +27,16 @@ export const NoDataCard: FunctionComponent<NoDataPageActions> = ({
return (
<EuiCard
paddingSize="l"
title={title}
// TODO: we should require both title and description to be passed in by consumers since defaults are not adequate.
// see comment: https://github.com/elastic/kibana/pull/111261/files#r708399140
title={title!}
description={i18n.translate('kibana-react.noDataPage.noDataCard.description', {
defaultMessage: `Proceed without collecting data`,
})}
betaBadgeLabel={recommended ? NO_DATA_RECOMMENDED : undefined}
footer={footer}
{...(cardRest as any)}
layout={layout as 'vertical' | undefined}
{...cardRest}
/>
);
};

View file

@ -7,6 +7,7 @@
*/
import { ScopedHistory } from 'kibana/public';
import { MouseEvent } from 'react';
import { History, parsePath } from 'history';
interface LocationObject {
@ -15,10 +16,10 @@ interface LocationObject {
hash?: string;
}
const isModifiedEvent = (event: any) =>
const isModifiedEvent = (event: MouseEvent) =>
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
const isLeftClickEvent = (event: any) => event.button === 0;
const isLeftClickEvent = (event: MouseEvent) => event.button === 0;
export const toLocationObject = (to: string | LocationObject) =>
typeof to === 'string' ? parsePath(to) : to;
@ -34,7 +35,7 @@ export const reactRouterNavigate = (
export const reactRouterOnClickHandler =
(history: ScopedHistory | History, to: string | LocationObject, onClickCallback?: Function) =>
(event: any) => {
(event: MouseEvent) => {
if (onClickCallback) {
onClickCallback(event);
}
@ -43,7 +44,9 @@ export const reactRouterOnClickHandler =
return;
}
if (event.target.getAttribute('target')) {
if (
(event.target as unknown as { getAttribute: (a: string) => unknown })?.getAttribute('target')
) {
return;
}

View file

@ -17,7 +17,7 @@ export interface Props {
children: ReactNode;
className?: string;
resizerClassName?: string;
onPanelWidthChange?: (arrayOfPanelWidths: number[]) => any;
onPanelWidthChange?: (arrayOfPanelWidths: number[]) => void;
}
interface State {

View file

@ -12,7 +12,7 @@ import { PanelRegistry } from './registry';
const PanelContext = createContext({ registry: new PanelRegistry() });
interface ContextProps {
children: any;
children: JSX.Element;
registry: PanelRegistry;
}

View file

@ -26,17 +26,13 @@ import React from 'react';
import { KibanaPageTemplate } from '../page_template';
import { toMountPoint } from '../util';
interface Item {
id?: string;
}
export interface TableListViewProps {
export interface TableListViewProps<V> {
createItem?(): void;
deleteItems?(items: object[]): Promise<void>;
editItem?(item: object): void;
deleteItems?(items: V[]): Promise<void>;
editItem?(item: V): void;
entityName: string;
entityNamePlural: string;
findItems(query: string): Promise<{ total: number; hits: object[] }>;
findItems(query: string): Promise<{ total: number; hits: V[] }>;
listingLimit: number;
initialFilter: string;
initialPageSize: number;
@ -44,7 +40,7 @@ export interface TableListViewProps {
* Should be an EuiEmptyPrompt (but TS doesn't support this typing)
*/
emptyPrompt?: JSX.Element;
tableColumns: Array<EuiBasicTableColumn<any>>;
tableColumns: Array<EuiBasicTableColumn<V>>;
tableListTitle: string;
toastNotifications: ToastsStart;
/**
@ -63,8 +59,8 @@ export interface TableListViewProps {
searchFilters?: SearchFilterConfig[];
}
export interface TableListViewState {
items: object[];
export interface TableListViewState<V> {
items: V[];
hasInitialFetchReturned: boolean;
isFetchingItems: boolean;
isDeletingItems: boolean;
@ -81,11 +77,14 @@ export interface TableListViewState {
// and not supporting server-side paging.
// This component does not try to tackle these problems (yet) and is just feature matching the legacy component
// TODO support server side sorting/paging once title and description are sortable on the server.
class TableListView extends React.Component<TableListViewProps, TableListViewState> {
class TableListView<V extends {}> extends React.Component<
TableListViewProps<V>,
TableListViewState<V>
> {
private pagination = {};
private _isMounted = false;
constructor(props: TableListViewProps) {
constructor(props: TableListViewProps<V>) {
super(props);
this.pagination = {
@ -134,7 +133,7 @@ class TableListView extends React.Component<TableListViewProps, TableListViewSta
this.setState({
hasInitialFetchReturned: true,
isFetchingItems: false,
items: !filter ? sortBy(response.hits, 'title') : response.hits,
items: !filter ? sortBy<V>(response.hits, 'title') : response.hits,
totalItems: response.total,
showLimitError: response.total > this.props.listingLimit,
});
@ -404,17 +403,17 @@ class TableListView extends React.Component<TableListViewProps, TableListViewSta
const selection = this.props.deleteItems
? {
onSelectionChange: (obj: Item[]) => {
onSelectionChange: (obj: V[]) => {
this.setState({
selectedIds: obj
.map((item) => item.id)
.filter((id: undefined | string): id is string => Boolean(id)),
.map((item) => (item as Record<string, undefined | string>)?.id)
.filter((id): id is string => Boolean(id)),
});
},
}
: undefined;
const actions: EuiTableActionsColumnType<any>['actions'] = [
const actions: EuiTableActionsColumnType<V>['actions'] = [
{
name: i18n.translate('kibana-react.tableListView.listing.table.editActionName', {
defaultMessage: 'Edit',
@ -427,7 +426,7 @@ class TableListView extends React.Component<TableListViewProps, TableListViewSta
),
icon: 'pencil',
type: 'icon',
enabled: ({ error }: { error: string }) => !error,
enabled: (v) => !(v as unknown as { error: string })?.error,
onClick: this.props.editItem,
},
];

View file

@ -60,12 +60,12 @@ export const MountPointPortal: React.FC<MountPointPortalProps> = ({ children, se
}
};
class MountPointPortalErrorBoundary extends Component<{}, { error?: any }> {
class MountPointPortalErrorBoundary extends Component<{}, { error?: unknown }> {
state = {
error: undefined,
};
static getDerivedStateFromError(error: any) {
static getDerivedStateFromError(error: unknown) {
return { error };
}

View file

@ -6,11 +6,14 @@
* Side Public License, v 1.
*/
export function test(value: any) {
export function test(value?: Record<string, unknown>) {
return value && value.__reactMount__;
}
export function print(value: any, serialize: any) {
export function print(
value: Record<string, unknown>,
serialize: (args: Record<string, unknown>) => { replace: (s1: string, s2: string) => unknown }
) {
// there is no proper way to correctly indent multiline values
// so the trick here is to use the Object representation and rewriting the root object name
return serialize({

View file

@ -8,7 +8,15 @@
import React from 'react';
import { METRIC_TYPE } from '@kbn/analytics';
import { EuiBetaBadge, EuiButton, EuiEmptyPrompt, EuiIcon, EuiLink, EuiBadge } from '@elastic/eui';
import {
EuiBetaBadge,
EuiButton,
EuiEmptyPrompt,
EuiIcon,
EuiLink,
EuiBadge,
EuiBasicTableColumn,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { ApplicationStart } from 'kibana/public';
@ -86,61 +94,62 @@ export const getTableColumns = (
application: ApplicationStart,
kbnUrlStateStorage: IKbnUrlStateStorage,
taggingApi?: SavedObjectsTaggingApi
) => [
{
field: 'title',
name: i18n.translate('visualize.listing.table.titleColumnName', {
defaultMessage: 'Title',
}),
sortable: true,
render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) =>
// In case an error occurs i.e. the vis has wrong type, we render the vis but without the link
!error ? (
<RedirectAppLinks application={application} className="visListingTable__titleLink">
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
<EuiLink
href={getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl)}
onClick={() => {
doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name);
}}
data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`}
>
{field}
</EuiLink>
</RedirectAppLinks>
) : (
field
),
},
{
field: 'typeTitle',
name: i18n.translate('visualize.listing.table.typeColumnName', {
defaultMessage: 'Type',
}),
sortable: true,
render: (field: string, record: VisualizationListItem) =>
!record.error ? (
<span>
{renderItemTypeIcon(record)}
{record.typeTitle}
{getBadge(record)}
</span>
) : (
<EuiBadge iconType="alert" color="warning">
{record.error}
</EuiBadge>
),
},
{
field: 'description',
name: i18n.translate('visualize.listing.table.descriptionColumnName', {
defaultMessage: 'Description',
}),
sortable: true,
render: (field: string, record: VisualizationListItem) => <span>{record.description}</span>,
},
...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []),
];
) =>
[
{
field: 'title',
name: i18n.translate('visualize.listing.table.titleColumnName', {
defaultMessage: 'Title',
}),
sortable: true,
render: (field: string, { editApp, editUrl, title, error, type }: VisualizationListItem) =>
// In case an error occurs i.e. the vis has wrong type, we render the vis but without the link
!error ? (
<RedirectAppLinks application={application} className="visListingTable__titleLink">
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
<EuiLink
href={getVisualizeListItemLink(application, kbnUrlStateStorage, editApp, editUrl)}
onClick={() => {
doTelemetryForAddEvent(typeof type === 'string' ? type : type?.name);
}}
data-test-subj={`visListingTitleLink-${title.split(' ').join('-')}`}
>
{field}
</EuiLink>
</RedirectAppLinks>
) : (
field
),
},
{
field: 'typeTitle',
name: i18n.translate('visualize.listing.table.typeColumnName', {
defaultMessage: 'Type',
}),
sortable: true,
render: (field: string, record: VisualizationListItem) =>
!record.error ? (
<span>
{renderItemTypeIcon(record)}
{record.typeTitle}
{getBadge(record)}
</span>
) : (
<EuiBadge iconType="alert" color="warning">
{record.error}
</EuiBadge>
),
},
{
field: 'description',
name: i18n.translate('visualize.listing.table.descriptionColumnName', {
defaultMessage: 'Description',
}),
sortable: true,
render: (field: string, record: VisualizationListItem) => <span>{record.description}</span>,
},
...(taggingApi ? [taggingApi.ui.getTableColumnDefinition()] : []),
] as unknown as Array<EuiBasicTableColumn<Record<string, unknown>>>;
export const getNoItemsMessage = (createItem: () => void) => (
<EuiEmptyPrompt