diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
index 008fc8237c129..74376134f54ff 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.test.tsx
@@ -10,6 +10,7 @@ import { mountWithIntl, nextTick } from '@kbn/test/jest';
import { act } from 'react-dom/test-utils';
import { EsIndexActionConnector } from '../types';
import IndexActionConnectorFields from './es_index_connector';
+import { EuiComboBox, EuiSwitch, EuiSwitchEvent, EuiSelect } from '@elastic/eui';
jest.mock('../../../../common/lib/kibana');
jest.mock('../../../../common/index_controls', () => ({
@@ -19,83 +20,263 @@ jest.mock('../../../../common/index_controls', () => ({
getIndexPatterns: jest.fn(),
}));
+const { getIndexPatterns } = jest.requireMock('../../../../common/index_controls');
+getIndexPatterns.mockResolvedValueOnce([
+ {
+ id: 'indexPattern1',
+ attributes: {
+ title: 'indexPattern1',
+ },
+ },
+ {
+ id: 'indexPattern2',
+ attributes: {
+ title: 'indexPattern2',
+ },
+ },
+]);
+
+const { getFields } = jest.requireMock('../../../../common/index_controls');
+
+async function setup(props) {
+ const wrapper = mountWithIntl();
+ await act(async () => {
+ await nextTick();
+ wrapper.update();
+ });
+ return wrapper;
+}
+
+function setupGetFieldsResponse(getFieldsWithDateMapping) {
+ getFields.mockResolvedValueOnce([
+ {
+ type: getFieldsWithDateMapping ? 'date' : 'keyword',
+ name: 'test1',
+ },
+ {
+ type: 'text',
+ name: 'test2',
+ },
+ ]);
+}
describe('IndexActionConnectorFields renders', () => {
- test('all connector fields is rendered', async () => {
- const { getIndexPatterns } = jest.requireMock('../../../../common/index_controls');
- getIndexPatterns.mockResolvedValueOnce([
- {
- id: 'indexPattern1',
- attributes: {
- title: 'indexPattern1',
- },
- },
- {
- id: 'indexPattern2',
- attributes: {
- title: 'indexPattern2',
- },
- },
- ]);
- const { getFields } = jest.requireMock('../../../../common/index_controls');
- getFields.mockResolvedValueOnce([
- {
- type: 'date',
- name: 'test1',
- },
- {
- type: 'text',
- name: 'test2',
- },
- ]);
-
- const actionConnector = {
- secrets: {},
- id: 'test',
- actionTypeId: '.index',
- name: 'es_index',
- config: {
- index: 'test',
- refresh: false,
- executionTimeField: 'test1',
- },
- } as EsIndexActionConnector;
- const wrapper = mountWithIntl(
- {}}
- editActionSecrets={() => {}}
- readOnly={false}
- />
- );
+ test('renders correctly when creating connector', async () => {
+ const props = {
+ action: {
+ actionTypeId: '.index',
+ config: {},
+ secrets: {},
+ } as EsIndexActionConnector,
+ editActionConfig: () => {},
+ editActionSecrets: () => {},
+ errors: { index: [] },
+ readOnly: false,
+ };
+ const wrapper = mountWithIntl();
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+
+ // time field switch shouldn't show up initially
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+
+ const indexComboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="connectorIndexesComboBox"]');
+
+ // time field switch should show up if index has date type field mapping
+ setupGetFieldsResponse(true);
await act(async () => {
+ indexComboBox.prop('onChange')!([{ label: 'selection' }]);
await nextTick();
wrapper.update();
});
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
- expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').length > 0).toBeTruthy();
- expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').length > 0).toBeTruthy();
+ // time field switch should go away if index does not has date type field mapping
+ setupGetFieldsResponse(false);
+ await act(async () => {
+ indexComboBox.prop('onChange')!([{ label: 'selection' }]);
+ await nextTick();
+ wrapper.update();
+ });
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+
+ // time field dropdown should show up if index has date type field mapping and time switch is clicked
+ setupGetFieldsResponse(true);
+ await act(async () => {
+ indexComboBox.prop('onChange')!([{ label: 'selection' }]);
+ await nextTick();
+ wrapper.update();
+ });
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ const timeFieldSwitch = wrapper
+ .find(EuiSwitch)
+ .filter('[data-test-subj="hasTimeFieldCheckbox"]');
+ await act(async () => {
+ timeFieldSwitch.prop('onChange')!(({
+ target: { checked: true },
+ } as unknown) as EuiSwitchEvent);
+ await nextTick();
+ wrapper.update();
+ });
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
+ });
+
+ test('renders correctly when editing connector - no date type field mapping', async () => {
+ const indexName = 'index-no-date-fields';
+ const props = {
+ action: {
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ },
+ secrets: {},
+ } as EsIndexActionConnector,
+ editActionConfig: () => {},
+ editActionSecrets: () => {},
+ errors: { index: [] },
+ readOnly: false,
+ };
+ setupGetFieldsResponse(false);
+ const wrapper = await setup(props);
+
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+
+ // time related fields shouldn't show up
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+
+ const indexComboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="connectorIndexesComboBox"]');
+ expect(indexComboBox.prop('selectedOptions')).toEqual([{ label: indexName, value: indexName }]);
+
+ const refreshSwitch = wrapper.find(EuiSwitch).filter('[data-test-subj="indexRefreshCheckbox"]');
+ expect(refreshSwitch.prop('checked')).toEqual(false);
+ });
+
+ test('renders correctly when editing connector - refresh set to true', async () => {
+ const indexName = 'index-no-date-fields';
+ const props = {
+ action: {
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: true,
+ },
+ secrets: {},
+ } as EsIndexActionConnector,
+ editActionConfig: () => {},
+ editActionSecrets: () => {},
+ errors: { index: [] },
+ readOnly: false,
+ };
+ setupGetFieldsResponse(false);
+ const wrapper = await setup(props);
+
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeFalsy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+
+ const indexComboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="connectorIndexesComboBox"]');
+ expect(indexComboBox.prop('selectedOptions')).toEqual([{ label: indexName, value: indexName }]);
+
+ const refreshSwitch = wrapper.find(EuiSwitch).filter('[data-test-subj="indexRefreshCheckbox"]');
+ expect(refreshSwitch.prop('checked')).toEqual(true);
+ });
+
+ test('renders correctly when editing connector - with date type field mapping but no time field selected', async () => {
+ const indexName = 'index-no-date-fields';
+ const props = {
+ action: {
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ },
+ secrets: {},
+ } as EsIndexActionConnector,
+ editActionConfig: () => {},
+ editActionSecrets: () => {},
+ errors: { index: [] },
+ readOnly: false,
+ };
+ setupGetFieldsResponse(true);
+ const wrapper = await setup(props);
+
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeFalsy();
+
+ const indexComboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="connectorIndexesComboBox"]');
+ expect(indexComboBox.prop('selectedOptions')).toEqual([{ label: indexName, value: indexName }]);
+
+ const refreshSwitch = wrapper.find(EuiSwitch).filter('[data-test-subj="indexRefreshCheckbox"]');
+ expect(refreshSwitch.prop('checked')).toEqual(false);
+
+ const timeFieldSwitch = wrapper
+ .find(EuiSwitch)
+ .filter('[data-test-subj="hasTimeFieldCheckbox"]');
+ expect(timeFieldSwitch.prop('checked')).toEqual(false);
+ });
+
+ test('renders correctly when editing connector - with date type field mapping and selected time field', async () => {
+ const indexName = 'index-no-date-fields';
+ const props = {
+ action: {
+ name: 'Index Connector for Index With No Date Type',
+ actionTypeId: '.index',
+ config: {
+ index: indexName,
+ refresh: false,
+ executionTimeField: 'test1',
+ },
+ secrets: {},
+ } as EsIndexActionConnector,
+ editActionConfig: () => {},
+ editActionSecrets: () => {},
+ errors: { index: [] },
+ readOnly: false,
+ };
+ setupGetFieldsResponse(true);
+ const wrapper = await setup(props);
- const indexSearchBoxValue = wrapper.find('[data-test-subj="comboBoxSearchInput"]');
- expect(indexSearchBoxValue.first().props().value).toEqual('');
+ expect(wrapper.find('[data-test-subj="connectorIndexesComboBox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="indexRefreshCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="hasTimeFieldCheckbox"]').exists()).toBeTruthy();
+ expect(wrapper.find('[data-test-subj="executionTimeFieldSelect"]').exists()).toBeTruthy();
- const indexComboBox = wrapper.find('#indexConnectorSelectSearchBox');
- indexComboBox.first().simulate('click');
- const event = { target: { value: 'indexPattern1' } };
- indexComboBox.find('input').first().simulate('change', event);
+ const indexComboBox = wrapper
+ .find(EuiComboBox)
+ .filter('[data-test-subj="connectorIndexesComboBox"]');
+ expect(indexComboBox.prop('selectedOptions')).toEqual([{ label: indexName, value: indexName }]);
- const indexSearchBoxValueBeforeEnterData = wrapper.find(
- '[data-test-subj="comboBoxSearchInput"]'
- );
- expect(indexSearchBoxValueBeforeEnterData.first().props().value).toEqual('indexPattern1');
+ const refreshSwitch = wrapper.find(EuiSwitch).filter('[data-test-subj="indexRefreshCheckbox"]');
+ expect(refreshSwitch.prop('checked')).toEqual(false);
- const indexComboBoxClear = wrapper.find('[data-test-subj="comboBoxClearButton"]');
- indexComboBoxClear.first().simulate('click');
+ const timeFieldSwitch = wrapper
+ .find(EuiSwitch)
+ .filter('[data-test-subj="hasTimeFieldCheckbox"]');
+ expect(timeFieldSwitch.prop('checked')).toEqual(true);
- const indexSearchBoxValueAfterEnterData = wrapper.find(
- '[data-test-subj="comboBoxSearchInput"]'
- );
- expect(indexSearchBoxValueAfterEnterData.first().props().value).toEqual('indexPattern1');
+ const timeFieldSelect = wrapper
+ .find(EuiSelect)
+ .filter('[data-test-subj="executionTimeFieldSelect"]');
+ expect(timeFieldSelect.prop('value')).toEqual('test1');
});
});
diff --git a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
index cd3a03ecce15c..9f766e4799018 100644
--- a/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
+++ b/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types/es_index/es_index_connector.tsx
@@ -30,29 +30,44 @@ import {
} from '../../../../common/index_controls';
import { useKibana } from '../../../../common/lib/kibana';
+interface TimeFieldOptions {
+ value: string;
+ text: string;
+}
+
const IndexActionConnectorFields: React.FunctionComponent<
ActionConnectorFieldsProps
> = ({ action, editActionConfig, errors, readOnly }) => {
const { http, docLinks } = useKibana().services;
const { index, refresh, executionTimeField } = action.config;
- const [hasTimeFieldCheckbox, setTimeFieldCheckboxState] = useState(
+ const [showTimeFieldCheckbox, setShowTimeFieldCheckboxState] = useState(
+ executionTimeField != null
+ );
+ const [hasTimeFieldCheckbox, setHasTimeFieldCheckboxState] = useState(
executionTimeField != null
);
const [indexPatterns, setIndexPatterns] = useState([]);
const [indexOptions, setIndexOptions] = useState([]);
- const [timeFieldOptions, setTimeFieldOptions] = useState>([
- firstFieldOption,
- ]);
+ const [timeFieldOptions, setTimeFieldOptions] = useState([]);
const [isIndiciesLoading, setIsIndiciesLoading] = useState(false);
+ const setTimeFields = (fields: TimeFieldOptions[]) => {
+ if (fields.length > 0) {
+ setShowTimeFieldCheckboxState(true);
+ setTimeFieldOptions([firstFieldOption, ...fields]);
+ } else {
+ setShowTimeFieldCheckboxState(false);
+ setTimeFieldOptions([]);
+ }
+ };
+
useEffect(() => {
const indexPatternsFunction = async () => {
setIndexPatterns(await getIndexPatterns());
if (index) {
const currentEsFields = await getFields(http!, [index]);
- const timeFields = getTimeFieldOptions(currentEsFields as any);
- setTimeFieldOptions([firstFieldOption, ...timeFields]);
+ setTimeFields(getTimeFieldOptions(currentEsFields as any));
}
};
indexPatternsFunction();
@@ -123,13 +138,11 @@ const IndexActionConnectorFields: React.FunctionComponent<
// reset time field and expression fields if indices are deleted
if (indices.length === 0) {
- setTimeFieldOptions([]);
+ setTimeFields([]);
return;
}
const currentEsFields = await getFields(http!, indices);
- const timeFields = getTimeFieldOptions(currentEsFields as any);
-
- setTimeFieldOptions([firstFieldOption, ...timeFields]);
+ setTimeFields(getTimeFieldOptions(currentEsFields as any));
}}
onSearchChange={async (search) => {
setIsIndiciesLoading(true);
@@ -172,38 +185,40 @@ const IndexActionConnectorFields: React.FunctionComponent<
}
/>
- {
- setTimeFieldCheckboxState(!hasTimeFieldCheckbox);
- // if changing from checked to not checked (hasTimeField === true),
- // set time field to null
- if (hasTimeFieldCheckbox) {
- editActionConfig('executionTimeField', null);
+ {showTimeFieldCheckbox && (
+ {
+ setHasTimeFieldCheckboxState(!hasTimeFieldCheckbox);
+ // if changing from checked to not checked (hasTimeField === true),
+ // set time field to null
+ if (hasTimeFieldCheckbox) {
+ editActionConfig('executionTimeField', null);
+ }
+ }}
+ label={
+ <>
+
+
+ >
}
- }}
- label={
- <>
-
-
- >
- }
- />
- {hasTimeFieldCheckbox ? (
+ />
+ )}
+ {hasTimeFieldCheckbox && (
<>
>
- ) : null}
+ )}
>
);
};