Skip to content

Commit

Permalink
multi-digit issue fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
vitPinchuk committed Nov 28, 2024
1 parent d251c6c commit 5a7c9bf
Show file tree
Hide file tree
Showing 5 changed files with 366 additions and 371 deletions.
98 changes: 7 additions & 91 deletions src/components/ElementOptionsEditor/ElementOptionsEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { FORM_ELEMENT_OPTION_DEFAULT, ICON_OPTIONS, SELECT_ELEMENT_OPTIONS, TEST
import { FormElementType } from '@/types';
import { reorder } from '@/utils';

import { ElementOptionEditor } from './components/ElementOptionEditor';
import { getStyles } from './ElementOptionsEditor.styles';

/**
Expand Down Expand Up @@ -143,97 +144,12 @@ export const ElementOptionsEditor: React.FC<Props> = ({ options = [], onChange,
</>
}
>
<>
<InlineFieldRow>
<InlineField label="Type" labelWidth={6}>
<Select
options={SELECT_ELEMENT_OPTIONS}
onChange={(event: SelectableValue) => {
const newValue =
event?.value === FormElementType.NUMBER
? Number(option.value) || 0
: String(option.value);

onChangeItem(
{
...option,
value: newValue,
id: newValue.toString(),
type: event?.value,
},
option
);
}}
width={12}
value={option.type}
aria-label={TEST_IDS.formElementsEditor.fieldOptionType}
/>
</InlineField>
<InlineField invalid={!option.value} label="Value" labelWidth={6} grow={true}>
<Input
placeholder={option.type === FormElementType.NUMBER ? 'number' : 'string'}
type={option.type === FormElementType.NUMBER ? 'number' : undefined}
onChange={(event: ChangeEvent<HTMLInputElement>) => {
onChangeItem(
{
...option,
value:
option.type === FormElementType.NUMBER
? Number(event.target.value)
: event.target.value,
},
option,
true
);
}}
onBlur={() => {
const newId = option.value?.toString() || FORM_ELEMENT_OPTION_DEFAULT.id;
onChangeItem(
{
...option,
id: newId,
},
option
);
}}
value={option.value}
data-testid={TEST_IDS.formElementsEditor.fieldOptionValue}
/>
</InlineField>
</InlineFieldRow>
<InlineFieldRow>
{iconEnabled && (
<InlineField label="Icon" labelWidth={6}>
<Select
onChange={(event) => {
onChangeItem({
...option,
icon: event?.value,
});
}}
options={ICON_OPTIONS}
isClearable={true}
value={option.icon}
width={12}
aria-label={TEST_IDS.formElementsEditor.fieldOptionIcon}
/>
</InlineField>
)}
<InlineField label="Label" labelWidth={6} grow={true} shrink={true}>
<Input
placeholder="label"
onChange={(event: ChangeEvent<HTMLInputElement>) => {
onChangeItem({
...option,
label: event.target.value,
});
}}
value={option.label}
data-testid={TEST_IDS.formElementsEditor.fieldOptionLabel}
/>
</InlineField>
</InlineFieldRow>
</>
<ElementOptionEditor
key={option.id}
option={option}
onChangeItem={onChangeItem}
iconEnabled={iconEnabled}
/>
</Collapse>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { toDataFrame } from '@grafana/data';
import { DragDropContext, DropResult } from '@hello-pangea/dnd';
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import React from 'react';

import { getFormElementsEditorSelectors } from '@/utils';

import { ElementOptionEditor } from './ElementOptionEditor';
import { FormElementType } from '@/types';
import { FORM_ELEMENT_DEFAULT } from '@/constants';

/**
* Mock timers
*/
jest.useFakeTimers();

/**
* Form Elements Editor
*/
describe('Element Option Editor', () => {
const onChange = jest.fn();

/**
* Form Elements Editor Selectors
*/
const selectors = getFormElementsEditorSelectors(screen);

/**
* Get Tested Component
* @param value
* @param context
* @param restProps
*/
const getComponent = ({ option = {}, ...restProps }: any) => {
return <ElementOptionEditor {...restProps} option={option} />;
};

it('Should update option type to number and convert value', async () => {
const originalOption = { label: 'label', type: FormElementType.STRING, value: '123', id: '123' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionType()).toBeInTheDocument();
expect(selectors.fieldOptionType()).toHaveValue(FormElementType.STRING);

/**
* Change option type
*/
await act(() => fireEvent.change(selectors.fieldOptionType(), { target: { value: FormElementType.NUMBER } }));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.NUMBER,
value: 123,
}),
expect.objectContaining({
type: FormElementType.STRING,
})
);
});

