Skip to content

Commit

Permalink
try to change emailselect to textinput
Browse files Browse the repository at this point in the history
  • Loading branch information
calypsomatic committed Feb 25, 2025
1 parent 6387e1a commit 4605ee3
Show file tree
Hide file tree
Showing 6 changed files with 29 additions and 114 deletions.
11 changes: 6 additions & 5 deletions src/billing/Members/Members.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EmailSelect } from 'src/groups/Members/EmailSelect';
import { Member } from 'src/groups/Members/MemberTable';
import { Billing, BillingContract } from 'src/libs/ajax/billing/Billing';
import { Groups, GroupsContract } from 'src/libs/ajax/Groups';
import { Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces';
import { asMockedFn, MockedFn, partial, renderWithAppContexts } from 'src/testing/test-utils';

jest.mock('src/libs/ajax/billing/Billing');
Expand Down Expand Up @@ -84,6 +85,11 @@ describe('Members', () => {
const addProjectUsers: MockedFn<BillingContract['addProjectUsers']> = jest.fn();
asMockedFn(Billing).mockReturnValue(partial<BillingContract>({ addProjectUsers }));
// Next 2 mocks are needed for suggestions in the NewUserModal.
asMockedFn(Workspaces).mockReturnValue(
partial<WorkspacesAjaxContract>({
getShareLog: jest.fn(async () => []),
})
);
asMockedFn(Groups).mockReturnValue(
partial<GroupsContract>({
list: jest.fn(async () => []),
Expand All @@ -95,11 +101,6 @@ describe('Members', () => {
const defaultProps = {
label: 'User emails',
placeholder: 'Test emails',
isMulti: true,
isClearable: true,
isSearchable: true,
options: ['test-user@company.com', 'test-user2@company.com'],
emails: ['test-user@company.com'],
setEmails: jest.fn(),
};

Expand Down
56 changes: 6 additions & 50 deletions src/groups/Members/EmailSelect.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@ describe('EmailSelect', () => {
const defaultProps = {
label: 'User emails',
placeholder: 'Type or select user emails',
isMulti: true,
isClearable: true,
isSearchable: true,
options: ['test1@example.com', 'test2@example.com'],
emails: ['test1@example.com'],
setEmails: jest.fn(),
};

Expand All @@ -29,67 +24,28 @@ describe('EmailSelect', () => {
expect(label).toBeInTheDocument();
});

it('calls setEmails when an email is selected', () => {
it('calls setEmails when an email is entered', () => {
// Arrange
render(<EmailSelect {...defaultProps} />);
const input = screen.getByLabelText(defaultProps.placeholder);

// Act
fireEvent.change(input, { target: { value: 'test2@example.com' } });
fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });
// fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });

// Assert
expect(defaultProps.setEmails).toHaveBeenCalledWith(['test1@example.com', 'test2@example.com']);
expect(defaultProps.setEmails).toHaveBeenCalledWith(['test2@example.com']);
});

it('calls setEmails when an email is removed', () => {
it('divides emails by comma', () => {
// Arrange
render(<EmailSelect {...defaultProps} />);
const input = screen.getByLabelText(defaultProps.placeholder);

// Act
fireEvent.keyDown(input, { key: 'Backspace', code: 'Backspace' });
fireEvent.change(input, { target: { value: 'test2@example.com,test1@example.com' } });

// Assert
expect(defaultProps.setEmails).toHaveBeenCalledWith([]);
});

it('renders the correct number of selected options', () => {
// Arrange
render(<EmailSelect {...defaultProps} />);
const input = screen.getByLabelText(defaultProps.placeholder);

// Act
fireEvent.focus(input);
const options = screen.getAllByRole('button'); // Each selected option has a remove button

// Assert
expect(options).toHaveLength(defaultProps.emails.length);
});

it('updates searchValue on input change', () => {
// Arrange
render(<EmailSelect {...defaultProps} />);
const input = screen.getByLabelText(defaultProps.placeholder);

// Act
fireEvent.change(input, { target: { value: 'newemail@example.com' } });

// Assert
// @ts-ignore
expect(input.value).toBe('newemail@example.com');
});

it('saves searchValue to emails on blur', () => {
// Arrange
render(<EmailSelect {...defaultProps} />);
const input = screen.getByLabelText(defaultProps.placeholder);

// Act
fireEvent.change(input, { target: { value: 'newemail@example.com' } });
fireEvent.blur(input);

// Assert
expect(defaultProps.setEmails).toHaveBeenCalledWith(['test1@example.com', 'newemail@example.com']);
expect(defaultProps.setEmails).toHaveBeenCalledWith(['test2@example.com', 'test1@example.com']);
});
});
59 changes: 11 additions & 48 deletions src/groups/Members/EmailSelect.tsx
Original file line number Diff line number Diff line change
@@ -1,80 +1,43 @@
import { CreatableSelect, useUniqueId } from '@terra-ui-packages/components';
import { useUniqueId } from '@terra-ui-packages/components';
import _ from 'lodash/fp';
import React, { useState } from 'react';
import React from 'react';
import { TextInput } from 'src/components/input';
import { FormLabel } from 'src/libs/forms';

interface EmailSelectProps {
label?: string;
placeholder?: string;
isMulti?: boolean;
isClearable?: boolean;
isSearchable?: boolean;
options: string[];
emails: string[];
setEmails: (values: string[]) => void;
}

export const EmailSelect: React.FC<EmailSelectProps> = ({
label = 'User emails',
placeholder = 'Type or select user emails',
isMulti = true,
isClearable = true,
isSearchable = true,
options,
emails,
placeholder = 'Type user emails separated by commas',
setEmails,
}) => {
const [searchValue, setSearchValue] = useState<string>('');

const emailInputId = useUniqueId();
const emptySearchValue = (searchValue: string) => searchValue === '';
const addSelectedOptions = (options: string[]) => {
const selectedOptions: string[] = _.flatMap(
(option) =>
option
.split(',')
.map((email) => email.trim())
.filter((email) => email !== ''),
options
);
const addSelectedOptions = (emails: string) => {
const selectedOptions: string[] = emails
.split(',')
.map((email) => email.trim())
.filter((email) => email !== '');
const newEmail: string | undefined = _.find((email: string) => !emails.includes(email), selectedOptions);
if (newEmail || newEmail === undefined) {
setEmails(selectedOptions);
}
setSearchValue('');
};

const handleOnInputChange = (inputValue: any) => {
!emptySearchValue(inputValue) && setSearchValue(inputValue);
};

const handleOnBlur = () => {
!emptySearchValue(searchValue) && addSelectedOptions([...emails, searchValue]);
};

const handleOnChange = (options: Array<{ value: string; label: string }>) => {
addSelectedOptions(_.map((option) => option.value, options));
};

return (
<>
<FormLabel id={emailInputId} required style={{ marginTop: '0.25rem' }}>
{label}
</FormLabel>
<CreatableSelect
<TextInput
id={emailInputId}
isMulti={isMulti}
isClearable={isClearable}
isSearchable={isSearchable}
placeholder={placeholder}
aria-label={placeholder}
value={_.map((value: string) => ({ value, label: value }), emails)}
options={_.map((value: string) => ({ value, label: value }), options)}
onInputChange={handleOnInputChange}
onBlur={handleOnBlur}
onChange={handleOnChange}
onChange={addSelectedOptions}
height={200}
noOptionsMessage={() => null}
/>
</>
);
Expand Down
8 changes: 4 additions & 4 deletions src/groups/Members/NewMemberModal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('NewMemberModal', () => {
it('handles user input for emails and roles', async () => {
// Arrange
render(<NewMemberModal {...defaultProps} />);
const emailInput = screen.getByLabelText('Type or select user emails');
const emailInput = screen.getByLabelText('Type user emails separated by commas');
const roleSelect = screen.getByLabelText('Select Role');

// Act
Expand All @@ -87,13 +87,13 @@ describe('NewMemberModal', () => {
it('submits valid data and calls addFunction', async () => {
// Arrange
render(<NewMemberModal {...defaultProps} />);
const emailInput = screen.getByLabelText('Type or select user emails');
const emailInput = screen.getByLabelText('Type user emails separated by commas');
const addButton = screen.getByText('Add Users');
const userEmails = ['test1@example.com', 'test2@example.com'];
const userRole = 'Member';

// Act
fireEvent.change(emailInput, { target: { value: userEmails } });
fireEvent.change(emailInput, { target: { value: userEmails.join(',') } });
fireEvent.keyDown(emailInput, { key: 'Enter', code: 'Enter' });
fireEvent.click(addButton);

Expand All @@ -114,7 +114,7 @@ describe('NewMemberModal', () => {

render(<NewMemberModal {...defaultProps} />);

const emailInput = screen.getByLabelText('Type or select user emails');
const emailInput = screen.getByLabelText('Type user emails separated by commas');
const addButton = screen.getByText('Add Users');

fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
Expand Down
2 changes: 1 addition & 1 deletion src/groups/Members/NewMemberModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export const NewMemberModal = (props: NewMemberModalProps) => {
>
<div style={{ display: 'flex', alignItems: 'flex-end', gap: '1.5rem' }}>
<div style={{ flex: 2, width: '500px', alignSelf: 'flex-start', marginTop: '0.75rem' }}>
<EmailSelect options={[]} emails={userEmails} setEmails={setUserEmails} />
<EmailSelect setEmails={setUserEmails} />
</div>
<div style={{ flex: '1', alignSelf: 'flex-start' }}>
<RoleSelect options={[memberLabel, adminLabel]} role={role} setRole={setRole} />
Expand Down
7 changes: 1 addition & 6 deletions src/workspaces/ShareWorkspaceModal/ShareWorkspaceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,7 @@ const ShareWorkspaceModal: React.FC<ShareWorkspaceModalProps> = (props: ShareWor
<Modal title='Share Workspace' width={720} showButtons={false} onDismiss={onDismiss}>
<div style={{ display: 'flex', alignItems: 'flex-end', gap: '0.5rem' }}>
<div style={{ flexGrow: 2, width: '400px', alignSelf: 'flex-start' }}>
<EmailSelect
placeholder='Add people or groups'
options={[]}
emails={searchValues}
setEmails={setSearchValues}
/>
<EmailSelect placeholder='Add people or groups' setEmails={setSearchValues} />
</div>
<div style={{ flexGrow: 1, alignSelf: 'stretch', marginTop: '1.4rem' }}>
<AclInput
Expand Down

0 comments on commit 4605ee3

Please sign in to comment.