Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor email input handling to format comma-separated addresses #193128

Merged
merged 7 commits into from
Oct 2, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@

import React from 'react';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { render, fireEvent, screen } from '@testing-library/react';
import { render, fireEvent, screen, within } from '@testing-library/react';
import { __IntlProvider as IntlProvider } from '@kbn/i18n-react';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { triggersActionsUiMock } from '@kbn/triggers-actions-ui-plugin/public/mocks';
import EmailParamsFields from './email_params';
import { getIsExperimentalFeatureEnabled } from '../../common/get_experimental_features';
import { getFormattedEmailOptions } from './email_params';

jest.mock('@kbn/kibana-react-plugin/public', () => ({
useKibana: jest.fn(),
Expand All @@ -28,6 +29,24 @@ const mockKibana = () => {
});
};

const emailTestCases = [
{
field: 'to',
fieldValue: '[email protected], [email protected] , [email protected], ',
expected: ['[email protected]', '[email protected]', '[email protected]'],
},
{
field: 'cc',
fieldValue: '[email protected], [email protected] , [email protected], ',
expected: ['[email protected]', '[email protected]', '[email protected]'],
},
{
field: 'bcc',
fieldValue: '[email protected], [email protected] , [email protected], ',
expected: ['[email protected]', '[email protected]', '[email protected]'],
},
];

describe('EmailParamsFields renders', () => {
beforeEach(() => {
jest.clearAllMocks();
Expand Down Expand Up @@ -62,6 +81,40 @@ describe('EmailParamsFields renders', () => {
expect(await screen.findByTestId('messageTextArea')).toBeVisible();
});

emailTestCases.forEach(({ field, fieldValue, expected }) => {
test(`"${field}" field value updates correctly when comma-separated emails are pasted`, async () => {
const actionParams = {
cc: ['[email protected]'],
bcc: ['[email protected]'],
to: ['[email protected]'],
subject: 'test',
message: 'test message',
};

const editAction = jest.fn();

render(
<IntlProvider locale="en">
<EmailParamsFields
actionParams={actionParams}
errors={{ to: [], cc: [], bcc: [], subject: [], message: [] }}
editAction={editAction}
defaultMessage={'Some default message'}
index={0}
/>
</IntlProvider>
);

const euiComboBox = screen.getByTestId(`${field}EmailAddressInput`);
const input = within(euiComboBox).getByTestId('comboBoxSearchInput');
fireEvent.change(input, { target: { value: fieldValue } });
expect(input).toHaveValue(fieldValue);

fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
expect(editAction).toHaveBeenCalledWith(field, expected, 0);
});
});

test('message param field is rendered with default value if not set', () => {
const actionParams = {
cc: [],
Expand Down Expand Up @@ -234,3 +287,48 @@ describe('EmailParamsFields renders', () => {
expect(editAction).not.toHaveBeenCalled();
});
});

describe('getFormattedEmailOptions', () => {
test('should return new options added to previous options', () => {
const searchValue = '[email protected], [email protected]';
const previousOptions = [{ label: '[email protected]' }];
const newOptions = getFormattedEmailOptions(searchValue, previousOptions);

expect(newOptions).toEqual([
{ label: '[email protected]' },
{ label: '[email protected]' },
{ label: '[email protected]' },
]);
});

test('should trim extra spaces in search value', () => {
const searchValue = ' [email protected] , [email protected] , ';
const previousOptions: Array<{ label: string }> = [];
const newOptions = getFormattedEmailOptions(searchValue, previousOptions);

expect(newOptions).toEqual([{ label: '[email protected]' }, { label: '[email protected]' }]);
});

test('should prevent duplicate email addresses', () => {
const searchValue = '[email protected], [email protected]';
const previousOptions = [{ label: '[email protected]' }, { label: '[email protected]' }];
const newOptions = getFormattedEmailOptions(searchValue, previousOptions);

expect(newOptions).toEqual([{ label: '[email protected]' }, { label: '[email protected]' }]);
});

test('should return previous options if search value is empty', () => {
const searchValue = '';
const previousOptions = [{ label: '[email protected]' }];
const newOptions = getFormattedEmailOptions(searchValue, previousOptions);
expect(newOptions).toEqual([{ label: '[email protected]' }]);
});

test('should handle single email without comma', () => {
const searchValue = '[email protected]';
const previousOptions = [{ label: '[email protected]' }];
const newOptions = getFormattedEmailOptions(searchValue, previousOptions);

expect(newOptions).toEqual([{ label: '[email protected]' }, { label: '[email protected]' }]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ import { EmailActionParams } from '../types';

const noop = () => {};

export const getFormattedEmailOptions = (
searchValue: string,
previousOptions: Array<{ label: string }>
): Array<{ label: string }> => {
if (!searchValue.trim()) return previousOptions;
const previousEmails: string[] = previousOptions.map((option) => option.label);
const allUniqueEmails: Set<string> = new Set(previousEmails);
searchValue.split(',').forEach((email) => {
const trimmedEmail = email.trim();
if (trimmedEmail) allUniqueEmails.add(trimmedEmail);
});
const formattedOptions = Array.from(allUniqueEmails).map((email) => ({ label: email }));
return formattedOptions;
};

export const EmailParamsFields = ({
actionParams,
editAction,
Expand Down Expand Up @@ -105,7 +120,7 @@ export const EmailParamsFields = ({
data-test-subj="toEmailAddressInput"
selectedOptions={toOptions}
onCreateOption={(searchValue: string) => {
const newOptions = [...toOptions, { label: searchValue }];
const newOptions = getFormattedEmailOptions(searchValue, toOptions);
editAction(
'to',
newOptions.map((newOption) => newOption.label),
Expand Down Expand Up @@ -148,7 +163,7 @@ export const EmailParamsFields = ({
data-test-subj="ccEmailAddressInput"
selectedOptions={ccOptions}
onCreateOption={(searchValue: string) => {
const newOptions = [...ccOptions, { label: searchValue }];
const newOptions = getFormattedEmailOptions(searchValue, ccOptions);
editAction(
'cc',
newOptions.map((newOption) => newOption.label),
Expand Down Expand Up @@ -192,7 +207,7 @@ export const EmailParamsFields = ({
data-test-subj="bccEmailAddressInput"
selectedOptions={bccOptions}
onCreateOption={(searchValue: string) => {
const newOptions = [...bccOptions, { label: searchValue }];
const newOptions = getFormattedEmailOptions(searchValue, bccOptions);
editAction(
'bcc',
newOptions.map((newOption) => newOption.label),
Expand Down