Skip to content
This repository has been archived by the owner on Sep 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #80 from att/custom_iterations
Browse files Browse the repository at this point in the history
Custom iterations
  • Loading branch information
ohadkoren authored Jan 16, 2024
2 parents 418835e + 4d51b49 commit d85f89d
Show file tree
Hide file tree
Showing 21 changed files with 390 additions and 50 deletions.
4 changes: 3 additions & 1 deletion portal/src/app/components/home/Home.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
@import "src/styles/z-index";

.app_wrapper {
padding: 80px;
padding-block-start: 20px;
padding-inline-start: 80px;
padding-block-end: 40px;
}

.init_state_wrapper {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
.view_in_grafana_wrapper {
display: flex;
align-items: center;
color: var($attPurple);
font-size: 16px;

.eye_icon {
margin-inline-end: 5px;
Expand Down Expand Up @@ -56,11 +54,11 @@
display: none;
}

.grafana_wrapper:hover .hover_image {
.view_in_grafana_wrapper:hover .hover_image {
display: block;
}

.grafana_wrapper:hover .default_image {
.view_in_grafana_wrapper:hover .default_image {
display: none;
}

Expand Down
33 changes: 25 additions & 8 deletions portal/src/app/components/protocol-query/ProtocolQuery.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ export const ProtocolQuery: React.FC<ProtocolQueryProps> = (props: ProtocolQuery
const [experimentName, setExperimentName] = useState('');
const [algorithms, setAlgorithms] = useState<SelectOptionType>();
const [prevSelectedValues, setPrevSelectedValues] = useState<string[]>([]);
const [iterationsCount, setIterationsCount] = useState<SelectOptionType>();
const [description, setDescription] = useState('');

const [iterationsCount, setIterationsCount] = useState<AttSelectOption[]>([]);
const [showInputOption, setShowInputOption] = useState(false);
const [inputValue, setInputValue] = useState('');
const [iterationsMenuIsOpen, setIterationsMenuIsOpen] = useState(false);

const onSubmitHandler = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
onRunClick({
Expand All @@ -57,7 +61,8 @@ export const ProtocolQuery: React.FC<ProtocolQueryProps> = (props: ProtocolQuery
}, [algosBySection, algorithmOptions, prevSelectedValues]);

const onIterationsNumChanged: OnSelectChanged = useCallback((options: SelectOptionType): void => {
const selectedIterationNum: Options<AttSelectOption> = options as Options<AttSelectOption>;
const selectedIterationNum: AttSelectOption[] = options as AttSelectOption[];
setIterationsMenuIsOpen(true);
setIterationsCount(selectedIterationNum);
}, []);

Expand All @@ -77,7 +82,7 @@ export const ProtocolQuery: React.FC<ProtocolQueryProps> = (props: ProtocolQuery
<form className={styles.wrapper} data-testid='protocol-query-form' onSubmit={onSubmitHandler}>
<div className={styles.form_item}>
<label className={styles.form_item_label}>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.EXPERIMENT_NAME} <span className={styles.required}>*</span>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.EXPERIMENT_NAME} <span className={styles.required}>{PROTOCOL_QUERY_EN.FIELDS_LABEL.REQUIRED}</span>
</label>
<input
className={styles.input_form_item}
Expand All @@ -88,7 +93,7 @@ export const ProtocolQuery: React.FC<ProtocolQueryProps> = (props: ProtocolQuery
</div>
<div className={styles.form_item}>
<label className={styles.form_item_label}>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.ALGORITHM} <span className={styles.required}>*</span>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.ALGORITHM} <span className={styles.required}>{PROTOCOL_QUERY_EN.FIELDS_LABEL.REQUIRED}</span>
</label>
<AttSelect
className={styles.select_form_item}
Expand All @@ -105,19 +110,31 @@ export const ProtocolQuery: React.FC<ProtocolQueryProps> = (props: ProtocolQuery
</div>
<div className={styles.form_item}>
<label className={styles.form_item_label}>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.ITERATIONS_NUMBER} <span className={styles.required}>*</span>
{PROTOCOL_QUERY_EN.FIELDS_LABEL.ITERATIONS_NUMBER} <span className={styles.required}>{PROTOCOL_QUERY_EN.FIELDS_LABEL.REQUIRED}</span>
</label>
<AttSelect
className={styles.select_form_item}
options={iterationsOptions}
placeholder={PROTOCOL_QUERY_EN.FIELDS_LABEL.PLACEHOLDER}
value={iterationsCount as AttSelectOption}
placeholder=''
value={iterationsCount as AttSelectOption[]}
onChange={onIterationsNumChanged}
isMulti
hideSelectedOptions={false}
closeMenuOnSelect={false}
menuIsOpen={iterationsMenuIsOpen}
setMenuIsOpen={setIterationsMenuIsOpen}
required
customComponent={{ Option: IterationsSelectorCustomOption as React.FC }}
customComponent={{
Option: (props: any) =>
<IterationsSelectorCustomOption
{...props}
showInputOption={showInputOption}
setShowInputOption={setShowInputOption}
inputValue={inputValue}
setInputValue={setInputValue}
setMenuIsOpen={setIterationsMenuIsOpen}
/>
}}
/>
</div>
<div className={styles.form_item}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,7 @@ exports[`ProtocolQuery should render ProtocolQuery 1`] = `
<div
class="att_select__placeholder css-1jqq78o-placeholder"
id="react-select-3-placeholder"
>
+ Add new
</div>
/>
<div
class="att_select__input-container css-d9r47t-Component"
data-value=""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ describe('useGetIterations', () => {
});

const { result } = renderHook(() => useGetIterations());
expect(result.current.iterationsOptions.length).toEqual(mockData.iterations.length);
expect(result.current.iterationsOptions.length).toEqual(mockData.iterations.length + 2);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { AttSelectOption } from "../../../shared/components/att-select";
import { IHttp, useFetch } from "../../../shared/hooks/useFetch";
import { useEffect, useState } from "react";
import { APIS } from "../../../apis";
import { SELECTOR_CUSTOM_OPTION_EN } from "../../../shared/components/selector-custom-option/translate/en";

export interface IUseGetIterations {
iterationsOptions: AttSelectOption[];
Expand All @@ -24,7 +25,11 @@ export function useGetIterations(): IUseGetIterations {
useEffect(() => {
if (data) {
const iterationsOptions: AttSelectOption[] = data.iterations.map((iteration: number) => ({ label: iteration.toString(), value: iteration.toString() }));
setIterations(iterationsOptions);
setIterations([
...iterationsOptions,
{ label: SELECTOR_CUSTOM_OPTION_EN.ADD_NEW, value: SELECTOR_CUSTOM_OPTION_EN.ADD_NEW, metadata: { isInput: true }},
{ label: SELECTOR_CUSTOM_OPTION_EN.ADD_NEW_BUTTON, value: SELECTOR_CUSTOM_OPTION_EN.ADD_NEW_BUTTON, metadata: { isAddNewButton: true }}
]);
}
}, [data]);

Expand Down
2 changes: 1 addition & 1 deletion portal/src/app/components/protocol-query/translate/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const PROTOCOL_QUERY_EN = {
EXPORT: 'Export',
},
FIELDS_LABEL: {
PLACEHOLDER: '+ Add new',
REQUIRED: '*',
EXPERIMENT_NAME: 'Experiment name',
ITERATIONS_NUMBER: 'Number of iterations',
ALGORITHM: 'Algorithm(s)',
Expand Down
35 changes: 34 additions & 1 deletion portal/src/app/shared/components/att-select/AttSelect.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { render, RenderResult } from '@testing-library/react';
import { fireEvent, render, RenderResult } from '@testing-library/react';
import { AttSelect, AttSelectProps } from './AttSelect';
import { AttSelectOption } from './AttSelect.model';

Expand Down Expand Up @@ -38,6 +38,39 @@ describe('AttSelect', () => {
const spinner = container.querySelector('.att_select_spinner');
expect(spinner).toBeInTheDocument();
});

test('should open the menu when onMenuOpen is called', () => {
const props: AttSelectProps = {
options,
placeholder: '',
value: item1,
onChange: () => expect.anything(),
isProcessing: true,
};
const setMenuIsOpen = jest.fn();
const { getByRole } = render(<AttSelect {...props} setMenuIsOpen={setMenuIsOpen} />);

fireEvent.mouseDown(getByRole('combobox'));

expect(setMenuIsOpen).toHaveBeenCalledWith(true);
});

test('should close the menu when onMenuClose is called', () => {
const props: AttSelectProps = {
options,
placeholder: '',
value: item1,
onChange: () => expect.anything(),
isProcessing: true,
};
const setMenuIsOpen = jest.fn();
const { getByRole } = render(<AttSelect {...props} setMenuIsOpen={setMenuIsOpen} />);

fireEvent.mouseDown(getByRole('combobox'));
fireEvent.blur(getByRole('combobox'));

expect(setMenuIsOpen).toHaveBeenCalledWith(false);
});
});

const item1: AttSelectOption = {
Expand Down
5 changes: 5 additions & 0 deletions portal/src/app/shared/components/att-select/AttSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export interface AttSelectProps {
theme?: AttSelectTheme;
isMulti?: boolean;
closeMenuOnSelect?: boolean;
menuIsOpen?: boolean;
setMenuIsOpen?: (value: boolean) => void;
hideSelectedOptions?: boolean;
e2eId?: string;
id?: string;
Expand All @@ -58,6 +60,9 @@ const AttSelectPrivate: AttSelectPrivateType = (props: AttSelectProps, ref: Forw
placeholder={props.placeholder}
isMulti={props.isMulti}
isClearable={props.isClearable}
menuIsOpen={props.menuIsOpen}
onMenuOpen={() => props.setMenuIsOpen && props.setMenuIsOpen(true)}
onMenuClose={() => props.setMenuIsOpen && props.setMenuIsOpen(false)}
closeMenuOnSelect={props.closeMenuOnSelect}
hideSelectedOptions={props.hideSelectedOptions}
required={props.required}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
cursor: pointer;
}

.option_wrapper {
inline-size: 17px;
block-size: 17px;
margin-inline-end: 12px;
}

.iterations_input_option {
margin-inline-end: 10px;
cursor: pointer;
}

.input_option {
display: none;
}

.option_wrapper {
inline-size: 17px;
block-size: 17px;
margin-inline-end: 12px;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { render, fireEvent, RenderResult } from '@testing-library/react';
import { components } from 'react-select';
import { render, RenderResult } from '@testing-library/react';
import { AlgorithmsSelectorCustomOption, IterationsSelectorCustomOption, SelectorCustomOptionProps } from './SelectorCustomOption';
import { algorithmSections } from '../../../components/protocol-query/constants';

describe('SelectorCustomOption', () => {
const mockOption = { value: 'option1', label: 'Option 1' };
Expand All @@ -20,7 +18,7 @@ describe('SelectorCustomOption', () => {
cx: jest.fn(),
getStyles: jest.fn(),
getClassNames: jest.fn(),
getValue: jest.fn(),
getValue: jest.fn().mockReturnValue([{ label: 'Option 1', value: 'option1' }]),
hasValue: true,
isMulti: true,
isRtl: false,
Expand All @@ -29,6 +27,11 @@ describe('SelectorCustomOption', () => {
setValue: jest.fn(),
theme: expect.any(Object),
onOptionChanged: jest.fn(),
showInputOption: false,
setShowInputOption: jest.fn(),
inputValue: '1111',
setInputValue: jest.fn(),
setMenuIsOpen: jest.fn(),
};

it('should render AlgorithmsSelectorCustomOption correctly', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { OptionProps, components } from 'react-select';
import styles from './SelectorCustomOption.module.scss';
import cn from 'classnames';
import { GroupBase, OptionProps, components } from 'react-select';
import { AttSelectOption } from '../att-select';
import { algorithmSections } from '../../../components/protocol-query/constants';
import cn from 'classnames';
import CheckedSvg from '../../../../assets/images/checked.svg';
import UnCheckedSvg from '../../../../assets/images/unchecked.svg';
import { CustomInput } from './components';

const CheckedAriaLabel: string = 'checked';

export type SelectorCustomOptionProps = OptionProps<AttSelectOption, true> & {
export type SelectorCustomOptionProps = OptionProps<AttSelectOption<any>, true, GroupBase<AttSelectOption<any>>> & {
onOptionChanged: (option: AttSelectOption) => void;
showInputOption: boolean;
setShowInputOption: (show: boolean) => void;
inputValue: string;
setInputValue: (value: string) => void;
setMenuIsOpen: (isOpen: boolean) => void;
};

export const AlgorithmsSelectorCustomOption: React.FC<SelectorCustomOptionProps> = (props: SelectorCustomOptionProps) => {
Expand All @@ -18,11 +26,13 @@ export const AlgorithmsSelectorCustomOption: React.FC<SelectorCustomOptionProps>
<components.Option {...props}>
<div className={styles.option_wrapper}>
<input
id={props.label}
type="checkbox"
className={styles.input_option}
checked={props.isSelected}
onChange={() => props.onOptionChanged} />
<img className={optionStyle} src={props.isSelected ? CheckedSvg : UnCheckedSvg} alt="checked" />
onChange={() => props.onOptionChanged}
/>
<img className={optionStyle} src={props.isSelected ? CheckedSvg : UnCheckedSvg} alt={CheckedAriaLabel} />
</div>
<span className={optionStyle}>{props.label}</span>
</components.Option>
Expand All @@ -31,16 +41,23 @@ export const AlgorithmsSelectorCustomOption: React.FC<SelectorCustomOptionProps>

export const IterationsSelectorCustomOption: React.FC<SelectorCustomOptionProps> = (props: SelectorCustomOptionProps) => {
return (
<components.Option {...props}>
<div className={styles.option_wrapper}>
<input
type="checkbox"
className={cn(styles.iterations_input_option, styles.input_option)}
checked={props.isSelected}
onChange={() => props.onOptionChanged} />
<img src={props.isSelected ? CheckedSvg : UnCheckedSvg} alt="checked" />
</div>
<span>{props.label}</span>
</components.Option>
<>
{!props.data.metadata && (
<components.Option {...props}>
<div className={styles.option_wrapper}>
<input
id={props.label}
type="checkbox"
className={cn(styles.iterations_input_option, styles.input_option)}
checked={props.isSelected}
onChange={() => props.onOptionChanged}
/>
<img src={props.isSelected ? CheckedSvg : UnCheckedSvg} alt={CheckedAriaLabel} />
</div>
<span>{props.label}</span>
</components.Option>
)}
<CustomInput {...props} />
</>
);
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ exports[`SelectorCustomOption should render AlgorithmsSelectorCustomOption corre
>
<input
class="input_option"
id="Option 1"
type="checkbox"
/>
<img
Expand All @@ -36,6 +37,7 @@ exports[`SelectorCustomOption should render IterationsSelectorCustomOption corre
>
<input
class="iterations_input_option input_option"
id="Option 1"
type="checkbox"
/>
<img
Expand Down
Loading

0 comments on commit d85f89d

Please sign in to comment.