Skip to content

Commit

Permalink
Add highlight for freeSolo products (#103)
Browse files Browse the repository at this point in the history
* Show new icon for freeSolo products

* Show add/edit icons for freeSolo product

* Fixed saving freeSolo product form changes on submit

* Clear new product form after product input changed
  • Loading branch information
pkirilin authored May 12, 2024
1 parent a868161 commit ac813ca
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 3 deletions.
28 changes: 26 additions & 2 deletions src/frontend/src/entities/product/ui/ProductAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import AddIcon from '@mui/icons-material/Add';
import EditIcon from '@mui/icons-material/Edit';
import FiberNewIcon from '@mui/icons-material/FiberNew';
import {
CircularProgress,
type FilterOptionsState,
Autocomplete,
createFilterOptions,
type SvgIconOwnProps,
Box,
} from '@mui/material';
import TextField from '@mui/material/TextField';
import { type FC, type SyntheticEvent } from 'react';
import { useState, type FC, type SyntheticEvent } from 'react';
import { type FormValues, type AutocompleteOption, EMPTY_FORM_VALUES } from '../model';

const filter = createFilterOptions<AutocompleteOption>();
Expand Down Expand Up @@ -43,6 +48,9 @@ export const ProductAutocomplete: FC<ProductAutocompleteProps> = ({
error,
autoFocus,
}) => {
const [newProductIconColor, setNewProductIconColor] =
useState<SvgIconOwnProps['color']>('action');

const filterOptions = (
options: AutocompleteOption[],
state: FilterOptionsState<AutocompleteOption>,
Expand Down Expand Up @@ -138,7 +146,16 @@ export const ProductAutocomplete: FC<ProductAutocompleteProps> = ({
freeSolo
getOptionLabel={getOptionLabel}
filterOptions={filterOptions}
renderOption={(props, option) => <li {...props}>{option.name}</li>}
renderOption={(props, option) => (
<Box component="li" {...props} display="flex" alignItems="center" gap={1}>
{option.freeSolo && (
<Box display="flex">
{option.editing ? <EditIcon fontSize="small" /> : <AddIcon fontSize="small" />}
</Box>
)}
<Box display="flex">{option.name}</Box>
</Box>
)}
renderInput={inputParams => (
<TextField
{...inputParams}
Expand All @@ -147,8 +164,15 @@ export const ProductAutocomplete: FC<ProductAutocompleteProps> = ({
error={error}
helperText={helperText}
autoFocus={autoFocus}
onFocus={() => {
setNewProductIconColor('primary');
}}
onBlur={() => {
setNewProductIconColor('action');
}}
InputProps={{
...inputParams.InputProps,
startAdornment: value?.freeSolo ? <FiberNewIcon color={newProductIconColor} /> : null,
endAdornment: loading ? (
<CircularProgress color="inherit" size={20} />
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,10 @@ export const thenProductCategoryIsEmpty = async (): Promise<void> => {
expect(screen.getByRole('combobox', { name: /category/i })).not.toHaveValue();
};

export const thenProductCategoryHasValue = async (value: string): Promise<void> => {
expect(screen.getByRole('combobox', { name: /category/i })).toHaveValue(value);
};

export const thenSubmitNoteButtonIsDisabled = async (): Promise<void> => {
expect(screen.getByRole('button', { name: /submit/i })).toBeDisabled();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import {
thenProductNameIsInvalid,
thenProductIsInvalid,
thenAddProductButtonIsDisabled,
whenEditedNotExistingProductOption,
thenProductCategoryHasValue,
} from './NoteInputDialog.fixture';

describe('when opened for existing note', () => {
Expand Down Expand Up @@ -314,3 +316,52 @@ test('I cannot add note with new product which name is invalid', async () => {
await thenProductNameIsInvalid();
await thenAddProductButtonIsDisabled();
});

test(`I can continue editing new product I've added before`, async () => {
const user = userEvent.setup();

render(givenNoteInputDialog().withQuantity(100).withCategoriesForSelect('Vegetables').please());

await whenDialogOpened(user);
await whenAddedNotExistingProductOption(user, 'Potato');
await whenProductCaloriesCostChanged(user, 120);
await whenProductDefaultQuantityChanged(user, 80);
await whenProductCategorySelected(user, /vegetables/i);
await whenProductAdded(user);
await thenNoteFormShouldBeVisible();

await whenEditedNotExistingProductOption(user, 'Potato');
await thenProductNameHasValue('Potato');
await thenProductCaloriesCostHasValue(120);
await thenProductDefaultQuantityHasValue(80);
await thenProductCategoryHasValue('Vegetables');
});

test('New product form is cleared after I change product to existing one', async () => {
const user = userEvent.setup();

render(
givenNoteInputDialog()
.withQuantity(100)
.withProductForSelect({ name: 'Cucumber' })
.withCategoriesForSelect('Vegetables')
.please(),
);

await whenDialogOpened(user);
await whenAddedNotExistingProductOption(user, 'Potato');
await whenProductCaloriesCostChanged(user, 120);
await whenProductDefaultQuantityChanged(user, 80);
await whenProductCategorySelected(user, /vegetables/i);
await whenProductAdded(user);
await thenNoteFormShouldBeVisible();

await whenProductSelected(user, /cucumber/i);
await whenProductCleared(user);
await whenAddedNotExistingProductOption(user, 'Carrot');
await thenProductFormShouldBeVisible();
await thenProductNameHasValue('Carrot');
await thenProductCaloriesCostHasValue(100);
await thenProductDefaultQuantityHasValue(100);
await thenProductCategoryIsEmpty();
});
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const NoteInputDialog: FC<Props> = ({
},
onProductChange: value => {
setProductAutocompleteValue(value);
clearProductFormValues();

if (value?.freeSolo === true) {
setCurrentInputDialogType('product');
Expand All @@ -91,7 +92,9 @@ export const NoteInputDialog: FC<Props> = ({
onClose: () => {
setCurrentInputDialogType('note');
},
onSubmit: ({ name, caloriesCost, defaultQuantity, category }) => {
onSubmit: formValues => {
const { name, caloriesCost, defaultQuantity, category } = formValues;

setProductAutocompleteValue({
freeSolo: true,
editing: true,
Expand All @@ -100,6 +103,8 @@ export const NoteInputDialog: FC<Props> = ({
defaultQuantity,
category,
});

setProductFormValues(formValues);
setCurrentInputDialogType('note');
},
});
Expand Down

0 comments on commit ac813ca

Please sign in to comment.