it('Should update option type to number and use default value if NaN', async () => {
const originalOption = { label: 'label', type: FormElementType.STRING, value: 'abc', id: 'abc' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionType()).toBeInTheDocument();
expect(selectors.fieldOptionType()).toHaveValue(FormElementType.STRING);

/**
* Change option type
*/
await act(() => fireEvent.change(selectors.fieldOptionType(), { target: { value: FormElementType.NUMBER } }));

// const updatedOption = openOption(elementSelectors, '0');

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.NUMBER,
}),
expect.objectContaining({
type: FormElementType.STRING,
})
);
});

it('Should update option type to string and convert value', async () => {
const originalOption = { label: 'label', type: FormElementType.NUMBER, value: 123, id: '123' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionType()).toBeInTheDocument();
expect(selectors.fieldOptionType()).toHaveValue(FormElementType.NUMBER);

/**
* Change option type
*/
await act(() => fireEvent.change(selectors.fieldOptionType(), { target: { value: FormElementType.STRING } }));
expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.STRING,
value: '123',
}),
expect.objectContaining({
type: FormElementType.NUMBER,
})
);
});

it('Should update option value for STRING option', async () => {
const originalOption = { label: 'label', type: FormElementType.STRING, value: '111', id: '111' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionValue()).toBeInTheDocument();
expect(selectors.fieldOptionValue()).toHaveValue('111');

await act(() => fireEvent.change(selectors.fieldOptionValue(), { target: { value: '123' } }));
await act(() => fireEvent.blur(selectors.fieldOptionValue()));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.STRING,
value: '123',
}),
expect.objectContaining({
type: FormElementType.STRING,
}),
true
);
});

it('Should update option value for NUMBER option', async () => {
const originalOption = { label: 'label', type: FormElementType.NUMBER, value: 0, id: '0' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionValue()).toBeInTheDocument();
expect(selectors.fieldOptionValue()).toHaveValue(0);

/**
* Change option number value
*/
await act(() => fireEvent.change(selectors.fieldOptionValue(), { target: { value: '123' } }));
await act(() => fireEvent.blur(selectors.fieldOptionValue()));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.NUMBER,
value: 123,
}),
expect.objectContaining({
type: FormElementType.NUMBER,
value: 0,
}),
true
);
});

it('Should use default option id if empty value', async () => {
const originalOption = { label: 'label', type: FormElementType.STRING, value: '111', id: '111' };

render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionValue()).toBeInTheDocument();
expect(selectors.fieldOptionValue()).toHaveValue('111');

await act(() => fireEvent.change(selectors.fieldOptionValue(), { target: { value: '' } }));
await act(() => fireEvent.blur(selectors.fieldOptionValue()));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.STRING,
value: '',
}),
expect.objectContaining({
type: FormElementType.STRING,
value: '111',
}),
true
);
});

it('Should update option label', async () => {
const originalOption = { label: 'label', type: FormElementType.NUMBER, value: 0, id: '0' };
render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionLabel()).toBeInTheDocument();

/**
* Change option label
*/
await act(() => fireEvent.change(selectors.fieldOptionLabel(), { target: { value: 'new label' } }));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.NUMBER,
label: 'new label',
})
);
});

it('Should update option icon', async () => {
const originalOption = { label: 'label', type: FormElementType.NUMBER, value: 0, icon: undefined, id: '0' };
render(getComponent({ option: originalOption, onChangeItem: onChange }));

expect(selectors.fieldOptionIcon()).toBeInTheDocument();

/**
* Change option icon
*/
await act(() => fireEvent.change(selectors.fieldOptionIcon(), { target: { value: 'check' } }));

expect(onChange).toHaveBeenCalled();
expect(onChange).toHaveBeenCalledWith(
expect.objectContaining({
type: FormElementType.NUMBER,
label: 'label',
icon: 'check',
})
);
});
});
Loading

0 comments on commit 5a7c9bf

Please sign in to comment.