From 00d3a3385f5c1b4ace73156d631d033f17893ec0 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Tue, 3 Mar 2020 13:00:26 +0100 Subject: [PATCH 1/2] [ML] Deprecate KibanaContext. --- .../transform/public/app/common/request.ts | 2 +- .../toast_notification_text.test.tsx | 10 +- .../use_search_items}/common.ts | 19 +- .../app/hooks/use_search_items/index.ts | 8 + .../use_search_items/use_search_items.ts} | 56 +- .../transform/public/app/lib/kibana/index.ts | 17 - .../public/app/lib/kibana/kibana_context.tsx | 72 -- .../lib/kibana/use_current_index_pattern.ts | 19 - .../clone_transform_section.tsx | 22 +- .../source_index_preview.test.tsx.snap | 597 +++++++++++++++- .../expanded_row.test.tsx | 2 - .../source_index_preview.test.tsx | 20 +- .../source_index_preview.tsx | 638 +++++++++--------- .../step_create_form.test.tsx.snap | 597 +++++++++++++++- .../step_create/step_create_form.test.tsx | 15 +- .../step_create/step_create_form.tsx | 10 +- .../__snapshots__/pivot_preview.test.tsx.snap | 637 +++++++++++++++-- .../step_define_form.test.tsx.snap | 580 +++++++++++++++- .../step_define_summary.test.tsx.snap | 121 +++- .../step_define/pivot_preview.test.tsx | 21 +- .../components/step_define/pivot_preview.tsx | 304 ++++----- .../step_define/step_define_form.test.tsx | 18 +- .../step_define/step_define_form.tsx | 61 +- .../step_define/step_define_summary.test.tsx | 14 +- .../step_define/step_define_summary.tsx | 53 +- .../step_details/step_details_form.tsx | 621 ++++++++--------- .../components/wizard/wizard.tsx | 31 +- .../create_transform_section.tsx | 94 ++- .../legacy/plugins/transform/public/plugin.ts | 12 +- .../transform/public/shared_imports.ts | 1 + .../legacy/plugins/transform/public/shim.ts | 17 +- 31 files changed, 3438 insertions(+), 1251 deletions(-) rename x-pack/legacy/plugins/transform/public/app/{lib/kibana => hooks/use_search_items}/common.ts (96%) create mode 100644 x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/index.ts rename x-pack/legacy/plugins/transform/public/app/{lib/kibana/kibana_provider.tsx => hooks/use_search_items/use_search_items.ts} (53%) delete mode 100644 x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts delete mode 100644 x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx delete mode 100644 x-pack/legacy/plugins/transform/public/app/lib/kibana/use_current_index_pattern.ts diff --git a/x-pack/legacy/plugins/transform/public/app/common/request.ts b/x-pack/legacy/plugins/transform/public/app/common/request.ts index 3b740de177ef8..31089b86a2c2d 100644 --- a/x-pack/legacy/plugins/transform/public/app/common/request.ts +++ b/x-pack/legacy/plugins/transform/public/app/common/request.ts @@ -7,7 +7,7 @@ import { DefaultOperator } from 'elasticsearch'; import { dictionaryToArray } from '../../../common/types/common'; -import { SavedSearchQuery } from '../lib/kibana'; +import { SavedSearchQuery } from '../hooks/use_search_items'; import { StepDefineExposedState } from '../sections/create_transform/components/step_define/step_define_form'; import { StepDetailsExposedState } from '../sections/create_transform/components/step_details/step_details_form'; diff --git a/x-pack/legacy/plugins/transform/public/app/components/toast_notification_text.test.tsx b/x-pack/legacy/plugins/transform/public/app/components/toast_notification_text.test.tsx index 81af5c974fe04..095b57de97d9a 100644 --- a/x-pack/legacy/plugins/transform/public/app/components/toast_notification_text.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/components/toast_notification_text.test.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { render } from '@testing-library/react'; -import { KibanaContext } from '../lib/kibana'; import { createPublicShim } from '../../shim'; import { getAppProviders } from '../app_dependencies'; import { ToastNotificationText } from './toast_notification_text'; jest.mock('../../shared_imports'); +jest.mock('ui/new_platform'); describe('ToastNotificationText', () => { test('should render the text as plain text', () => { @@ -23,9 +23,7 @@ describe('ToastNotificationText', () => { }; const { container } = render( - - - + ); expect(container.textContent).toBe('a short text message'); @@ -39,9 +37,7 @@ describe('ToastNotificationText', () => { }; const { container } = render( - - - + ); expect(container.textContent).toBe( diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/common.ts similarity index 96% rename from x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts rename to x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/common.ts index aa4cd21281e22..2258f8f33f01d 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/common.ts +++ b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/common.ts @@ -14,6 +14,8 @@ import { import { matchAllQuery } from '../../common'; +export type SavedSearchQuery = object; + type IndexPatternId = string; type SavedSearchId = string; @@ -60,7 +62,7 @@ export function getIndexPatternIdByTitle(indexPatternTitle: string): string | un return indexPatternCache.find(d => d?.attributes?.title === indexPatternTitle)?.id; } -type CombinedQuery = Record<'bool', any> | unknown; +type CombinedQuery = Record<'bool', any> | object; export function loadCurrentIndexPattern( indexPatterns: IndexPatternsContract, @@ -79,17 +81,20 @@ export function loadCurrentSavedSearch(savedSearches: any, savedSearchId: SavedS function isIndexPattern(arg: any): arg is IndexPattern { return arg !== undefined; } + +export interface SearchItems { + indexPattern: IndexPattern; + savedSearch: any; + query: any; + combinedQuery: CombinedQuery; +} + // Helper for creating the items used for searching and job creation. export function createSearchItems( indexPattern: IndexPattern | undefined, savedSearch: any, config: IUiSettingsClient -): { - indexPattern: IndexPattern; - savedSearch: any; - query: any; - combinedQuery: CombinedQuery; -} { +): SearchItems { // query is only used by the data visualizer as it needs // a lucene query_string. // Using a blank query will cause match_all:{} to be used diff --git a/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/index.ts b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/index.ts new file mode 100644 index 0000000000000..aa4f04f43b335 --- /dev/null +++ b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { SavedSearchQuery, SearchItems } from './common'; +export { useSearchItems } from './use_search_items'; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts similarity index 53% rename from x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx rename to x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts index f2574a4a85f29..12fc75c20ffa4 100644 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_provider.tsx +++ b/x-pack/legacy/plugins/transform/public/app/hooks/use_search_items/use_search_items.ts @@ -4,30 +4,36 @@ * you may not use this file except in compliance with the Elastic License. */ -import React, { useEffect, useState, FC } from 'react'; +import { useEffect, useState } from 'react'; + +import { createSavedSearchesLoader } from '../../../shared_imports'; import { useAppDependencies } from '../../app_dependencies'; import { createSearchItems, + getIndexPatternIdByTitle, loadCurrentIndexPattern, loadIndexPatterns, loadCurrentSavedSearch, + SearchItems, } from './common'; -import { InitializedKibanaContextValue, KibanaContext, KibanaContextValue } from './kibana_context'; - -interface Props { - savedObjectId: string; -} +export const useSearchItems = (defaultSavedObjectId: string | undefined) => { + const [savedObjectId, setSavedObjectId] = useState(defaultSavedObjectId); -export const KibanaProvider: FC = ({ savedObjectId, children }) => { const appDeps = useAppDependencies(); const indexPatterns = appDeps.plugins.data.indexPatterns; + const uiSettings = appDeps.core.uiSettings; const savedObjectsClient = appDeps.core.savedObjects.client; - const savedSearches = appDeps.plugins.savedSearches.getClient(); + const savedSearches = createSavedSearchesLoader({ + savedObjectsClient, + indexPatterns, + chrome: appDeps.core.chrome, + overlays: appDeps.core.overlays, + }); - const [contextValue, setContextValue] = useState({ initialized: false }); + const [searchItems, setSearchItems] = useState(undefined); async function fetchSavedObject(id: string) { await loadIndexPatterns(savedObjectsClient, indexPatterns); @@ -47,31 +53,21 @@ export const KibanaProvider: FC = ({ savedObjectId, children }) => { // Just let fetchedSavedSearch stay undefined in case it doesn't exist. } - const kibanaConfig = appDeps.core.uiSettings; - - const { - indexPattern: currentIndexPattern, - savedSearch: currentSavedSearch, - combinedQuery, - } = createSearchItems(fetchedIndexPattern, fetchedSavedSearch, kibanaConfig); - - const kibanaContext: InitializedKibanaContextValue = { - indexPatterns, - initialized: true, - kibanaConfig, - combinedQuery, - currentIndexPattern, - currentSavedSearch, - }; - - setContextValue(kibanaContext); + setSearchItems(createSearchItems(fetchedIndexPattern, fetchedSavedSearch, uiSettings)); } useEffect(() => { - fetchSavedObject(savedObjectId); - // fetchSavedObject should not be tracked. + if (savedObjectId !== undefined) { + fetchSavedObject(savedObjectId); + } + // Run this only when savedObjectId changes. // eslint-disable-next-line react-hooks/exhaustive-deps }, [savedObjectId]); - return {children}; + return { + getIndexPatternIdByTitle, + loadIndexPatterns, + searchItems, + setSavedObjectId, + }; }; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts deleted file mode 100644 index 62107cb37ff2c..0000000000000 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -export { getIndexPatternIdByTitle, loadIndexPatterns } from './common'; -export { - useKibanaContext, - InitializedKibanaContextValue, - KibanaContext, - KibanaContextValue, - SavedSearchQuery, - RenderOnlyWithInitializedKibanaContext, -} from './kibana_context'; -export { KibanaProvider } from './kibana_provider'; -export { useCurrentIndexPattern } from './use_current_index_pattern'; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx b/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx deleted file mode 100644 index 3acec1ea0e809..0000000000000 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/kibana_context.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import React, { createContext, useContext, FC } from 'react'; - -import { IUiSettingsClient } from 'kibana/public'; - -import { SavedSearch } from '../../../../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/types'; -import { - IndexPattern, - IndexPatternsContract, -} from '../../../../../../../../src/plugins/data/public'; - -interface UninitializedKibanaContextValue { - initialized: false; -} - -export interface InitializedKibanaContextValue { - combinedQuery: any; - indexPatterns: IndexPatternsContract; - initialized: true; - kibanaConfig: IUiSettingsClient; - currentIndexPattern: IndexPattern; - currentSavedSearch?: SavedSearch; -} - -export type KibanaContextValue = UninitializedKibanaContextValue | InitializedKibanaContextValue; - -export function isKibanaContextInitialized(arg: any): arg is InitializedKibanaContextValue { - return arg.initialized; -} - -export type SavedSearchQuery = object; - -export const KibanaContext = createContext({ initialized: false }); - -/** - * Custom hook to get the current kibanaContext. - * - * @remarks - * This hook should only be used in components wrapped in `RenderOnlyWithInitializedKibanaContext`, - * otherwise it will throw an error when KibanaContext hasn't been initialized yet. - * In return you get the benefit of not having to check if it's been initialized in the component - * where it's used. - * - * @returns `kibanaContext` - */ -export const useKibanaContext = () => { - const kibanaContext = useContext(KibanaContext); - - if (!isKibanaContextInitialized(kibanaContext)) { - throw new Error('useKibanaContext: kibanaContext not initialized'); - } - - return kibanaContext; -}; - -/** - * Wrapper component to render children only if `kibanaContext` has been initialized. - * In combination with `useKibanaContext` this avoids having to check for the initialization - * in consuming components. - * - * @returns `children` or `null` depending on whether `kibanaContext` is initialized or not. - */ -export const RenderOnlyWithInitializedKibanaContext: FC = ({ children }) => { - const kibanaContext = useContext(KibanaContext); - - return isKibanaContextInitialized(kibanaContext) ? <>{children} : null; -}; diff --git a/x-pack/legacy/plugins/transform/public/app/lib/kibana/use_current_index_pattern.ts b/x-pack/legacy/plugins/transform/public/app/lib/kibana/use_current_index_pattern.ts deleted file mode 100644 index 12c5bde171b8b..0000000000000 --- a/x-pack/legacy/plugins/transform/public/app/lib/kibana/use_current_index_pattern.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { useContext } from 'react'; - -import { isKibanaContextInitialized, KibanaContext } from './kibana_context'; - -export const useCurrentIndexPattern = () => { - const context = useContext(KibanaContext); - - if (!isKibanaContextInitialized(context)) { - throw new Error('useCurrentIndexPattern: kibanaContext not initialized'); - } - - return context.currentIndexPattern; -}; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx index 8f58bc94e7c12..ccbcdcf5fb7fe 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/clone_transform/clone_transform_section.tsx @@ -23,6 +23,7 @@ import { } from '@elastic/eui'; import { useApi } from '../../hooks/use_api'; +import { useSearchItems } from '../../hooks/use_search_items'; import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; @@ -30,12 +31,6 @@ import { useAppDependencies, useDocumentationLinks } from '../../app_dependencie import { TransformPivotConfig } from '../../common'; import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation'; import { PrivilegesWrapper } from '../../lib/authorization'; -import { - getIndexPatternIdByTitle, - loadIndexPatterns, - KibanaProvider, - RenderOnlyWithInitializedKibanaContext, -} from '../../lib/kibana'; import { Wizard } from '../create_transform/components/wizard'; @@ -81,7 +76,12 @@ export const CloneTransformSection: FC = ({ match }) => { const [transformConfig, setTransformConfig] = useState(); const [errorMessage, setErrorMessage] = useState(); const [isInitialized, setIsInitialized] = useState(false); - const [savedObjectId, setSavedObjectId] = useState(undefined); + const { + getIndexPatternIdByTitle, + loadIndexPatterns, + searchItems, + setSavedObjectId, + } = useSearchItems(undefined); const fetchTransformConfig = async () => { try { @@ -182,12 +182,8 @@ export const CloneTransformSection: FC = ({ match }) => {
{JSON.stringify(errorMessage)}
)} - {savedObjectId !== undefined && isInitialized === true && transformConfig !== undefined && ( - - - - - + {searchItems !== undefined && isInitialized === true && transformConfig !== undefined && ( + )} diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap index e43f2e37bb416..5f73e91aa9aca 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap @@ -1,24 +1,583 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Transform: Minimal initialization 1`] = ` -
- - + + + - -
+ > + + + + + `; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/expanded_row.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/expanded_row.test.tsx index bfde8f171874e..ddd1a1482fd35 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/expanded_row.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/expanded_row.test.tsx @@ -39,8 +39,6 @@ describe('Transform: ', () => { }, }; - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx index 16949425284fd..ec79735741427 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.test.tsx @@ -7,8 +7,10 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { KibanaContext } from '../../../../lib/kibana'; +import { createPublicShim } from '../../../../../shim'; +import { getAppProviders } from '../../../../app_dependencies'; import { getPivotQuery } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { SourceIndexPreview } from './source_index_preview'; @@ -18,22 +20,24 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('ui/new_platform'); jest.mock('../../../../../shared_imports'); describe('Transform: ', () => { test('Minimal initialization', () => { const props = { + indexPattern: { + title: 'the-index-pattern-title', + fields: [] as any[], + } as SearchItems['indexPattern'], query: getPivotQuery('the-query'), }; - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. + const Providers = getAppProviders(createPublicShim()); const wrapper = shallow( -
- - - -
+ + + ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx index 2b7d36cada3c6..3268d6697ed44 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/source_index_preview.tsx @@ -40,8 +40,6 @@ import { KBN_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/ import { Dictionary } from '../../../../../../common/types/common'; import { formatHumanReadableDateTimeSeconds } from '../../../../../../common/utils/date_utils'; -import { useCurrentIndexPattern } from '../../../../lib/kibana'; - import { toggleSelectedField, EsDoc, @@ -49,6 +47,7 @@ import { MAX_COLUMNS, PivotQuery, } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { getSourceIndexDevConsoleStatement } from './common'; import { ExpandedRow } from './expanded_row'; @@ -73,351 +72,362 @@ const SourceIndexPreviewTitle: React.FC = ({ indexPatte ); interface Props { + indexPattern: SearchItems['indexPattern']; query: PivotQuery; cellClick?(search: string): void; } -export const SourceIndexPreview: React.FC = React.memo(({ cellClick, query }) => { - const [clearTable, setClearTable] = useState(false); - - const indexPattern = useCurrentIndexPattern(); - - const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); - const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); - - // EuiInMemoryTable has an issue with dynamic sortable columns - // and will trigger a full page Kibana error in such a case. - // The following is a workaround until this is solved upstream: - // - If the sortable/columns config changes, - // the table will be unmounted/not rendered. - // This is what setClearTable(true) in toggleColumn() does. - // - After that on next render it gets re-enabled. To make sure React - // doesn't consolidate the state updates, setTimeout is used. - if (clearTable) { - setTimeout(() => setClearTable(false), 0); - } +export const SourceIndexPreview: React.FC = React.memo( + ({ indexPattern, cellClick, query }) => { + const [clearTable, setClearTable] = useState(false); + + const [selectedFields, setSelectedFields] = useState([] as EsFieldName[]); + const [isColumnsPopoverVisible, setColumnsPopoverVisible] = useState(false); + + // EuiInMemoryTable has an issue with dynamic sortable columns + // and will trigger a full page Kibana error in such a case. + // The following is a workaround until this is solved upstream: + // - If the sortable/columns config changes, + // the table will be unmounted/not rendered. + // This is what setClearTable(true) in toggleColumn() does. + // - After that on next render it gets re-enabled. To make sure React + // doesn't consolidate the state updates, setTimeout is used. + if (clearTable) { + setTimeout(() => setClearTable(false), 0); + } - function toggleColumnsPopover() { - setColumnsPopoverVisible(!isColumnsPopoverVisible); - } + function toggleColumnsPopover() { + setColumnsPopoverVisible(!isColumnsPopoverVisible); + } - function closeColumnsPopover() { - setColumnsPopoverVisible(false); - } + function closeColumnsPopover() { + setColumnsPopoverVisible(false); + } - function toggleColumn(column: EsFieldName) { - // spread to a new array otherwise the component wouldn't re-render - setClearTable(true); - setSelectedFields([...toggleSelectedField(selectedFields, column)]); - } + function toggleColumn(column: EsFieldName) { + // spread to a new array otherwise the component wouldn't re-render + setClearTable(true); + setSelectedFields([...toggleSelectedField(selectedFields, column)]); + } - const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState( - {} as ItemIdToExpandedRowMap - ); + const [itemIdToExpandedRowMap, setItemIdToExpandedRowMap] = useState( + {} as ItemIdToExpandedRowMap + ); - function toggleDetails(item: EsDoc) { - if (itemIdToExpandedRowMap[item._id]) { - delete itemIdToExpandedRowMap[item._id]; - } else { - itemIdToExpandedRowMap[item._id] = ; + function toggleDetails(item: EsDoc) { + if (itemIdToExpandedRowMap[item._id]) { + delete itemIdToExpandedRowMap[item._id]; + } else { + itemIdToExpandedRowMap[item._id] = ; + } + // spread to a new object otherwise the component wouldn't re-render + setItemIdToExpandedRowMap({ ...itemIdToExpandedRowMap }); } - // spread to a new object otherwise the component wouldn't re-render - setItemIdToExpandedRowMap({ ...itemIdToExpandedRowMap }); - } - - const { errorMessage, status, tableItems } = useSourceIndexData( - indexPattern, - query, - selectedFields, - setSelectedFields - ); - if (status === SOURCE_INDEX_STATUS.ERROR) { - return ( - - - - - {errorMessage} - - - + const { errorMessage, status, tableItems } = useSourceIndexData( + indexPattern, + query, + selectedFields, + setSelectedFields ); - } - if (status === SOURCE_INDEX_STATUS.LOADED && tableItems.length === 0) { - return ( - - - -

- {i18n.translate('xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutBody', { - defaultMessage: - 'The query for the source index returned no results. Please make sure you have sufficient permissions, the index contains documents and your query is not too restrictive.', + if (status === SOURCE_INDEX_STATUS.ERROR) { + return ( + + + - - - ); - } - - let docFields: EsFieldName[] = []; - let docFieldsCount = 0; - if (tableItems.length > 0) { - docFields = Object.keys(tableItems[0]._source); - docFields.sort(); - docFieldsCount = docFields.length; - } + color="danger" + iconType="cross" + > + + {errorMessage} + + + + ); + } - const columns: Array> = selectedFields.map(k => { - const column: ColumnType = { - field: `_source["${k}"]`, - name: k, - sortable: true, - truncateText: true, - }; - - const field = indexPattern.fields.find(f => f.name === k); - - const formatField = (d: string) => { - return field !== undefined && field.type === KBN_FIELD_TYPES.DATE - ? formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000) - : d; - }; - - const render = (d: any) => { - if (Array.isArray(d) && d.every(item => typeof item === 'string')) { - // If the cells data is an array of strings, return as a comma separated list. - // The list will get limited to 5 items with `…` at the end if there's more in the original array. - return `${d - .map(item => formatField(item)) - .slice(0, 5) - .join(', ')}${d.length > 5 ? ', …' : ''}`; - } else if (Array.isArray(d)) { - // If the cells data is an array of e.g. objects, display a 'array' badge with a - // tooltip that explains that this type of field is not supported in this table. - return ( - + + - - {i18n.translate('xpack.transform.sourceIndexPreview.SourceIndexArrayBadgeContent', { - defaultMessage: 'array', - })} - - - ); - } else if (typeof d === 'object' && d !== null) { - // If the cells data is an object, display a 'object' badge with a - // tooltip that explains that this type of field is not supported in this table. - return ( - + {i18n.translate('xpack.transform.sourceIndexPreview.SourceIndexNoDataCalloutBody', { defaultMessage: - 'The full content of this object based column is available in the expanded row.', - } - )} - > - - {i18n.translate('xpack.transform.sourceIndexPreview.SourceIndexObjectBadgeContent', { - defaultMessage: 'object', + 'The query for the source index returned no results. Please make sure you have sufficient permissions, the index contains documents and your query is not too restrictive.', })} - - - ); - } - - return formatField(d); - }; - - if (typeof field !== 'undefined') { - switch (field.type) { - case KBN_FIELD_TYPES.BOOLEAN: - column.dataType = 'boolean'; - break; - case KBN_FIELD_TYPES.DATE: - column.align = 'right'; - column.render = (d: any) => formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); - break; - case KBN_FIELD_TYPES.NUMBER: - column.dataType = 'number'; - break; - default: - column.render = render; - break; - } - } else { - column.render = render; +

+
+
+ ); } - if (CELL_CLICK_ENABLED && cellClick) { - column.render = (d: string) => ( - cellClick(`${k}:(${d})`)}> - {render(d)} - - ); + let docFields: EsFieldName[] = []; + let docFieldsCount = 0; + if (tableItems.length > 0) { + docFields = Object.keys(tableItems[0]._source); + docFields.sort(); + docFieldsCount = docFields.length; } - return column; - }); + const columns: Array> = selectedFields.map(k => { + const column: ColumnType = { + field: `_source["${k}"]`, + name: k, + sortable: true, + truncateText: true, + }; + + const field = indexPattern.fields.find(f => f.name === k); + + const formatField = (d: string) => { + return field !== undefined && field.type === KBN_FIELD_TYPES.DATE + ? formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000) + : d; + }; + + const render = (d: any) => { + if (Array.isArray(d) && d.every(item => typeof item === 'string')) { + // If the cells data is an array of strings, return as a comma separated list. + // The list will get limited to 5 items with `…` at the end if there's more in the original array. + return `${d + .map(item => formatField(item)) + .slice(0, 5) + .join(', ')}${d.length > 5 ? ', …' : ''}`; + } else if (Array.isArray(d)) { + // If the cells data is an array of e.g. objects, display a 'array' badge with a + // tooltip that explains that this type of field is not supported in this table. + return ( + + + {i18n.translate('xpack.transform.sourceIndexPreview.SourceIndexArrayBadgeContent', { + defaultMessage: 'array', + })} + + + ); + } else if (typeof d === 'object' && d !== null) { + // If the cells data is an object, display a 'object' badge with a + // tooltip that explains that this type of field is not supported in this table. + return ( + + + {i18n.translate( + 'xpack.transform.sourceIndexPreview.SourceIndexObjectBadgeContent', + { + defaultMessage: 'object', + } + )} + + + ); + } - let sorting: SortingPropType = false; + return formatField(d); + }; + + if (typeof field !== 'undefined') { + switch (field.type) { + case KBN_FIELD_TYPES.BOOLEAN: + column.dataType = 'boolean'; + break; + case KBN_FIELD_TYPES.DATE: + column.align = 'right'; + column.render = (d: any) => formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); + break; + case KBN_FIELD_TYPES.NUMBER: + column.dataType = 'number'; + break; + default: + column.render = render; + break; + } + } else { + column.render = render; + } - if (columns.length > 0) { - sorting = { - sort: { - field: `_source["${selectedFields[0]}"]`, - direction: SORT_DIRECTION.ASC, - }, - }; - } + if (CELL_CLICK_ENABLED && cellClick) { + column.render = (d: string) => ( + cellClick(`${k}:(${d})`)}> + {render(d)} + + ); + } - columns.unshift({ - align: RIGHT_ALIGNMENT, - width: '40px', - isExpander: true, - render: (item: EsDoc) => ( - toggleDetails(item)} - aria-label={ - itemIdToExpandedRowMap[item._id] - ? i18n.translate('xpack.transform.sourceIndexPreview.rowCollapse', { - defaultMessage: 'Collapse', - }) - : i18n.translate('xpack.transform.sourceIndexPreview.rowExpand', { - defaultMessage: 'Expand', - }) - } - iconType={itemIdToExpandedRowMap[item._id] ? 'arrowUp' : 'arrowDown'} - /> - ), - }); + return column; + }); - const euiCopyText = i18n.translate('xpack.transform.sourceIndexPreview.copyClipboardTooltip', { - defaultMessage: 'Copy Dev Console statement of the source index preview to the clipboard.', - }); + let sorting: SortingPropType = false; - const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); + if (columns.length > 0) { + sorting = { + sort: { + field: `_source["${selectedFields[0]}"]`, + direction: SORT_DIRECTION.ASC, + }, + }; + } - return ( - - - - - - - - - {docFieldsCount > MAX_COLUMNS && ( + columns.unshift({ + align: RIGHT_ALIGNMENT, + width: '40px', + isExpander: true, + render: (item: EsDoc) => ( + toggleDetails(item)} + aria-label={ + itemIdToExpandedRowMap[item._id] + ? i18n.translate('xpack.transform.sourceIndexPreview.rowCollapse', { + defaultMessage: 'Collapse', + }) + : i18n.translate('xpack.transform.sourceIndexPreview.rowExpand', { + defaultMessage: 'Expand', + }) + } + iconType={itemIdToExpandedRowMap[item._id] ? 'arrowUp' : 'arrowDown'} + /> + ), + }); + + const euiCopyText = i18n.translate('xpack.transform.sourceIndexPreview.copyClipboardTooltip', { + defaultMessage: 'Copy Dev Console statement of the source index preview to the clipboard.', + }); + + const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); + + return ( + + + + + + + + + {docFieldsCount > MAX_COLUMNS && ( + + {i18n.translate('xpack.transform.sourceIndexPreview.fieldSelection', { + defaultMessage: + '{selectedFieldsLength, number} of {docFieldsCount, number} {docFieldsCount, plural, one {field} other {fields}} selected', + values: { selectedFieldsLength: selectedFields.length, docFieldsCount }, + })} + + )} + + - {i18n.translate('xpack.transform.sourceIndexPreview.fieldSelection', { - defaultMessage: - '{selectedFieldsLength, number} of {docFieldsCount, number} {docFieldsCount, plural, one {field} other {fields}} selected', - values: { selectedFieldsLength: selectedFields.length, docFieldsCount }, - })} - - )} - - - - + } + isOpen={isColumnsPopoverVisible} + closePopover={closeColumnsPopover} + ownFocus + > + + {i18n.translate( + 'xpack.transform.sourceIndexPreview.selectFieldsPopoverTitle', { - defaultMessage: 'Select columns', + defaultMessage: 'Select fields', } )} - /> - } - isOpen={isColumnsPopoverVisible} - closePopover={closeColumnsPopover} - ownFocus + +
+ {docFields.map(d => ( + toggleColumn(d)} + disabled={selectedFields.includes(d) && selectedFields.length === 1} + /> + ))} +
+
+
+
+ + - - {i18n.translate('xpack.transform.sourceIndexPreview.selectFieldsPopoverTitle', { - defaultMessage: 'Select fields', - })} - -
- {docFields.map(d => ( - toggleColumn(d)} - disabled={selectedFields.includes(d) && selectedFields.length === 1} - /> - ))} -
- - -
- - - {(copy: () => void) => ( - - )} - - -
-
-
- {status === SOURCE_INDEX_STATUS.LOADING && } - {status !== SOURCE_INDEX_STATUS.LOADING && ( - - )} - {clearTable === false && columns.length > 0 && sorting !== false && ( - ({ - 'data-test-subj': `transformSourceIndexPreviewRow row-${item._id}`, - })} - sorting={sorting} - /> - )} -
- ); -}); + {(copy: () => void) => ( + + )} + +
+
+
+
+ {status === SOURCE_INDEX_STATUS.LOADING && } + {status !== SOURCE_INDEX_STATUS.LOADING && ( + + )} + {clearTable === false && columns.length > 0 && sorting !== false && ( + ({ + 'data-test-subj': `transformSourceIndexPreviewRow row-${item._id}`, + })} + sorting={sorting} + /> + )} +
+ ); + } +); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap index e034badea9b11..d7d53bd70d47d 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap @@ -1,27 +1,580 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Transform: Minimal initialization 1`] = ` -
- - + + + - -
+ > + + + + + `; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx index 625c545ee8c46..80968fd6e2887 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.test.tsx @@ -7,7 +7,8 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { KibanaContext } from '../../../../lib/kibana'; +import { createPublicShim } from '../../../../../shim'; +import { getAppProviders } from '../../../../app_dependencies'; import { StepCreateForm } from './step_create_form'; @@ -17,6 +18,7 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('ui/new_platform'); jest.mock('../../../../../shared_imports'); describe('Transform: ', () => { @@ -29,14 +31,11 @@ describe('Transform: ', () => { onChange() {}, }; - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. + const Providers = getAppProviders(createPublicShim()); const wrapper = shallow( -
- - - -
+ + + ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx index 312d8a30dab77..b61045e9b5c77 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/step_create_form.tsx @@ -34,7 +34,6 @@ import { PROGRESS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants import { getTransformProgress, getDiscoverUrl } from '../../../../common'; import { useApi } from '../../../../hooks/use_api'; -import { useKibanaContext } from '../../../../lib/kibana'; import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies'; import { RedirectToTransformManagement } from '../../../../common/navigation'; import { ToastNotificationText } from '../../../../components'; @@ -75,7 +74,8 @@ export const StepCreateForm: FC = React.memo( ); const deps = useAppDependencies(); - const kibanaContext = useKibanaContext(); + const indexPatterns = deps.plugins.data.indexPatterns; + const uiSettings = deps.core.uiSettings; const toastNotifications = useToastNotifications(); useEffect(() => { @@ -160,7 +160,7 @@ export const StepCreateForm: FC = React.memo( const indexPatternName = transformConfig.dest.index; try { - const newIndexPattern = await kibanaContext.indexPatterns.make(); + const newIndexPattern = await indexPatterns.make(); Object.assign(newIndexPattern, { id: '', @@ -183,8 +183,8 @@ export const StepCreateForm: FC = React.memo( // check if there's a default index pattern, if not, // set the newly created one as the default index pattern. - if (!kibanaContext.kibanaConfig.get('defaultIndex')) { - await kibanaContext.kibanaConfig.set('defaultIndex', id); + if (!uiSettings.get('defaultIndex')) { + await uiSettings.set('defaultIndex', id); } toastNotifications.addSuccess( diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap index a7da172a67b8a..c3f446d7a04d1 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap @@ -1,44 +1,603 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Transform: Minimal initialization 1`] = ` -
- - + + + - -
+ > + + + + + `; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap index 70a0bfc12b208..b524921569176 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap @@ -1,17 +1,571 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Transform: Minimal initialization 1`] = ` -
- - - -
+ + + + + + + + + `; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_summary.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_summary.test.tsx.snap index b18233e5c53e3..4955a0a95b7e9 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_summary.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_summary.test.tsx.snap @@ -1,42 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Transform: Minimal initialization 1`] = ` -
- + - + + + + + + + + +
+ + + + - -
+ query={ + Object { + "query_string": Object { + "default_operator": "AND", + "query": "the-search-query", + }, + } + } + /> + + + `; diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx index 2ac4295da1eed..6b49a305e515b 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.test.tsx @@ -7,8 +7,8 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { KibanaContext } from '../../../../lib/kibana'; - +import { createPublicShim } from '../../../../../shim'; +import { getAppProviders } from '../../../../app_dependencies'; import { getPivotQuery, PivotAggsConfig, @@ -16,6 +16,7 @@ import { PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { PivotPreview } from './pivot_preview'; @@ -25,6 +26,7 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('ui/new_platform'); jest.mock('../../../../../shared_imports'); describe('Transform: ', () => { @@ -44,17 +46,18 @@ describe('Transform: ', () => { const props = { aggs: { 'the-agg-name': agg }, groupBy: { 'the-group-by-name': groupBy }, + indexPattern: { + title: 'the-index-pattern-title', + fields: [] as any[], + } as SearchItems['indexPattern'], query: getPivotQuery('the-query'), }; - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. + const Providers = getAppProviders(createPublicShim()); const wrapper = shallow( -
- - - -
+ + + ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx index 241f65614cea2..1932e7dbe219f 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/pivot_preview.tsx @@ -30,8 +30,6 @@ import { dictionaryToArray } from '../../../../../../common/types/common'; import { ES_FIELD_TYPES } from '../../../../../../../../../../src/plugins/data/public'; import { formatHumanReadableDateTimeSeconds } from '../../../../../../common/utils/date_utils'; -import { useCurrentIndexPattern } from '../../../../lib/kibana'; - import { getFlattenedFields, PreviewRequestBody, @@ -40,6 +38,7 @@ import { PivotGroupByConfigDict, PivotQuery, } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { getPivotPreviewDevConsoleStatement } from './common'; import { PreviewItem, PIVOT_PREVIEW_STATUS, usePivotPreviewData } from './use_pivot_preview_data'; @@ -115,176 +114,177 @@ const ErrorMessage: FC = ({ message }) => ( interface PivotPreviewProps { aggs: PivotAggsConfigDict; groupBy: PivotGroupByConfigDict; + indexPattern: SearchItems['indexPattern']; query: PivotQuery; } -export const PivotPreview: FC = React.memo(({ aggs, groupBy, query }) => { - const [clearTable, setClearTable] = useState(false); +export const PivotPreview: FC = React.memo( + ({ aggs, groupBy, indexPattern, query }) => { + const [clearTable, setClearTable] = useState(false); - const indexPattern = useCurrentIndexPattern(); + const { + previewData, + previewMappings, + errorMessage, + previewRequest, + status, + } = usePivotPreviewData(indexPattern, query, aggs, groupBy); - const { - previewData, - previewMappings, - errorMessage, - previewRequest, - status, - } = usePivotPreviewData(indexPattern, query, aggs, groupBy); + const groupByArr = dictionaryToArray(groupBy); - const groupByArr = dictionaryToArray(groupBy); + // EuiInMemoryTable has an issue with dynamic sortable columns + // and will trigger a full page Kibana error in such a case. + // The following is a workaround until this is solved upstream: + // - If the sortable/columns config changes, + // the table will be unmounted/not rendered. + // This is what the useEffect() part does. + // - After that the table gets re-enabled. To make sure React + // doesn't consolidate the state updates, setTimeout is used. + const firstColumnName = + previewData.length > 0 + ? Object.keys(previewData[0]).sort(sortColumns(groupByArr))[0] + : undefined; - // EuiInMemoryTable has an issue with dynamic sortable columns - // and will trigger a full page Kibana error in such a case. - // The following is a workaround until this is solved upstream: - // - If the sortable/columns config changes, - // the table will be unmounted/not rendered. - // This is what the useEffect() part does. - // - After that the table gets re-enabled. To make sure React - // doesn't consolidate the state updates, setTimeout is used. - const firstColumnName = - previewData.length > 0 - ? Object.keys(previewData[0]).sort(sortColumns(groupByArr))[0] - : undefined; + const firstColumnNameChanged = usePrevious(firstColumnName) !== firstColumnName; + useEffect(() => { + if (firstColumnNameChanged) { + setClearTable(true); + } + if (clearTable) { + setTimeout(() => setClearTable(false), 0); + } + }, [firstColumnNameChanged, clearTable]); - const firstColumnNameChanged = usePrevious(firstColumnName) !== firstColumnName; - useEffect(() => { if (firstColumnNameChanged) { - setClearTable(true); - } - if (clearTable) { - setTimeout(() => setClearTable(false), 0); + return null; } - }, [firstColumnNameChanged, clearTable]); - - if (firstColumnNameChanged) { - return null; - } - if (status === PIVOT_PREVIEW_STATUS.ERROR) { - return ( - - - - - - - ); - } - - if (previewData.length === 0) { - let noDataMessage = i18n.translate( - 'xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', - { - defaultMessage: - 'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.', - } - ); + if (status === PIVOT_PREVIEW_STATUS.ERROR) { + return ( + + + + + + + ); + } - const aggsArr = dictionaryToArray(aggs); - if (aggsArr.length === 0 || groupByArr.length === 0) { - noDataMessage = i18n.translate( - 'xpack.transform.pivotPreview.PivotPreviewIncompleteConfigCalloutBody', + if (previewData.length === 0) { + let noDataMessage = i18n.translate( + 'xpack.transform.pivotPreview.PivotPreviewNoDataCalloutBody', { - defaultMessage: 'Please choose at least one group-by field and aggregation.', + defaultMessage: + 'The preview request did not return any data. Please ensure the optional query returns data and that values exist for the field used by group-by and aggregation fields.', } ); + + const aggsArr = dictionaryToArray(aggs); + if (aggsArr.length === 0 || groupByArr.length === 0) { + noDataMessage = i18n.translate( + 'xpack.transform.pivotPreview.PivotPreviewIncompleteConfigCalloutBody', + { + defaultMessage: 'Please choose at least one group-by field and aggregation.', + } + ); + } + return ( + + + +

{noDataMessage}

+
+
+ ); } - return ( - - - -

{noDataMessage}

-
-
- ); - } - const columnKeys = getFlattenedFields(previewData[0]); - columnKeys.sort(sortColumns(groupByArr)); + const columnKeys = getFlattenedFields(previewData[0]); + columnKeys.sort(sortColumns(groupByArr)); - const columns = columnKeys.map(k => { - const column: ColumnType = { - field: k, - name: k, - sortable: true, - truncateText: true, - }; - if (typeof previewMappings.properties[k] !== 'undefined') { - const esFieldType = previewMappings.properties[k].type; - switch (esFieldType) { - case ES_FIELD_TYPES.BOOLEAN: - column.dataType = 'boolean'; - break; - case ES_FIELD_TYPES.DATE: - column.align = 'right'; - column.render = (d: any) => formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); - break; - case ES_FIELD_TYPES.BYTE: - case ES_FIELD_TYPES.DOUBLE: - case ES_FIELD_TYPES.FLOAT: - case ES_FIELD_TYPES.HALF_FLOAT: - case ES_FIELD_TYPES.INTEGER: - case ES_FIELD_TYPES.LONG: - case ES_FIELD_TYPES.SCALED_FLOAT: - case ES_FIELD_TYPES.SHORT: - column.dataType = 'number'; - break; - case ES_FIELD_TYPES.KEYWORD: - case ES_FIELD_TYPES.TEXT: - column.dataType = 'string'; - break; + const columns = columnKeys.map(k => { + const column: ColumnType = { + field: k, + name: k, + sortable: true, + truncateText: true, + }; + if (typeof previewMappings.properties[k] !== 'undefined') { + const esFieldType = previewMappings.properties[k].type; + switch (esFieldType) { + case ES_FIELD_TYPES.BOOLEAN: + column.dataType = 'boolean'; + break; + case ES_FIELD_TYPES.DATE: + column.align = 'right'; + column.render = (d: any) => formatHumanReadableDateTimeSeconds(moment(d).unix() * 1000); + break; + case ES_FIELD_TYPES.BYTE: + case ES_FIELD_TYPES.DOUBLE: + case ES_FIELD_TYPES.FLOAT: + case ES_FIELD_TYPES.HALF_FLOAT: + case ES_FIELD_TYPES.INTEGER: + case ES_FIELD_TYPES.LONG: + case ES_FIELD_TYPES.SCALED_FLOAT: + case ES_FIELD_TYPES.SHORT: + column.dataType = 'number'; + break; + case ES_FIELD_TYPES.KEYWORD: + case ES_FIELD_TYPES.TEXT: + column.dataType = 'string'; + break; + } } - } - return column; - }); + return column; + }); - if (columns.length === 0) { - return null; - } + if (columns.length === 0) { + return null; + } - const sorting = { - sort: { - field: columns[0].field as string, - direction: SORT_DIRECTION.ASC, - }, - }; + const sorting = { + sort: { + field: columns[0].field as string, + direction: SORT_DIRECTION.ASC, + }, + }; - const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); + const MlInMemoryTableBasic = mlInMemoryTableBasicFactory(); - return ( - - - {status === PIVOT_PREVIEW_STATUS.LOADING && } - {status !== PIVOT_PREVIEW_STATUS.LOADING && ( - - )} - {previewData.length > 0 && clearTable === false && columns.length > 0 && ( - ({ - 'data-test-subj': 'transformPivotPreviewRow', - })} - sorting={sorting} - /> - )} - - ); -}); + return ( + + + {status === PIVOT_PREVIEW_STATUS.LOADING && } + {status !== PIVOT_PREVIEW_STATUS.LOADING && ( + + )} + {previewData.length > 0 && clearTable === false && columns.length > 0 && ( + ({ + 'data-test-subj': 'transformPivotPreviewRow', + })} + sorting={sorting} + /> + )} + + ); + } +); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx index 44edd1340e8d6..f31af733fa3ee 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.test.tsx @@ -7,14 +7,16 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { KibanaContext } from '../../../../lib/kibana'; - +import { createPublicShim } from '../../../../../shim'; +import { getAppProviders } from '../../../../app_dependencies'; import { PivotAggsConfigDict, PivotGroupByConfigDict, PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; + import { StepDefineForm, getAggNameConflictToastMessages } from './step_define_form'; // workaround to make React.memo() work with enzyme @@ -23,18 +25,16 @@ jest.mock('react', () => { return { ...r, memo: (x: any) => x }; }); +jest.mock('ui/new_platform'); jest.mock('../../../../../shared_imports'); describe('Transform: ', () => { test('Minimal initialization', () => { - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. + const Providers = getAppProviders(createPublicShim()); const wrapper = shallow( -
- - {}} /> - -
+ + {}} searchItems={{} as SearchItems} /> + ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx index bde832894632c..b5333e69001d7 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_form.tsx @@ -25,6 +25,7 @@ import { EuiSwitch, } from '@elastic/eui'; +import { SavedSearchQuery, SearchItems } from '../../../../hooks/use_search_items'; import { useXJsonMode, xJsonMode } from '../../../../hooks/use_x_json_mode'; import { useDocumentationLinks, useToastNotifications } from '../../../../app_dependencies'; import { TransformPivotConfig } from '../../../../common'; @@ -37,12 +38,6 @@ import { PivotPreview } from './pivot_preview'; import { KqlFilterBar } from '../../../../../shared_imports'; import { SwitchModal } from './switch_modal'; -import { - useKibanaContext, - InitializedKibanaContextValue, - SavedSearchQuery, -} from '../../../../lib/kibana'; - import { getPivotQuery, getPreviewRequestBody, @@ -77,18 +72,14 @@ export interface StepDefineExposedState { const defaultSearch = '*'; const emptySearch = ''; -export function getDefaultStepDefineState( - kibanaContext: InitializedKibanaContextValue -): StepDefineExposedState { +export function getDefaultStepDefineState(searchItems: SearchItems): StepDefineExposedState { return { aggList: {} as PivotAggsConfigDict, groupByList: {} as PivotGroupByConfigDict, isAdvancedPivotEditorEnabled: false, isAdvancedSourceEditorEnabled: false, - searchString: - kibanaContext.currentSavedSearch !== undefined ? kibanaContext.combinedQuery : defaultSearch, - searchQuery: - kibanaContext.currentSavedSearch !== undefined ? kibanaContext.combinedQuery : defaultSearch, + searchString: searchItems.savedSearch !== undefined ? searchItems.combinedQuery : defaultSearch, + searchQuery: searchItems.savedSearch !== undefined ? searchItems.combinedQuery : defaultSearch, sourceConfigUpdated: false, valid: false, }; @@ -241,14 +232,14 @@ export function getAggNameConflictToastMessages( interface Props { overrides?: StepDefineExposedState; onChange(s: StepDefineExposedState): void; + searchItems: SearchItems; } -export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange }) => { - const kibanaContext = useKibanaContext(); +export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange, searchItems }) => { const toastNotifications = useToastNotifications(); const { esQueryDsl, esTransformPivot } = useDocumentationLinks(); - const defaults = { ...getDefaultStepDefineState(kibanaContext), ...overrides }; + const defaults = { ...getDefaultStepDefineState(searchItems), ...overrides }; // The search filter const [searchString, setSearchString] = useState(defaults.searchString); @@ -271,7 +262,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange // The list of selected group by fields const [groupByList, setGroupByList] = useState(defaults.groupByList); - const indexPattern = kibanaContext.currentIndexPattern; + const { indexPattern } = searchItems; const { groupByOptions, @@ -572,7 +563,7 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange
- {kibanaContext.currentSavedSearch === undefined && typeof searchString === 'string' && ( + {searchItems.savedSearch === undefined && typeof searchString === 'string' && ( = React.memo(({ overrides = {}, onChange )} - {kibanaContext.currentSavedSearch === undefined && ( + {searchItems.savedSearch === undefined && ( @@ -724,16 +715,15 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange )} - {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id !== undefined && ( - - {kibanaContext.currentSavedSearch.title} - - )} + {searchItems.savedSearch !== undefined && searchItems.savedSearch.id !== undefined && ( + + {searchItems.savedSearch.title} + + )} {!isAdvancedPivotEditorEnabled && ( @@ -907,9 +897,18 @@ export const StepDefineForm: FC = React.memo(({ overrides = {}, onChange - + - + ); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx index 78f6fc30f9191..e3a9830ea1904 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.test.tsx @@ -7,14 +7,14 @@ import { shallow } from 'enzyme'; import React from 'react'; -import { KibanaContext } from '../../../../lib/kibana'; - import { PivotAggsConfig, PivotGroupByConfig, PIVOT_SUPPORTED_AGGS, PIVOT_SUPPORTED_GROUP_BY_AGGS, } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; + import { StepDefineExposedState } from './step_define_form'; import { StepDefineSummary } from './step_define_summary'; @@ -40,7 +40,7 @@ describe('Transform: ', () => { aggName: 'the-group-by-agg-name', dropDownName: 'the-group-by-drop-down-name', }; - const props: StepDefineExposedState = { + const formState: StepDefineExposedState = { aggList: { 'the-agg-name': agg }, groupByList: { 'the-group-by-name': groupBy }, isAdvancedPivotEditorEnabled: false, @@ -51,14 +51,8 @@ describe('Transform: ', () => { valid: true, }; - // Using a wrapping
element because shallow() would fail - // with the Provider being the outer most component. const wrapper = shallow( -
- - - -
+ ); expect(wrapper).toMatchSnapshot(); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx index 30c447f62c760..f8fb9db9bd686 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/step_define_summary.tsx @@ -17,26 +17,27 @@ import { EuiText, } from '@elastic/eui'; -import { useKibanaContext } from '../../../../lib/kibana'; +import { getPivotQuery } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { AggListSummary } from '../aggregation_list'; import { GroupByListSummary } from '../group_by_list'; -import { PivotPreview } from './pivot_preview'; -import { getPivotQuery } from '../../../../common'; +import { PivotPreview } from './pivot_preview'; import { StepDefineExposedState } from './step_define_form'; const defaultSearch = '*'; const emptySearch = ''; -export const StepDefineSummary: FC = ({ - searchString, - searchQuery, - groupByList, - aggList, -}) => { - const kibanaContext = useKibanaContext(); +interface Props { + formState: StepDefineExposedState; + searchItems: SearchItems; +} +export const StepDefineSummary: FC = ({ + formState: { searchString, searchQuery, groupByList, aggList }, + searchItems, +}) => { const pivotQuery = getPivotQuery(searchQuery); let useCodeBlock = false; let displaySearch; @@ -55,8 +56,8 @@ export const StepDefineSummary: FC = ({
- {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id === undefined && + {searchItems.savedSearch !== undefined && + searchItems.savedSearch.id === undefined && typeof searchString === 'string' && ( = ({ defaultMessage: 'Index pattern', })} > - {kibanaContext.currentIndexPattern.title} + {searchItems.indexPattern.title} {useCodeBlock === false && displaySearch !== emptySearch && ( = ({ )} - {kibanaContext.currentSavedSearch !== undefined && - kibanaContext.currentSavedSearch.id !== undefined && ( - - {kibanaContext.currentSavedSearch.title} - - )} + {searchItems.savedSearch !== undefined && searchItems.savedSearch.id !== undefined && ( + + {searchItems.savedSearch.title} + + )} = ({ - + diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx index 5ae2180bfe779..ea9483af49302 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_details/step_details_form.tsx @@ -11,11 +11,15 @@ import { i18n } from '@kbn/i18n'; import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from '@elastic/eui'; import { toMountPoint } from '../../../../../../../../../../src/plugins/kibana_react/public'; -import { useKibanaContext } from '../../../../lib/kibana'; import { isValidIndexName } from '../../../../../../common/utils/es_utils'; -import { useDocumentationLinks, useToastNotifications } from '../../../../app_dependencies'; +import { + useAppDependencies, + useDocumentationLinks, + useToastNotifications, +} from '../../../../app_dependencies'; import { ToastNotificationText } from '../../../../components'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { useApi } from '../../../../hooks/use_api'; import { isTransformIdValid, TransformId, TransformPivotConfig } from '../../../../common'; @@ -67,109 +71,129 @@ export function applyTransformConfigToDetailsState( interface Props { overrides?: StepDetailsExposedState; onChange(s: StepDetailsExposedState): void; + searchItems: SearchItems; } -export const StepDetailsForm: FC = React.memo(({ overrides = {}, onChange }) => { - const kibanaContext = useKibanaContext(); - const toastNotifications = useToastNotifications(); - const { esIndicesCreateIndex } = useDocumentationLinks(); +export const StepDetailsForm: FC = React.memo( + ({ overrides = {}, onChange, searchItems }) => { + const deps = useAppDependencies(); + const toastNotifications = useToastNotifications(); + const { esIndicesCreateIndex } = useDocumentationLinks(); - const defaults = { ...getDefaultStepDetailsState(), ...overrides }; + const defaults = { ...getDefaultStepDetailsState(), ...overrides }; - const [transformId, setTransformId] = useState(defaults.transformId); - const [transformDescription, setTransformDescription] = useState( - defaults.transformDescription - ); - const [destinationIndex, setDestinationIndex] = useState(defaults.destinationIndex); - const [transformIds, setTransformIds] = useState([]); - const [indexNames, setIndexNames] = useState([]); - const [indexPatternTitles, setIndexPatternTitles] = useState([]); - const [createIndexPattern, setCreateIndexPattern] = useState(defaults.createIndexPattern); + const [transformId, setTransformId] = useState(defaults.transformId); + const [transformDescription, setTransformDescription] = useState( + defaults.transformDescription + ); + const [destinationIndex, setDestinationIndex] = useState( + defaults.destinationIndex + ); + const [transformIds, setTransformIds] = useState([]); + const [indexNames, setIndexNames] = useState([]); + const [indexPatternTitles, setIndexPatternTitles] = useState([]); + const [createIndexPattern, setCreateIndexPattern] = useState(defaults.createIndexPattern); - // Continuous mode state - const [isContinuousModeEnabled, setContinuousModeEnabled] = useState( - defaults.isContinuousModeEnabled - ); + // Continuous mode state + const [isContinuousModeEnabled, setContinuousModeEnabled] = useState( + defaults.isContinuousModeEnabled + ); - const api = useApi(); + const api = useApi(); - // fetch existing transform IDs and indices once for form validation - useEffect(() => { - // use an IIFE to avoid returning a Promise to useEffect. - (async function() { - try { - setTransformIds( - (await api.getTransforms()).transforms.map( - (transform: TransformPivotConfig) => transform.id - ) - ); - } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', { - defaultMessage: 'An error occurred getting the existing transform IDs:', - }), - text: toMountPoint(), - }); - } + // fetch existing transform IDs and indices once for form validation + useEffect(() => { + // use an IIFE to avoid returning a Promise to useEffect. + (async function() { + try { + setTransformIds( + (await api.getTransforms()).transforms.map( + (transform: TransformPivotConfig) => transform.id + ) + ); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', { + defaultMessage: 'An error occurred getting the existing transform IDs:', + }), + text: toMountPoint(), + }); + } - try { - setIndexNames((await api.getIndices()).map(index => index.name)); - } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', { - defaultMessage: 'An error occurred getting the existing index names:', - }), - text: toMountPoint(), - }); - } + try { + setIndexNames((await api.getIndices()).map(index => index.name)); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', { + defaultMessage: 'An error occurred getting the existing index names:', + }), + text: toMountPoint(), + }); + } - try { - setIndexPatternTitles(await kibanaContext.indexPatterns.getTitles()); - } catch (e) { - toastNotifications.addDanger({ - title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles', { - defaultMessage: 'An error occurred getting the existing index pattern titles:', - }), - text: toMountPoint(), - }); - } - })(); - // custom comparison - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [kibanaContext.initialized]); + try { + setIndexPatternTitles(await deps.plugins.data.indexPatterns.getTitles()); + } catch (e) { + toastNotifications.addDanger({ + title: i18n.translate( + 'xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles', + { + defaultMessage: 'An error occurred getting the existing index pattern titles:', + } + ), + text: toMountPoint(), + }); + } + })(); + // run once + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - const dateFieldNames = kibanaContext.currentIndexPattern.fields - .filter(f => f.type === 'date') - .map(f => f.name) - .sort(); - const isContinuousModeAvailable = dateFieldNames.length > 0; - const [continuousModeDateField, setContinuousModeDateField] = useState( - isContinuousModeAvailable ? dateFieldNames[0] : '' - ); - const [continuousModeDelay, setContinuousModeDelay] = useState(defaults.continuousModeDelay); - const isContinuousModeDelayValid = delayValidator(continuousModeDelay); + const dateFieldNames = searchItems.indexPattern.fields + .filter(f => f.type === 'date') + .map(f => f.name) + .sort(); + const isContinuousModeAvailable = dateFieldNames.length > 0; + const [continuousModeDateField, setContinuousModeDateField] = useState( + isContinuousModeAvailable ? dateFieldNames[0] : '' + ); + const [continuousModeDelay, setContinuousModeDelay] = useState(defaults.continuousModeDelay); + const isContinuousModeDelayValid = delayValidator(continuousModeDelay); - const transformIdExists = transformIds.some(id => transformId === id); - const transformIdEmpty = transformId === ''; - const transformIdValid = isTransformIdValid(transformId); + const transformIdExists = transformIds.some(id => transformId === id); + const transformIdEmpty = transformId === ''; + const transformIdValid = isTransformIdValid(transformId); - const indexNameExists = indexNames.some(name => destinationIndex === name); - const indexNameEmpty = destinationIndex === ''; - const indexNameValid = isValidIndexName(destinationIndex); - const indexPatternTitleExists = indexPatternTitles.some(name => destinationIndex === name); + const indexNameExists = indexNames.some(name => destinationIndex === name); + const indexNameEmpty = destinationIndex === ''; + const indexNameValid = isValidIndexName(destinationIndex); + const indexPatternTitleExists = indexPatternTitles.some(name => destinationIndex === name); - const valid = - !transformIdEmpty && - transformIdValid && - !transformIdExists && - !indexNameEmpty && - indexNameValid && - (!indexPatternTitleExists || !createIndexPattern) && - (!isContinuousModeAvailable || (isContinuousModeAvailable && isContinuousModeDelayValid)); + const valid = + !transformIdEmpty && + transformIdValid && + !transformIdExists && + !indexNameEmpty && + indexNameValid && + (!indexPatternTitleExists || !createIndexPattern) && + (!isContinuousModeAvailable || (isContinuousModeAvailable && isContinuousModeDelayValid)); - // expose state to wizard - useEffect(() => { - onChange({ + // expose state to wizard + useEffect(() => { + onChange({ + continuousModeDateField, + continuousModeDelay, + createIndexPattern, + isContinuousModeEnabled, + transformId, + transformDescription, + destinationIndex, + touched: true, + valid, + }); + // custom comparison + /* eslint-disable react-hooks/exhaustive-deps */ + }, [ continuousModeDateField, continuousModeDelay, createIndexPattern, @@ -177,232 +201,223 @@ export const StepDetailsForm: FC = React.memo(({ overrides = {}, onChange transformId, transformDescription, destinationIndex, - touched: true, valid, - }); - // custom comparison - /* eslint-disable react-hooks/exhaustive-deps */ - }, [ - continuousModeDateField, - continuousModeDelay, - createIndexPattern, - isContinuousModeEnabled, - transformId, - transformDescription, - destinationIndex, - valid, - /* eslint-enable react-hooks/exhaustive-deps */ - ]); + /* eslint-enable react-hooks/exhaustive-deps */ + ]); - return ( -
- - - setTransformId(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.transformIdInputAriaLabel', - { - defaultMessage: 'Choose a unique transform ID.', - } - )} + return ( +
+ + - - - setTransformDescription(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel', - { - defaultMessage: 'Choose an optional transform description.', - } - )} - data-test-subj="transformDescriptionInput" - /> - - - {i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', { - defaultMessage: 'Invalid destination index name.', - })} -
- - {i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink', - { - defaultMessage: 'Learn more about index name limitations.', - } - )} - - , - ] - } - > - setDestinationIndex(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel', + error={[ + ...(!transformIdEmpty && !transformIdValid + ? [ + i18n.translate('xpack.transform.stepDetailsForm.transformIdInvalidError', { + defaultMessage: + 'Must contain lowercase alphanumeric characters (a-z and 0-9), hyphens, and underscores only and must start and end with alphanumeric characters.', + }), + ] + : []), + ...(transformIdExists + ? [ + i18n.translate('xpack.transform.stepDetailsForm.transformIdExistsError', { + defaultMessage: 'A transform with this ID already exists.', + }), + ] + : []), + ]} + > + setTransformId(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.transformIdInputAriaLabel', + { + defaultMessage: 'Choose a unique transform ID.', + } + )} + isInvalid={(!transformIdEmpty && !transformIdValid) || transformIdExists} + data-test-subj="transformIdInput" + /> +
+ - - - setCreateIndexPattern(!createIndexPattern)} - data-test-subj="transformCreateIndexPatternSwitch" - /> - - - setContinuousModeEnabled(!isContinuousModeEnabled)} - disabled={isContinuousModeAvailable === false} - data-test-subj="transformContinuousModeSwitch" - /> - - {isContinuousModeEnabled && ( - - + setTransformDescription(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.transformDescriptionInputAriaLabel', { - defaultMessage: 'Date field', + defaultMessage: 'Choose an optional transform description.', } )} - helpText={i18n.translate( - 'xpack.transform.stepDetailsForm.continuousModeDateFieldHelpText', + data-test-subj="transformDescriptionInput" + /> + + + {i18n.translate('xpack.transform.stepDetailsForm.destinationIndexInvalidError', { + defaultMessage: 'Invalid destination index name.', + })} +
+ + {i18n.translate( + 'xpack.transform.stepDetailsForm.destinationIndexInvalidErrorLink', + { + defaultMessage: 'Learn more about index name limitations.', + } + )} + +
, + ] + } + > + setDestinationIndex(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.destinationIndexInputAriaLabel', { - defaultMessage: - 'Select the date field that can be used to identify new documents.', + defaultMessage: 'Choose a unique destination index name.', } )} - > - ({ text }))} - value={continuousModeDateField} - onChange={e => setContinuousModeDateField(e.target.value)} - data-test-subj="transformContinuousDateFieldSelect" - /> - - + + + - setContinuousModeDelay(e.target.value)} - aria-label={i18n.translate( - 'xpack.transform.stepDetailsForm.continuousModeAriaLabel', + checked={createIndexPattern === true} + onChange={() => setCreateIndexPattern(!createIndexPattern)} + data-test-subj="transformCreateIndexPatternSwitch" + /> + + + setContinuousModeEnabled(!isContinuousModeEnabled)} + disabled={isContinuousModeAvailable === false} + data-test-subj="transformContinuousModeSwitch" + /> + + {isContinuousModeEnabled && ( + + + ({ text }))} + value={continuousModeDateField} + onChange={e => setContinuousModeDateField(e.target.value)} + data-test-subj="transformContinuousDateFieldSelect" + /> + + - - - )} -
-
- ); -}); + error={ + !isContinuousModeDelayValid && [ + i18n.translate('xpack.transform.stepDetailsForm.continuousModeDelayError', { + defaultMessage: 'Invalid delay format', + }), + ] + } + helpText={i18n.translate( + 'xpack.transform.stepDetailsForm.continuousModeDelayHelpText', + { + defaultMessage: 'Time delay between current time and latest input data time.', + } + )} + > + setContinuousModeDelay(e.target.value)} + aria-label={i18n.translate( + 'xpack.transform.stepDetailsForm.continuousModeAriaLabel', + { + defaultMessage: 'Choose a delay.', + } + )} + isInvalid={!isContinuousModeDelayValid} + data-test-subj="transformContinuousDelayInput" + /> +
+ + )} +
+
+ ); + } +); diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx index f1861755d9742..0773ecbb1d8d3 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/wizard/wizard.tsx @@ -10,9 +10,8 @@ import { i18n } from '@kbn/i18n'; import { EuiSteps, EuiStepStatus } from '@elastic/eui'; -import { useKibanaContext } from '../../../../lib/kibana'; - import { getCreateRequestBody, TransformPivotConfig } from '../../../../common'; +import { SearchItems } from '../../../../hooks/use_search_items'; import { applyTransformConfigToDefineState, @@ -46,6 +45,7 @@ interface DefinePivotStepProps { stepDefineState: StepDefineExposedState; setCurrentStep: React.Dispatch>; setStepDefineState: React.Dispatch>; + searchItems: SearchItems; } const StepDefine: FC = ({ @@ -53,6 +53,7 @@ const StepDefine: FC = ({ stepDefineState, setCurrentStep, setStepDefineState, + searchItems, }) => { const definePivotRef = useRef(null); @@ -61,31 +62,36 @@ const StepDefine: FC = ({
{isCurrentStep && ( - + setCurrentStep(WIZARD_STEPS.DETAILS)} nextActive={stepDefineState.valid} /> )} - {!isCurrentStep && } + {!isCurrentStep && ( + + )} ); }; interface WizardProps { cloneConfig?: TransformPivotConfig; + searchItems: SearchItems; } -export const Wizard: FC = React.memo(({ cloneConfig }) => { - const kibanaContext = useKibanaContext(); - +export const Wizard: FC = React.memo(({ cloneConfig, searchItems }) => { // The current WIZARD_STEP const [currentStep, setCurrentStep] = useState(WIZARD_STEPS.DEFINE); // The DEFINE state const [stepDefineState, setStepDefineState] = useState( - applyTransformConfigToDefineState(getDefaultStepDefineState(kibanaContext), cloneConfig) + applyTransformConfigToDefineState(getDefaultStepDefineState(searchItems), cloneConfig) ); // The DETAILS state @@ -95,7 +101,11 @@ export const Wizard: FC = React.memo(({ cloneConfig }) => { const stepDetails = currentStep === WIZARD_STEPS.DETAILS ? ( - + ) : ( ); @@ -122,7 +132,7 @@ export const Wizard: FC = React.memo(({ cloneConfig }) => { } }, []); - const indexPattern = kibanaContext.currentIndexPattern; + const { indexPattern } = searchItems; const transformConfig = getCreateRequestBody( indexPattern.title, @@ -154,6 +164,7 @@ export const Wizard: FC = React.memo(({ cloneConfig }) => { stepDefineState={stepDefineState} setCurrentStep={setCurrentStep} setStepDefineState={setStepDefineState} + searchItems={searchItems} /> ), }, diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/create_transform_section.tsx b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/create_transform_section.tsx index e92ba256256a4..df487ff6cc9b6 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/create_transform_section.tsx +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/create_transform_section.tsx @@ -24,9 +24,9 @@ import { import { APP_CREATE_TRANSFORM_CLUSTER_PRIVILEGES } from '../../../../common/constants'; import { useDocumentationLinks } from '../../app_dependencies'; +import { useSearchItems } from '../../hooks/use_search_items'; import { breadcrumbService, docTitleService, BREADCRUMB_SECTION } from '../../services/navigation'; import { PrivilegesWrapper } from '../../lib/authorization'; -import { KibanaProvider, RenderOnlyWithInitializedKibanaContext } from '../../lib/kibana'; import { Wizard } from './components/wizard'; @@ -40,55 +40,53 @@ export const CreateTransformSection: FC = ({ match }) => { const { esTransform } = useDocumentationLinks(); + const { searchItems } = useSearchItems(match.params.savedObjectId); + return ( - - - - - -

- -   - -

-
- - - - - -
-
- - - - - - -
-
+ + + + +

+ +   + +

+
+ + + + + +
+
+ + + {searchItems !== undefined && } + +
); }; diff --git a/x-pack/legacy/plugins/transform/public/plugin.ts b/x-pack/legacy/plugins/transform/public/plugin.ts index 23fad00fb0786..7b5fbbb4a2151 100644 --- a/x-pack/legacy/plugins/transform/public/plugin.ts +++ b/x-pack/legacy/plugins/transform/public/plugin.ts @@ -11,7 +11,6 @@ import { breadcrumbService } from './app/services/navigation'; import { docTitleService } from './app/services/navigation'; import { textService } from './app/services/text'; import { uiMetricService } from './app/services/ui_metric'; -import { createSavedSearchesLoader } from '../../../../../src/plugins/discover/public'; export class Plugin { public start(core: ShimCore, plugins: ShimPlugins): void { @@ -27,7 +26,7 @@ export class Plugin { savedObjects, overlays, } = core; - const { data, management, savedSearches: coreSavedSearches, uiMetric, xsrfToken } = plugins; + const { data, management, uiMetric, xsrfToken } = plugins; // AppCore/AppPlugins to be passed on as React context const appDependencies = { @@ -46,7 +45,6 @@ export class Plugin { plugins: { data, management, - savedSearches: coreSavedSearches, xsrfToken, }, }; @@ -61,14 +59,6 @@ export class Plugin { }), order: 3, mount(params) { - const savedSearches = createSavedSearchesLoader({ - savedObjectsClient: core.savedObjects.client, - indexPatterns: plugins.data.indexPatterns, - chrome: core.chrome, - overlays: core.overlays, - }); - coreSavedSearches.setClient(savedSearches); - breadcrumbService.setup(params.setBreadcrumbs); params.setBreadcrumbs([ { diff --git a/x-pack/legacy/plugins/transform/public/shared_imports.ts b/x-pack/legacy/plugins/transform/public/shared_imports.ts index b077cd8836c4b..1ca71f8c4aa77 100644 --- a/x-pack/legacy/plugins/transform/public/shared_imports.ts +++ b/x-pack/legacy/plugins/transform/public/shared_imports.ts @@ -4,6 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ +export { createSavedSearchesLoader } from '../../../../../src/plugins/discover/public'; export { XJsonMode } from '../../../../plugins/es_ui_shared/console_lang/ace/modes/x_json'; export { collapseLiteralStrings, diff --git a/x-pack/legacy/plugins/transform/public/shim.ts b/x-pack/legacy/plugins/transform/public/shim.ts index 95f54605377a8..9941aabcf3255 100644 --- a/x-pack/legacy/plugins/transform/public/shim.ts +++ b/x-pack/legacy/plugins/transform/public/shim.ts @@ -11,7 +11,6 @@ import { docTitle } from 'ui/doc_title/doc_title'; // @ts-ignore: allow traversal to fail on x-pack build import { createUiStatsReporter } from '../../../../../src/legacy/core_plugins/ui_metric/public'; -import { SavedSearchLoader } from '../../../../../src/legacy/core_plugins/kibana/public/discover/np_ready/types'; import { TRANSFORM_DOC_PATHS } from './app/constants'; @@ -33,7 +32,7 @@ export type AppCore = Pick< | 'overlays' | 'notifications' >; -export type AppPlugins = Pick; +export type AppPlugins = Pick; export interface AppDependencies { core: AppCore; @@ -61,18 +60,10 @@ export interface ShimPlugins extends NpPlugins { uiMetric: { createUiStatsReporter: typeof createUiStatsReporter; }; - savedSearches: { - getClient(): any; - setClient(client: any): void; - }; xsrfToken: string; } export function createPublicShim(): { core: ShimCore; plugins: ShimPlugins } { - // This is an Angular service, which is why we use this provider pattern - // to access it within our React app. - let savedSearches: SavedSearchLoader; - const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = npStart.core.docLinks; return { @@ -94,12 +85,6 @@ export function createPublicShim(): { core: ShimCore; plugins: ShimPlugins } { }, plugins: { ...npStart.plugins, - savedSearches: { - setClient: (client: any): void => { - savedSearches = client; - }, - getClient: (): any => savedSearches, - }, uiMetric: { createUiStatsReporter, }, From 2ad86c99aa15cb4eaa4ae5bdfefa8c2d7d37af3b Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 4 Mar 2020 14:56:08 +0100 Subject: [PATCH 2/2] [ML] Updated snapshots. --- .../__snapshots__/source_index_preview.test.tsx.snap | 1 + .../step_create/__snapshots__/step_create_form.test.tsx.snap | 1 + .../step_define/__snapshots__/pivot_preview.test.tsx.snap | 1 + .../step_define/__snapshots__/step_define_form.test.tsx.snap | 1 + 4 files changed, 4 insertions(+) diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap index 5f73e91aa9aca..6d2d3d5c4a6a5 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/source_index_preview/__snapshots__/source_index_preview.test.tsx.snap @@ -144,6 +144,7 @@ exports[`Transform: Minimal initialization 1`] = ` "base": "https://www.elastic.co/guide/en/logstash/mocked-test-branch", }, "management": Object { + "dashboardSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-dashboard-settings", "kibanaSearchSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-search-settings", }, "metricbeat": Object { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap index d7d53bd70d47d..db4ff0c1a99ae 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_create/__snapshots__/step_create_form.test.tsx.snap @@ -144,6 +144,7 @@ exports[`Transform: Minimal initialization 1`] = ` "base": "https://www.elastic.co/guide/en/logstash/mocked-test-branch", }, "management": Object { + "dashboardSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-dashboard-settings", "kibanaSearchSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-search-settings", }, "metricbeat": Object { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap index c3f446d7a04d1..bc0d983c6e022 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/pivot_preview.test.tsx.snap @@ -144,6 +144,7 @@ exports[`Transform: Minimal initialization 1`] = ` "base": "https://www.elastic.co/guide/en/logstash/mocked-test-branch", }, "management": Object { + "dashboardSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-dashboard-settings", "kibanaSearchSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-search-settings", }, "metricbeat": Object { diff --git a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap index b524921569176..30c57a9f3f4ae 100644 --- a/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap +++ b/x-pack/legacy/plugins/transform/public/app/sections/create_transform/components/step_define/__snapshots__/step_define_form.test.tsx.snap @@ -144,6 +144,7 @@ exports[`Transform: Minimal initialization 1`] = ` "base": "https://www.elastic.co/guide/en/logstash/mocked-test-branch", }, "management": Object { + "dashboardSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-dashboard-settings", "kibanaSearchSettings": "https://www.elastic.co/guide/en/kibana/mocked-test-branch/advanced-options.html#kibana-search-settings", }, "metricbeat": Object {