Smart filters improvements (#1814)
* Add Filters add button and remove the Add Filter Icon * Generalize DeleteFilterModal and write tests suites + add custom addModal hook * add react-testing-hook-lib + add tests to useModal hook * Add parameter to ConfirmationModal + remove delete Modal add generic modal in add filter page * implementing the modal hook on add Filter * Finalize the Smart Filters functionality * Styling code modifications * Styling code modifications * Filters styling code modifcations * minor modifications in the tests suites * minor tests suites description modifications * minor tests suites code modifications * Adding unNamed Filter selection option + tests suites * Fix typo Signed-off-by: Roman Zabaluev <rzabaluev@provectus.com> * Adding tests Wuites to AddEditFilterContainer and to addFilter * Add Filters add button and remove the Add Filter Icon * Generalize DeleteFilterModal and write tests suites + add custom addModal hook * add react-testing-hook-lib + add tests to useModal hook * Add parameter to ConfirmationModal + remove delete Modal add generic modal in add filter page * implementing the modal hook on add Filter * Finalize the Smart Filters functionality * Styling code modifications * Styling code modifications * Filters styling code modifcations * minor modifications in the tests suites * minor tests suites description modifications * minor tests suites code modifications * Adding unNamed Filter selection option + tests suites * Fix typo Signed-off-by: Roman Zabaluev <rzabaluev@provectus.com> * Adding tests Wuites to AddEditFilterContainer and to addFilter * q parameter modifications in the SmartFilters * Add popup close functionality after applying the filters Co-authored-by: Roman Zabaluev <rzabaluev@provectus.com>
This commit is contained in:
parent
76c5fae4dd
commit
6eb6bb1d7e
20 changed files with 830 additions and 475 deletions
22
kafka-ui-react-app/package-lock.json
generated
22
kafka-ui-react-app/package-lock.json
generated
|
@ -4869,6 +4869,19 @@
|
|||
"@testing-library/dom": "^8.0.0"
|
||||
}
|
||||
},
|
||||
"@testing-library/react-hooks": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-7.0.2.tgz",
|
||||
"integrity": "sha512-dYxpz8u9m4q1TuzfcUApqi8iFfR6R0FaMbr2hjZJy1uC8z+bO/K4v8Gs9eogGKYQop7QsrBTFkv/BCF7MzD2Cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@types/react": ">=16.9.0",
|
||||
"@types/react-dom": ">=16.9.0",
|
||||
"@types/react-test-renderer": ">=16.9.0",
|
||||
"react-error-boundary": "^3.1.0"
|
||||
}
|
||||
},
|
||||
"@testing-library/user-event": {
|
||||
"version": "13.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
|
||||
|
@ -17194,6 +17207,15 @@
|
|||
"scheduler": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"react-error-boundary": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
}
|
||||
},
|
||||
"react-error-overlay": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.10.tgz",
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
"@openapitools/openapi-generator-cli": "^2.4.15",
|
||||
"@testing-library/dom": "^8.11.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/classnames": "^2.2.11",
|
||||
"@types/enzyme": "^3.10.9",
|
||||
|
|
|
@ -7,41 +7,38 @@ import { FormProvider, Controller, useForm } from 'react-hook-form';
|
|||
import { ErrorMessage } from '@hookform/error-message';
|
||||
import { Button } from 'components/common/Button/Button';
|
||||
import { FormError } from 'components/common/Input/Input.styled';
|
||||
import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||
import { AddMessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/AddFilter';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import yup from 'lib/yupExtended';
|
||||
|
||||
const validationSchema = yup.object().shape({
|
||||
name: yup.string().required(),
|
||||
saveFilter: yup.boolean(),
|
||||
code: yup.string().required(),
|
||||
name: yup.string().when('saveFilter', {
|
||||
is: (value: boolean | undefined) => typeof value === 'undefined' || value,
|
||||
then: (schema) => schema.required(),
|
||||
otherwise: (schema) => schema.notRequired(),
|
||||
}),
|
||||
});
|
||||
|
||||
export interface AddEditFilterContainerProps {
|
||||
title: string;
|
||||
cancelBtnHandler: () => void;
|
||||
submitBtnText: string;
|
||||
inputDisplayNameDefaultValue?: string;
|
||||
inputCodeDefaultValue?: string;
|
||||
toggleSaveFilterValue?: boolean;
|
||||
toggleSaveFilterSetter?: () => void;
|
||||
createNewFilterText?: string;
|
||||
submitCallback?: (values: MessageFilters) => void;
|
||||
submitCallbackWithReset?: boolean;
|
||||
isAdd?: boolean;
|
||||
submitCallback?: (values: AddMessageFilters) => void;
|
||||
}
|
||||
|
||||
const AddEditFilterContainer: React.FC<AddEditFilterContainerProps> = ({
|
||||
title,
|
||||
cancelBtnHandler,
|
||||
submitBtnText,
|
||||
inputDisplayNameDefaultValue = '',
|
||||
inputCodeDefaultValue = '',
|
||||
toggleSaveFilterValue,
|
||||
toggleSaveFilterSetter,
|
||||
createNewFilterText,
|
||||
submitCallback,
|
||||
submitCallbackWithReset,
|
||||
isAdd,
|
||||
}) => {
|
||||
const methods = useForm<MessageFilters>({
|
||||
const methods = useForm<AddMessageFilters>({
|
||||
mode: 'onChange',
|
||||
resolver: yupResolver(validationSchema),
|
||||
});
|
||||
|
@ -53,88 +50,77 @@ const AddEditFilterContainer: React.FC<AddEditFilterContainerProps> = ({
|
|||
} = methods;
|
||||
|
||||
const onSubmit = React.useCallback(
|
||||
(values: MessageFilters) => {
|
||||
(values: AddMessageFilters) => {
|
||||
submitCallback?.(values);
|
||||
if (submitCallbackWithReset) {
|
||||
reset({ name: '', code: '' });
|
||||
}
|
||||
reset({ name: '', code: '', saveFilter: false });
|
||||
},
|
||||
[reset, submitCallback, submitCallbackWithReset]
|
||||
[isAdd, reset, submitCallback]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<S.FilterTitle>{title}</S.FilterTitle>
|
||||
<FormProvider {...methods}>
|
||||
{createNewFilterText && (
|
||||
<S.CreatedFilter>{createNewFilterText}</S.CreatedFilter>
|
||||
<FormProvider {...methods}>
|
||||
<form onSubmit={handleSubmit(onSubmit)} aria-label="Filters submit Form">
|
||||
<div>
|
||||
<InputLabel>Filter code</InputLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name="code"
|
||||
defaultValue={inputCodeDefaultValue}
|
||||
render={({ field: { onChange, ref } }) => (
|
||||
<Textarea ref={ref} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormError>
|
||||
<ErrorMessage errors={errors} name="code" />
|
||||
</FormError>
|
||||
</div>
|
||||
{isAdd && (
|
||||
<S.CheckboxWrapper>
|
||||
<input
|
||||
{...methods.register('saveFilter')}
|
||||
name="saveFilter"
|
||||
type="checkbox"
|
||||
/>
|
||||
<InputLabel>Save this filter</InputLabel>
|
||||
</S.CheckboxWrapper>
|
||||
)}
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
aria-label="Filters submit Form"
|
||||
>
|
||||
<div>
|
||||
<InputLabel>Display name</InputLabel>
|
||||
<Input
|
||||
inputSize="M"
|
||||
placeholder="Enter Name"
|
||||
autoComplete="off"
|
||||
name="name"
|
||||
defaultValue={inputDisplayNameDefaultValue}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormError>
|
||||
<ErrorMessage errors={errors} name="name" />
|
||||
</FormError>
|
||||
</div>
|
||||
<div>
|
||||
<InputLabel>Filter code</InputLabel>
|
||||
<Controller
|
||||
control={control}
|
||||
name="code"
|
||||
defaultValue={inputCodeDefaultValue}
|
||||
render={({ field: { onChange, ref } }) => (
|
||||
<Textarea ref={ref} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormError>
|
||||
<ErrorMessage errors={errors} name="code" />
|
||||
</FormError>
|
||||
</div>
|
||||
{!!toggleSaveFilterSetter && (
|
||||
<S.CheckboxWrapper>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={toggleSaveFilterValue}
|
||||
onChange={toggleSaveFilterSetter}
|
||||
/>
|
||||
<InputLabel>Save this filter</InputLabel>
|
||||
</S.CheckboxWrapper>
|
||||
)}
|
||||
<S.FilterButtonWrapper>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
onClick={cancelBtnHandler}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="primary"
|
||||
type="submit"
|
||||
disabled={!isValid || isSubmitting || !isDirty}
|
||||
>
|
||||
{submitBtnText}
|
||||
</Button>
|
||||
</S.FilterButtonWrapper>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</>
|
||||
<div>
|
||||
<InputLabel>Display name</InputLabel>
|
||||
<Input
|
||||
inputSize="M"
|
||||
placeholder="Enter Name"
|
||||
autoComplete="off"
|
||||
name="name"
|
||||
defaultValue={inputDisplayNameDefaultValue}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<FormError>
|
||||
<ErrorMessage errors={errors} name="name" />
|
||||
</FormError>
|
||||
</div>
|
||||
<S.FilterButtonWrapper>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
onClick={cancelBtnHandler}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="primary"
|
||||
type="submit"
|
||||
disabled={!isValid || isSubmitting || !isDirty}
|
||||
>
|
||||
{submitBtnText}
|
||||
</Button>
|
||||
</S.FilterButtonWrapper>
|
||||
</form>
|
||||
</FormProvider>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import * as S from 'components/Topics/Topic/Details/Messages/Filters/Filters.styled';
|
||||
import { Button } from 'components/common/Button/Button';
|
||||
import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||
import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
|
||||
import SavedFilters from 'components/Topics/Topic/Details/Messages/Filters/SavedFilters';
|
||||
import SavedIcon from 'components/common/Icons/SavedIcon';
|
||||
|
||||
import AddEditFilterContainer from './AddEditFilterContainer';
|
||||
|
||||
|
@ -16,6 +17,10 @@ export interface FilterModalProps {
|
|||
editFilter(value: FilterEdit): void;
|
||||
}
|
||||
|
||||
export interface AddMessageFilters extends MessageFilters {
|
||||
saveFilter: boolean;
|
||||
}
|
||||
|
||||
const AddFilter: React.FC<FilterModalProps> = ({
|
||||
toggleIsOpen,
|
||||
filters,
|
||||
|
@ -25,139 +30,54 @@ const AddFilter: React.FC<FilterModalProps> = ({
|
|||
toggleEditModal,
|
||||
editFilter,
|
||||
}) => {
|
||||
const [addNewFilter, setAddNewFilter] = React.useState(false);
|
||||
const [toggleSaveFilter, setToggleSaveFilter] = React.useState(false);
|
||||
const [selectedFilter, setSelectedFilter] = React.useState(-1);
|
||||
const [toggleDeletionModal, setToggleDeletionModal] =
|
||||
const [savedFilterState, setSavedFilterState] =
|
||||
React.useState<boolean>(false);
|
||||
const [deleteIndex, setDeleteIndex] = React.useState<number>(-1);
|
||||
|
||||
const deleteFilterHandler = (index: number) => {
|
||||
setToggleDeletionModal(!toggleDeletionModal);
|
||||
setDeleteIndex(index);
|
||||
};
|
||||
const activeFilter = () => {
|
||||
if (selectedFilter > -1) {
|
||||
activeFilterHandler(filters[selectedFilter], selectedFilter);
|
||||
toggleIsOpen();
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = React.useCallback(
|
||||
async (values: MessageFilters) => {
|
||||
if (!toggleSaveFilter) {
|
||||
activeFilterHandler(values, -1);
|
||||
async (values: AddMessageFilters) => {
|
||||
const data = { ...values };
|
||||
if (data.saveFilter) {
|
||||
addFilter(data);
|
||||
} else {
|
||||
addFilter(values);
|
||||
// other case is not applying the filter
|
||||
data.name = data.name ? data.name : 'Unsaved filter';
|
||||
activeFilterHandler(data, -1);
|
||||
toggleIsOpen();
|
||||
}
|
||||
setAddNewFilter(!addNewFilter);
|
||||
},
|
||||
[addNewFilter, toggleSaveFilter, activeFilterHandler, addFilter]
|
||||
[activeFilterHandler, addFilter, toggleIsOpen]
|
||||
);
|
||||
return !addNewFilter ? (
|
||||
return (
|
||||
<>
|
||||
<S.FilterTitle>Add filter</S.FilterTitle>
|
||||
<S.NewFilterIcon onClick={() => setAddNewFilter(!addNewFilter)}>
|
||||
<i className="fas fa-plus fa-sm" /> New filter
|
||||
</S.NewFilterIcon>
|
||||
<S.CreatedFilter>Created filters</S.CreatedFilter>
|
||||
{toggleDeletionModal && (
|
||||
<S.ConfirmDeletionModal>
|
||||
<S.ConfirmDeletionModalHeader>
|
||||
<S.ConfirmDeletionTitle>Confirm deletion</S.ConfirmDeletionTitle>
|
||||
<S.CloseDeletionModalIcon
|
||||
data-testid="closeDeletionModalIcon"
|
||||
onClick={() => setToggleDeletionModal(!toggleDeletionModal)}
|
||||
>
|
||||
<i className="fas fa-times-circle" />
|
||||
</S.CloseDeletionModalIcon>
|
||||
</S.ConfirmDeletionModalHeader>
|
||||
<S.ConfirmDeletionText>
|
||||
Are you sure want to remove {filters[deleteIndex].name}?
|
||||
</S.ConfirmDeletionText>
|
||||
<S.FilterButtonWrapper>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
onClick={() => setToggleDeletionModal(!toggleDeletionModal)}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
deleteFilter(deleteIndex);
|
||||
setToggleDeletionModal(!toggleDeletionModal);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</S.FilterButtonWrapper>
|
||||
</S.ConfirmDeletionModal>
|
||||
)}
|
||||
<S.SavedFiltersContainer>
|
||||
{filters.length === 0 && <p>no saved filter(s)</p>}
|
||||
{filters.map((filter, index) => (
|
||||
<S.SavedFilter
|
||||
key={Symbol(filter.name).toString()}
|
||||
selected={selectedFilter === index}
|
||||
onClick={() => setSelectedFilter(index)}
|
||||
{savedFilterState ? (
|
||||
<SavedFilters
|
||||
deleteFilter={deleteFilter}
|
||||
activeFilterHandler={activeFilterHandler}
|
||||
closeModal={toggleIsOpen}
|
||||
onGoBack={() => setSavedFilterState(false)}
|
||||
filters={filters}
|
||||
onEdit={(index: number, filter: MessageFilters) => {
|
||||
toggleEditModal();
|
||||
editFilter({ index, filter });
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<S.SavedFiltersTextContainer
|
||||
onClick={() => setSavedFilterState(true)}
|
||||
>
|
||||
<S.SavedFilterName>{filter.name}</S.SavedFilterName>
|
||||
<S.FilterOptions>
|
||||
<S.FilterEdit
|
||||
onClick={() => {
|
||||
toggleEditModal();
|
||||
editFilter({ index, filter });
|
||||
}}
|
||||
>
|
||||
Edit
|
||||
</S.FilterEdit>
|
||||
<S.DeleteSavedFilter
|
||||
data-testid="deleteIcon"
|
||||
onClick={() => deleteFilterHandler(index)}
|
||||
>
|
||||
<i className="fas fa-times" />
|
||||
</S.DeleteSavedFilter>
|
||||
</S.FilterOptions>
|
||||
</S.SavedFilter>
|
||||
))}
|
||||
</S.SavedFiltersContainer>
|
||||
<S.FilterButtonWrapper>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
onClick={toggleIsOpen}
|
||||
disabled={toggleDeletionModal}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
onClick={activeFilter}
|
||||
disabled={toggleDeletionModal}
|
||||
>
|
||||
Select filter
|
||||
</Button>
|
||||
</S.FilterButtonWrapper>
|
||||
<SavedIcon /> <S.SavedFiltersText>Saved Filters</S.SavedFiltersText>
|
||||
</S.SavedFiltersTextContainer>
|
||||
<AddEditFilterContainer
|
||||
cancelBtnHandler={toggleIsOpen}
|
||||
submitBtnText="Add filter"
|
||||
submitCallback={onSubmit}
|
||||
isAdd
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<AddEditFilterContainer
|
||||
title="Add filter"
|
||||
cancelBtnHandler={() => setAddNewFilter(!addNewFilter)}
|
||||
submitBtnText="Add filter"
|
||||
submitCallback={onSubmit}
|
||||
submitCallbackWithReset
|
||||
createNewFilterText="Create a new filter"
|
||||
toggleSaveFilterValue={toggleSaveFilter}
|
||||
toggleSaveFilterSetter={() => setToggleSaveFilter(!toggleSaveFilter)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters
|
|||
import { FilterEdit } from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
|
||||
|
||||
import AddEditFilterContainer from './AddEditFilterContainer';
|
||||
import * as S from './Filters.styled';
|
||||
|
||||
export interface EditFilterProps {
|
||||
editFilter: FilterEdit;
|
||||
|
@ -23,14 +24,16 @@ const EditFilter: React.FC<EditFilterProps> = ({
|
|||
[editSavedFilter, editFilter.index, toggleEditModal]
|
||||
);
|
||||
return (
|
||||
<AddEditFilterContainer
|
||||
title="Edit saved filter"
|
||||
cancelBtnHandler={() => toggleEditModal()}
|
||||
submitBtnText="Save"
|
||||
inputDisplayNameDefaultValue={editFilter.filter.name}
|
||||
inputCodeDefaultValue={editFilter.filter.code}
|
||||
submitCallback={onSubmit}
|
||||
/>
|
||||
<>
|
||||
<S.FilterTitle>Edit saved filter</S.FilterTitle>
|
||||
<AddEditFilterContainer
|
||||
cancelBtnHandler={() => toggleEditModal()}
|
||||
submitBtnText="Save"
|
||||
inputDisplayNameDefaultValue={editFilter.filter.name}
|
||||
inputCodeDefaultValue={editFilter.filter.code}
|
||||
submitCallback={onSubmit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import styled from 'styled-components';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
interface SavedFilterProps {
|
||||
selected: boolean;
|
||||
|
@ -99,7 +99,6 @@ export const ClearAll = styled.span`
|
|||
color: ${({ theme }) => theme.metrics.filters.color.normal};
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
font-family: Inter;
|
||||
`;
|
||||
|
||||
export const MessageFilterModal = styled.div`
|
||||
|
@ -117,16 +116,19 @@ export const MessageFilterModal = styled.div`
|
|||
|
||||
export const FilterTitle = styled.h3`
|
||||
line-height: 32px;
|
||||
font-family: Inter;
|
||||
font-size: 20px;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
export const NewFilterIcon = styled.div`
|
||||
color: ${({ theme }) => theme.icons.newFilterIcon};
|
||||
padding-right: 6px;
|
||||
height: 12px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: '';
|
||||
width: calc(100% + 32px);
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
left: -16px;
|
||||
display: inline-block;
|
||||
background-color: #f1f2f3;
|
||||
}
|
||||
`;
|
||||
|
||||
export const CreatedFilter = styled.p`
|
||||
|
@ -154,15 +156,20 @@ export const SavedFilterName = styled.div`
|
|||
export const FilterButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 25px;
|
||||
margin-top: 10px;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
export const AddFiltersIcon = styled.div`
|
||||
color: ${({ theme }) => theme.metrics.filters.color.icon};
|
||||
padding-right: 6px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
padding-top: 16px;
|
||||
position: relative;
|
||||
&:before {
|
||||
content: '';
|
||||
width: calc(100% + 32px);
|
||||
height: 1px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -16px;
|
||||
display: inline-block;
|
||||
background-color: #f1f2f3;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ActiveSmartFilterWrapper = styled.div`
|
||||
|
@ -200,6 +207,7 @@ export const SavedFilter = styled.div.attrs({
|
|||
height: 32px;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
border-top: 1px solid #f1f2f3;
|
||||
&:hover ${FilterOptions} {
|
||||
display: flex;
|
||||
}
|
||||
|
@ -240,37 +248,6 @@ export const DeleteSavedFilterIcon = styled.div`
|
|||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const ConfirmDeletionModal = styled.div.attrs({ role: 'deletionModal' })`
|
||||
height: auto;
|
||||
width: 348px;
|
||||
border-radius: 8px;
|
||||
background: ${({ theme }) => theme.modal.backgroundColor};
|
||||
position: absolute;
|
||||
left: 20%;
|
||||
border: 1px solid ${({ theme }) => theme.breadcrumb};
|
||||
box-shadow: ${({ theme }) => theme.modal.shadow};
|
||||
padding: 16px;
|
||||
z-index: 2;
|
||||
`;
|
||||
|
||||
export const ConfirmDeletionModalHeader = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
height: 48px;
|
||||
`;
|
||||
|
||||
export const ConfirmDeletionTitle = styled.h3`
|
||||
font-size: 20px;
|
||||
line-height: 32px;
|
||||
`;
|
||||
|
||||
export const CloseDeletionModalIcon = styled.div`
|
||||
color: ${({ theme }) => theme.icons.closeModalIcon};
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const ConfirmDeletionText = styled.h3`
|
||||
color: ${({ theme }) => theme.modal.deletionTextColor};
|
||||
font-size: 14px;
|
||||
|
@ -310,3 +287,28 @@ export const MessageLoadingSpinner = styled.div<MessageLoadingSpinnerProps>`
|
|||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const SavedFiltersTextContainer = styled.div.attrs({
|
||||
role: 'savedFilterText',
|
||||
})`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
const textStyle = css`
|
||||
font-size: 14px;
|
||||
color: ${({ theme }) => theme.editFilterText.color};
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
export const SavedFiltersText = styled.div`
|
||||
${textStyle};
|
||||
margin-left: 7px;
|
||||
`;
|
||||
|
||||
export const BackToCustomText = styled.div`
|
||||
${textStyle};
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
|
|
@ -27,6 +27,7 @@ import FilterModal, {
|
|||
} from 'components/Topics/Topic/Details/Messages/Filters/FilterModal';
|
||||
import { SeekDirectionOptions } from 'components/Topics/Topic/Details/Messages/Messages';
|
||||
import TopicMessagesContext from 'components/contexts/TopicMessagesContext';
|
||||
import useModal from 'lib/hooks/useModal';
|
||||
|
||||
import * as S from './Filters.styled';
|
||||
import {
|
||||
|
@ -89,8 +90,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
const { searchParams, seekDirection, isLive, changeSeekDirection } =
|
||||
useContext(TopicMessagesContext);
|
||||
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const toggleIsOpen = () => setIsOpen(!isOpen);
|
||||
const { isOpen, toggle } = useModal();
|
||||
|
||||
const source = React.useRef<EventSource | null>(null);
|
||||
|
||||
|
@ -157,7 +157,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
return {
|
||||
q:
|
||||
queryType === MessageFilterType.GROOVY_SCRIPT
|
||||
? `valueAsText.contains('${activeFilter.code}')`
|
||||
? activeFilter.code
|
||||
: query,
|
||||
filterQueryType: queryType,
|
||||
attempt,
|
||||
|
@ -445,9 +445,10 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
/>
|
||||
</div>
|
||||
<S.ActiveSmartFilterWrapper>
|
||||
<S.AddFiltersIcon data-testid="addFilterIcon" onClick={toggleIsOpen}>
|
||||
<Button buttonType="primary" buttonSize="M" onClick={toggle}>
|
||||
<i className="fas fa-plus fa-sm" />
|
||||
</S.AddFiltersIcon>
|
||||
Add Filters
|
||||
</Button>
|
||||
{activeFilter.name && (
|
||||
<S.ActiveSmartFilter data-testid="activeSmartFilter">
|
||||
{activeFilter.name}
|
||||
|
@ -462,7 +463,7 @@ const Filters: React.FC<FiltersProps> = ({
|
|||
</S.ActiveSmartFilterWrapper>
|
||||
{isOpen && (
|
||||
<FilterModal
|
||||
toggleIsOpen={toggleIsOpen}
|
||||
toggleIsOpen={toggle}
|
||||
filters={savedFilters}
|
||||
addFilter={addFilter}
|
||||
deleteFilter={deleteFilter}
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
import React, { FC } from 'react';
|
||||
import { Button } from 'components/common/Button/Button';
|
||||
import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
|
||||
import useModal from 'lib/hooks/useModal';
|
||||
|
||||
import * as S from './Filters.styled';
|
||||
import { MessageFilters } from './Filters';
|
||||
|
||||
export interface Props {
|
||||
filters: MessageFilters[];
|
||||
onEdit(index: number, filter: MessageFilters): void;
|
||||
deleteFilter(index: number): void;
|
||||
activeFilterHandler(activeFilter: MessageFilters, index: number): void;
|
||||
closeModal(): void;
|
||||
onGoBack(): void;
|
||||
}
|
||||
|
||||
const SavedFilters: FC<Props> = ({
|
||||
filters,
|
||||
onEdit,
|
||||
deleteFilter,
|
||||
activeFilterHandler,
|
||||
closeModal,
|
||||
onGoBack,
|
||||
}) => {
|
||||
const { isOpen, setOpen, setClose } = useModal();
|
||||
const [deleteIndex, setDeleteIndex] = React.useState<number>(-1);
|
||||
const [selectedFilter, setSelectedFilter] = React.useState(-1);
|
||||
|
||||
const activeFilter = () => {
|
||||
if (selectedFilter > -1) {
|
||||
activeFilterHandler(filters[selectedFilter], selectedFilter);
|
||||
}
|
||||
closeModal();
|
||||
};
|
||||
|
||||
const deleteFilterHandler = (index: number) => {
|
||||
setOpen();
|
||||
setDeleteIndex(index);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ConfirmationModal
|
||||
isOpen={isOpen}
|
||||
title="Confirm deletion"
|
||||
onConfirm={() => {
|
||||
deleteFilter(deleteIndex);
|
||||
setClose();
|
||||
}}
|
||||
onCancel={setClose}
|
||||
submitBtnText="Delete"
|
||||
>
|
||||
<S.ConfirmDeletionText>
|
||||
Are you sure want to remove {filters[deleteIndex]?.name}?
|
||||
</S.ConfirmDeletionText>
|
||||
</ConfirmationModal>
|
||||
<S.BackToCustomText onClick={onGoBack}>
|
||||
Back To custom filters
|
||||
</S.BackToCustomText>
|
||||
<S.SavedFiltersContainer>
|
||||
<S.CreatedFilter>Saved filters</S.CreatedFilter>
|
||||
{filters.length === 0 && <p>No saved filter(s)</p>}
|
||||
{filters.map((filter, index) => (
|
||||
<S.SavedFilter
|
||||
key={Symbol(filter.name).toString()}
|
||||
selected={selectedFilter === index}
|
||||
onClick={() => setSelectedFilter(index)}
|
||||
>
|
||||
<S.SavedFilterName>{filter.name}</S.SavedFilterName>
|
||||
<S.FilterOptions>
|
||||
<S.FilterEdit onClick={() => onEdit(index, filter)}>
|
||||
Edit
|
||||
</S.FilterEdit>
|
||||
<S.DeleteSavedFilter
|
||||
data-testid="deleteIcon"
|
||||
onClick={() => deleteFilterHandler(index)}
|
||||
>
|
||||
<i className="fas fa-times" />
|
||||
</S.DeleteSavedFilter>
|
||||
</S.FilterOptions>
|
||||
</S.SavedFilter>
|
||||
))}
|
||||
</S.SavedFiltersContainer>
|
||||
<S.FilterButtonWrapper>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="secondary"
|
||||
type="button"
|
||||
onClick={closeModal}
|
||||
disabled={isOpen}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
buttonSize="M"
|
||||
buttonType="primary"
|
||||
type="button"
|
||||
onClick={activeFilter}
|
||||
disabled={isOpen}
|
||||
>
|
||||
Select filter
|
||||
</Button>
|
||||
</S.FilterButtonWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SavedFilters;
|
|
@ -8,9 +8,7 @@ import userEvent from '@testing-library/user-event';
|
|||
import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||
|
||||
describe('AddEditFilterContainer component', () => {
|
||||
const defaultTitle = 'Test Title';
|
||||
const defaultSubmitBtn = 'Submit Button';
|
||||
const defaultNewFilter = 'Create New Filters';
|
||||
|
||||
const mockData: MessageFilters = {
|
||||
name: 'mockName',
|
||||
|
@ -18,14 +16,11 @@ describe('AddEditFilterContainer component', () => {
|
|||
};
|
||||
|
||||
const setupComponent = (props: Partial<AddEditFilterContainerProps> = {}) => {
|
||||
const { title, submitBtnText, createNewFilterText } = props;
|
||||
const { submitBtnText } = props;
|
||||
return render(
|
||||
<AddEditFilterContainer
|
||||
title={title || defaultTitle}
|
||||
cancelBtnHandler={jest.fn()}
|
||||
submitBtnText={submitBtnText || defaultSubmitBtn}
|
||||
createNewFilterText={createNewFilterText || defaultNewFilter}
|
||||
toggleSaveFilterSetter={jest.fn()}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
@ -35,14 +30,9 @@ describe('AddEditFilterContainer component', () => {
|
|||
beforeEach(() => {
|
||||
setupComponent();
|
||||
});
|
||||
it('should render the components', () => {
|
||||
expect(screen.getByRole('heading', { level: 3 })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check the default parameters values', () => {
|
||||
expect(screen.getByText(defaultTitle)).toBeInTheDocument();
|
||||
it('should check the default Button text', () => {
|
||||
expect(screen.getByText(defaultSubmitBtn)).toBeInTheDocument();
|
||||
expect(screen.getByText(defaultNewFilter)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check whether the submit Button is disabled when the form is pristine and disabled if dirty', async () => {
|
||||
|
@ -51,12 +41,12 @@ describe('AddEditFilterContainer component', () => {
|
|||
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
|
||||
const inputNameElement = inputs[0];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
const textAreaElement = inputs[1];
|
||||
const textAreaElement = inputs[0];
|
||||
userEvent.type(textAreaElement, 'Hello World With TextArea');
|
||||
|
||||
const inputNameElement = inputs[1];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(submitButtonElem).toBeEnabled();
|
||||
});
|
||||
|
@ -71,12 +61,12 @@ describe('AddEditFilterContainer component', () => {
|
|||
it('should view the error message after typing and clearing the input', async () => {
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
|
||||
const inputNameElement = inputs[0];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
const textAreaElement = inputs[1];
|
||||
const textAreaElement = inputs[0];
|
||||
userEvent.type(textAreaElement, 'Hello World With TextArea');
|
||||
|
||||
const inputNameElement = inputs[1];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
userEvent.clear(inputNameElement);
|
||||
userEvent.clear(textAreaElement);
|
||||
|
||||
|
@ -96,8 +86,8 @@ describe('AddEditFilterContainer component', () => {
|
|||
});
|
||||
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
const inputNameElement = inputs[0];
|
||||
const textAreaElement = inputs[1];
|
||||
const textAreaElement = inputs[0];
|
||||
const inputNameElement = inputs[1];
|
||||
|
||||
expect(inputNameElement).toHaveValue(mockData.name);
|
||||
expect(textAreaElement).toHaveValue(mockData.code);
|
||||
|
@ -114,19 +104,19 @@ describe('AddEditFilterContainer component', () => {
|
|||
});
|
||||
|
||||
it('should test whether the submit Callback is being called', async () => {
|
||||
const submitCallback = jest.fn() as (v: MessageFilters) => void;
|
||||
const submitCallback = jest.fn();
|
||||
setupComponent({
|
||||
submitCallback,
|
||||
});
|
||||
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
|
||||
const inputNameElement = inputs[0];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
const textAreaElement = inputs[1];
|
||||
const textAreaElement = inputs[0];
|
||||
userEvent.type(textAreaElement, 'Hello World With TextArea');
|
||||
|
||||
const inputNameElement = inputs[1];
|
||||
userEvent.type(inputNameElement, 'Hello World!');
|
||||
|
||||
const submitBtnElement = screen.getByText(defaultSubmitBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
|
@ -140,48 +130,20 @@ describe('AddEditFilterContainer component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should display the checkbox if the props is passed and click stay checking', async () => {
|
||||
const setCheckboxMock = jest.fn();
|
||||
setupComponent({
|
||||
toggleSaveFilterSetter: setCheckboxMock,
|
||||
});
|
||||
|
||||
const checkbox = screen.getByRole('checkbox');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
|
||||
userEvent.click(checkbox);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(setCheckboxMock).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display the checkbox if the props is passed and initially check state', () => {
|
||||
setupComponent({
|
||||
toggleSaveFilterSetter: jest.fn(),
|
||||
toggleSaveFilterValue: true,
|
||||
});
|
||||
|
||||
setupComponent({ isAdd: true });
|
||||
const checkbox = screen.getByRole('checkbox');
|
||||
expect(checkbox).toBeInTheDocument();
|
||||
expect(checkbox).not.toBeChecked();
|
||||
userEvent.click(checkbox);
|
||||
expect(checkbox).toBeChecked();
|
||||
});
|
||||
|
||||
it('should pass and render the view props', () => {
|
||||
const title = 'titleTest';
|
||||
const createNewFilterText = 'createNewFilterTextTest';
|
||||
it('should pass and render the correct button text', () => {
|
||||
const submitBtnText = 'submitBtnTextTest';
|
||||
setupComponent({
|
||||
title,
|
||||
createNewFilterText,
|
||||
submitBtnText,
|
||||
});
|
||||
expect(screen.getByText(title)).toBeInTheDocument();
|
||||
expect(screen.getByText(createNewFilterText)).toBeInTheDocument();
|
||||
expect(screen.getByText(submitBtnText)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,8 +7,12 @@ import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters
|
|||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
const filters: MessageFilters[] = [{ name: 'name', code: 'code' }];
|
||||
const setupComponent = (props?: Partial<FilterModalProps>) =>
|
||||
const filters: MessageFilters[] = [
|
||||
{ name: 'name', code: 'code' },
|
||||
{ name: 'name2', code: 'code2' },
|
||||
];
|
||||
|
||||
const setupComponent = (props: Partial<FilterModalProps> = {}) =>
|
||||
render(
|
||||
<AddFilter
|
||||
toggleIsOpen={jest.fn()}
|
||||
|
@ -17,151 +21,103 @@ const setupComponent = (props?: Partial<FilterModalProps>) =>
|
|||
activeFilterHandler={jest.fn()}
|
||||
toggleEditModal={jest.fn()}
|
||||
editFilter={jest.fn()}
|
||||
filters={filters}
|
||||
filters={props.filters || filters}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
describe('AddFilter component', () => {
|
||||
it('renders component with filters', () => {
|
||||
setupComponent({ filters });
|
||||
expect(screen.getByRole('savedFilter')).toBeInTheDocument();
|
||||
});
|
||||
it('renders component without filters', () => {
|
||||
setupComponent({ filters: [] });
|
||||
expect(screen.getByText('no saved filter(s)')).toBeInTheDocument();
|
||||
});
|
||||
it('renders add filter modal with saved filters', () => {
|
||||
it('should test click on Saved Filters redirects to Saved components', () => {
|
||||
setupComponent();
|
||||
expect(screen.getByText('Created filters')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByRole('savedFilterText'));
|
||||
expect(screen.getByText('Saved filters')).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('savedFilter')).toHaveLength(2);
|
||||
});
|
||||
describe('Filter deletion', () => {
|
||||
it('open deletion modal', () => {
|
||||
setupComponent();
|
||||
userEvent.hover(screen.getByRole('savedFilter'));
|
||||
userEvent.click(screen.getByTestId('deleteIcon'));
|
||||
expect(screen.getByRole('deletionModal')).toBeInTheDocument();
|
||||
});
|
||||
it('close deletion modal with button', () => {
|
||||
setupComponent();
|
||||
userEvent.hover(screen.getByRole('savedFilter'));
|
||||
userEvent.click(screen.getByTestId('deleteIcon'));
|
||||
expect(screen.getByRole('deletionModal')).toBeInTheDocument();
|
||||
const cancelButton = screen.getAllByRole('button', { name: /Cancel/i });
|
||||
userEvent.click(cancelButton[0]);
|
||||
expect(screen.getByText('Created filters')).toBeInTheDocument();
|
||||
});
|
||||
it('close deletion modal with close icon', () => {
|
||||
setupComponent();
|
||||
userEvent.hover(screen.getByRole('savedFilter'));
|
||||
userEvent.click(screen.getByTestId('deleteIcon'));
|
||||
expect(screen.getByRole('deletionModal')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByTestId('closeDeletionModalIcon'));
|
||||
expect(screen.getByText('Created filters')).toBeInTheDocument();
|
||||
});
|
||||
it('delete filter', () => {
|
||||
const deleteFilter = jest.fn();
|
||||
setupComponent({ filters, deleteFilter });
|
||||
userEvent.hover(screen.getByRole('savedFilter'));
|
||||
userEvent.click(screen.getByTestId('deleteIcon'));
|
||||
userEvent.click(screen.getByRole('button', { name: /Delete/i }));
|
||||
expect(deleteFilter).toHaveBeenCalledTimes(1);
|
||||
expect(screen.getByText('Created filters')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should test click on return to custom filter redirects to Add filters', () => {
|
||||
setupComponent();
|
||||
userEvent.click(screen.getByRole('savedFilterText'));
|
||||
expect(screen.getByText('Saved filters')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('savedFilterText')).not.toBeInTheDocument();
|
||||
expect(screen.getAllByRole('savedFilter')).toHaveLength(2);
|
||||
|
||||
userEvent.click(screen.getByText(/back to custom filters/i));
|
||||
expect(screen.queryByText('Saved filters')).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('savedFilterText')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('Add new filter', () => {
|
||||
beforeEach(() => {
|
||||
setupComponent();
|
||||
});
|
||||
it('renders add new filter modal', async () => {
|
||||
await waitFor(() => {
|
||||
userEvent.click(screen.getByText('New filter'));
|
||||
});
|
||||
expect(screen.getByText('Create a new filter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('adding new filter', async () => {
|
||||
await waitFor(() => {
|
||||
userEvent.click(screen.getByText('New filter'));
|
||||
});
|
||||
expect(
|
||||
screen.getByRole('button', { name: /Add filter/i })
|
||||
).toBeDisabled();
|
||||
const codeValue = 'filter code';
|
||||
const nameValue = 'filter name';
|
||||
const textBoxes = screen.getAllByRole('textbox');
|
||||
|
||||
const codeTextBox = textBoxes[0];
|
||||
const nameTextBox = textBoxes[1];
|
||||
|
||||
const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
|
||||
expect(addFilterBtn).toBeDisabled();
|
||||
expect(screen.getByPlaceholderText('Enter Name')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
userEvent.type(screen.getAllByRole('textbox')[0], 'filter name');
|
||||
userEvent.type(screen.getAllByRole('textbox')[1], 'filter code');
|
||||
userEvent.type(codeTextBox, codeValue);
|
||||
userEvent.type(nameTextBox, nameValue);
|
||||
});
|
||||
expect(screen.getAllByRole('textbox')[0]).toHaveValue('filter name');
|
||||
expect(screen.getAllByRole('textbox')[1]).toHaveValue('filter code');
|
||||
expect(addFilterBtn).toBeEnabled();
|
||||
expect(codeTextBox).toHaveValue(codeValue);
|
||||
expect(nameTextBox).toHaveValue(nameValue);
|
||||
});
|
||||
it('close add new filter modal', () => {
|
||||
userEvent.click(screen.getByText('New filter'));
|
||||
expect(screen.getByText('Save this filter')).toBeInTheDocument();
|
||||
userEvent.click(screen.getByText('Cancel'));
|
||||
expect(screen.getByText('Created filters')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
describe('Edit filter', () => {
|
||||
it('opens editFilter modal', () => {
|
||||
const editFilter = jest.fn();
|
||||
const toggleEditModal = jest.fn();
|
||||
setupComponent({ editFilter, toggleEditModal });
|
||||
userEvent.click(screen.getByText('Edit'));
|
||||
expect(editFilter).toHaveBeenCalledTimes(1);
|
||||
expect(toggleEditModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
describe('Selecting a filter', () => {
|
||||
it('should mock the select function if the filter is check no otherwise', () => {
|
||||
const toggleOpenMock = jest.fn();
|
||||
const activeFilterMock = jest.fn() as (
|
||||
activeFilter: MessageFilters,
|
||||
index: number
|
||||
) => void;
|
||||
setupComponent({
|
||||
filters,
|
||||
toggleIsOpen: toggleOpenMock,
|
||||
activeFilterHandler: activeFilterMock,
|
||||
|
||||
it('should check unSaved filter without name', async () => {
|
||||
const codeTextBox = screen.getAllByRole('textbox')[0];
|
||||
const code = 'filter code';
|
||||
const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
|
||||
expect(addFilterBtn).toBeDisabled();
|
||||
expect(screen.getByPlaceholderText('Enter Name')).toBeInTheDocument();
|
||||
await waitFor(() => {
|
||||
userEvent.type(codeTextBox, code);
|
||||
});
|
||||
const selectFilterButton = screen.getByText(/Select filter/i);
|
||||
|
||||
userEvent.click(selectFilterButton);
|
||||
expect(activeFilterMock).not.toHaveBeenCalled();
|
||||
expect(toggleOpenMock).not.toHaveBeenCalled();
|
||||
|
||||
const savedFilterElement = screen.getByRole('savedFilter');
|
||||
userEvent.click(savedFilterElement);
|
||||
userEvent.click(selectFilterButton);
|
||||
|
||||
expect(activeFilterMock).toHaveBeenCalled();
|
||||
expect(toggleOpenMock).toHaveBeenCalled();
|
||||
expect(addFilterBtn).toBeEnabled();
|
||||
expect(codeTextBox).toHaveValue(code);
|
||||
});
|
||||
});
|
||||
|
||||
describe('onSubmit with Filter being saved', () => {
|
||||
let addFilterMock: (values: MessageFilters) => void;
|
||||
let activeFilterHandlerMock: (
|
||||
activeFilter: MessageFilters,
|
||||
index: number
|
||||
) => void;
|
||||
const addFilterMock = jest.fn();
|
||||
const activeFilterHandlerMock = jest.fn();
|
||||
const toggleModelMock = jest.fn();
|
||||
|
||||
const codeValue = 'filter code';
|
||||
const nameValue = 'filter name';
|
||||
|
||||
beforeEach(async () => {
|
||||
addFilterMock = jest.fn() as (values: MessageFilters) => void;
|
||||
activeFilterHandlerMock = jest.fn() as (
|
||||
activeFilter: MessageFilters,
|
||||
index: number
|
||||
) => void;
|
||||
setupComponent({
|
||||
addFilter: addFilterMock,
|
||||
activeFilterHandler: activeFilterHandlerMock,
|
||||
toggleIsOpen: toggleModelMock,
|
||||
});
|
||||
userEvent.click(screen.getByText(/New filter/i));
|
||||
|
||||
await waitFor(() => {
|
||||
userEvent.type(screen.getAllByRole('textbox')[0], 'filter name');
|
||||
userEvent.type(screen.getAllByRole('textbox')[1], 'filter code');
|
||||
userEvent.type(screen.getAllByRole('textbox')[0], codeValue);
|
||||
userEvent.type(screen.getAllByRole('textbox')[1], nameValue);
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
addFilterMock.mockClear();
|
||||
activeFilterHandlerMock.mockClear();
|
||||
toggleModelMock.mockClear();
|
||||
});
|
||||
|
||||
it('OnSubmit condition with checkbox off functionality', async () => {
|
||||
userEvent.click(screen.getAllByRole('button')[1]);
|
||||
// since both values are in it
|
||||
const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
|
||||
expect(addFilterBtn).toBeEnabled();
|
||||
userEvent.click(addFilterBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(activeFilterHandlerMock).toHaveBeenCalled();
|
||||
expect(addFilterMock).not.toHaveBeenCalled();
|
||||
|
@ -175,6 +131,57 @@ describe('AddFilter component', () => {
|
|||
await waitFor(() => {
|
||||
expect(activeFilterHandlerMock).not.toHaveBeenCalled();
|
||||
expect(addFilterMock).toHaveBeenCalled();
|
||||
expect(toggleModelMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should check the state submit button when checkbox state changes so is name input value', async () => {
|
||||
const checkbox = screen.getByRole('checkbox');
|
||||
const codeTextBox = screen.getAllByRole('textbox')[0];
|
||||
const nameTextBox = screen.getAllByRole('textbox')[1];
|
||||
const addFilterBtn = screen.getByRole('button', { name: /Add filter/i });
|
||||
|
||||
userEvent.clear(nameTextBox);
|
||||
expect(nameTextBox).toHaveValue('');
|
||||
|
||||
userEvent.click(addFilterBtn);
|
||||
await waitFor(() => {
|
||||
expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
|
||||
expect(activeFilterHandlerMock).toHaveBeenCalledWith(
|
||||
{
|
||||
name: 'Unsaved filter',
|
||||
code: codeValue,
|
||||
saveFilter: false,
|
||||
},
|
||||
-1
|
||||
);
|
||||
// get reset-ed
|
||||
expect(codeTextBox).toHaveValue('');
|
||||
expect(toggleModelMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
userEvent.type(codeTextBox, codeValue);
|
||||
expect(codeTextBox).toHaveValue(codeValue);
|
||||
|
||||
userEvent.click(checkbox);
|
||||
expect(addFilterBtn).toBeDisabled();
|
||||
|
||||
userEvent.type(nameTextBox, nameValue);
|
||||
expect(nameTextBox).toHaveValue(nameValue);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(addFilterBtn).toBeEnabled();
|
||||
});
|
||||
|
||||
userEvent.click(addFilterBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(activeFilterHandlerMock).toHaveBeenCalledTimes(1);
|
||||
expect(addFilterMock).toHaveBeenCalledWith({
|
||||
name: nameValue,
|
||||
code: codeValue,
|
||||
saveFilter: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,13 +25,16 @@ const setupComponent = (props?: Partial<EditFilterProps>) =>
|
|||
describe('EditFilter component', () => {
|
||||
it('renders component', () => {
|
||||
setupComponent();
|
||||
expect(screen.getByText(/edit saved filter/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('closes editFilter modal', () => {
|
||||
const toggleEditModal = jest.fn();
|
||||
setupComponent({ toggleEditModal });
|
||||
userEvent.click(screen.getByRole('button', { name: /Cancel/i }));
|
||||
expect(toggleEditModal).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('save edited fields and close modal', async () => {
|
||||
const toggleEditModal = jest.fn();
|
||||
const editSavedFilter = jest.fn();
|
||||
|
@ -40,13 +43,14 @@ describe('EditFilter component', () => {
|
|||
expect(toggleEditModal).toHaveBeenCalledTimes(1);
|
||||
expect(editSavedFilter).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('checks input values to match', () => {
|
||||
setupComponent();
|
||||
expect(screen.getAllByRole('textbox')[0]).toHaveValue(
|
||||
editFilter.filter.name
|
||||
editFilter.filter.code
|
||||
);
|
||||
expect(screen.getAllByRole('textbox')[1]).toHaveValue(
|
||||
editFilter.filter.code
|
||||
editFilter.filter.name
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,10 +26,15 @@ describe('FilterModal component', () => {
|
|||
setupWrapper();
|
||||
});
|
||||
it('renders component with add filter modal', () => {
|
||||
expect(screen.getByText('Add filter')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('heading', { name: /add filter/i, level: 3 })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
it('renders component with edit filter modal', async () => {
|
||||
await waitFor(() => userEvent.click(screen.getByRole('savedFilterText')));
|
||||
await waitFor(() => userEvent.click(screen.getByText('Edit')));
|
||||
expect(screen.getByText('Edit saved filter')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('heading', { name: /edit saved filter/i, level: 3 })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,35 +45,42 @@ describe('Filters component', () => {
|
|||
it('renders component', () => {
|
||||
setupWrapper();
|
||||
});
|
||||
|
||||
describe('when fetching', () => {
|
||||
it('shows cancel button while fetching', () => {
|
||||
setupWrapper({ isFetching: true });
|
||||
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when fetching is over', () => {
|
||||
it('shows submit button while fetching is over', () => {
|
||||
setupWrapper();
|
||||
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input elements', () => {
|
||||
const inputValue = 'Hello World!';
|
||||
|
||||
it('search input', () => {
|
||||
setupWrapper();
|
||||
const SearchInput = screen.getByPlaceholderText('Search');
|
||||
expect(SearchInput).toBeInTheDocument();
|
||||
expect(SearchInput).toHaveValue('');
|
||||
userEvent.type(SearchInput, 'Hello World!');
|
||||
expect(SearchInput).toHaveValue('Hello World!');
|
||||
userEvent.type(SearchInput, inputValue);
|
||||
expect(SearchInput).toHaveValue(inputValue);
|
||||
});
|
||||
|
||||
it('offset input', () => {
|
||||
setupWrapper();
|
||||
const OffsetInput = screen.getByPlaceholderText('Offset');
|
||||
expect(OffsetInput).toBeInTheDocument();
|
||||
expect(OffsetInput).toHaveValue('');
|
||||
userEvent.type(OffsetInput, 'Hello World!');
|
||||
expect(OffsetInput).toHaveValue('Hello World!');
|
||||
userEvent.type(OffsetInput, inputValue);
|
||||
expect(OffsetInput).toHaveValue(inputValue);
|
||||
});
|
||||
|
||||
it('timestamp input', () => {
|
||||
setupWrapper();
|
||||
const seekTypeSelect = screen.getAllByRole('listbox');
|
||||
|
@ -84,11 +91,12 @@ describe('Filters component', () => {
|
|||
const TimestampInput = screen.getByPlaceholderText('Select timestamp');
|
||||
expect(TimestampInput).toBeInTheDocument();
|
||||
expect(TimestampInput).toHaveValue('');
|
||||
userEvent.type(TimestampInput, 'Hello World!');
|
||||
expect(TimestampInput).toHaveValue('Hello World!');
|
||||
userEvent.type(TimestampInput, inputValue);
|
||||
expect(TimestampInput).toHaveValue(inputValue);
|
||||
expect(screen.getByText('Submit')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Select elements', () => {
|
||||
let seekTypeSelects: HTMLElement[];
|
||||
let options: HTMLElement[];
|
||||
|
@ -137,7 +145,11 @@ describe('Filters component', () => {
|
|||
describe('add new filter modal', () => {
|
||||
it('renders addFilter modal', () => {
|
||||
setupWrapper();
|
||||
userEvent.click(screen.getByTestId('addFilterIcon'));
|
||||
userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: /add filters/i,
|
||||
})
|
||||
);
|
||||
expect(screen.getByTestId('messageFilterModal')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
@ -146,21 +158,43 @@ describe('Filters component', () => {
|
|||
beforeEach(async () => {
|
||||
setupWrapper();
|
||||
|
||||
await waitFor(() => userEvent.click(screen.getByTestId('addFilterIcon')));
|
||||
userEvent.click(screen.getByText('New filter'));
|
||||
await waitFor(() => {
|
||||
userEvent.type(screen.getAllByRole('textbox')[2], 'filter name');
|
||||
userEvent.type(screen.getAllByRole('textbox')[3], 'filter code');
|
||||
});
|
||||
expect(screen.getAllByRole('textbox')[2]).toHaveValue('filter name');
|
||||
expect(screen.getAllByRole('textbox')[3]).toHaveValue('filter code');
|
||||
await waitFor(() =>
|
||||
userEvent.click(screen.getByRole('button', { name: /Add Filter/i }))
|
||||
userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: /add filters/i,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const filterName = 'filter name';
|
||||
const filterCode = 'filter code';
|
||||
|
||||
const messageFilterModal = screen.getByTestId('messageFilterModal');
|
||||
|
||||
await waitFor(() => {
|
||||
const textBoxElements =
|
||||
within(messageFilterModal).getAllByRole('textbox');
|
||||
userEvent.type(textBoxElements[0], filterName);
|
||||
userEvent.type(textBoxElements[1], filterCode);
|
||||
});
|
||||
const textBoxElements =
|
||||
within(messageFilterModal).getAllByRole('textbox');
|
||||
expect(textBoxElements[0]).toHaveValue(filterName);
|
||||
expect(textBoxElements[1]).toHaveValue(filterCode);
|
||||
|
||||
await waitFor(() => {
|
||||
return userEvent.click(
|
||||
within(messageFilterModal).getByRole('button', {
|
||||
name: /add filter/i,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('shows saved smart filter', () => {
|
||||
expect(screen.getByTestId('activeSmartFilter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('delete the active smart Filter', async () => {
|
||||
const smartFilterElement = screen.getByTestId('activeSmartFilter');
|
||||
const deleteIcon = within(smartFilterElement).getByTestId(
|
||||
|
|
|
@ -0,0 +1,157 @@
|
|||
import React from 'react';
|
||||
import SavedFilters, {
|
||||
Props,
|
||||
} from 'components/Topics/Topic/Details/Messages/Filters/SavedFilters';
|
||||
import { MessageFilters } from 'components/Topics/Topic/Details/Messages/Filters/Filters';
|
||||
import { screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'lib/testHelpers';
|
||||
|
||||
describe('SavedFilter Component', () => {
|
||||
const mockFilters: MessageFilters[] = [
|
||||
{ name: 'name', code: 'code' },
|
||||
{ name: 'name1', code: 'code1' },
|
||||
];
|
||||
|
||||
const setUpComponent = (props: Partial<Props> = {}) => {
|
||||
return render(
|
||||
<SavedFilters
|
||||
filters={props.filters || mockFilters}
|
||||
onEdit={props.onEdit || jest.fn()}
|
||||
closeModal={props.closeModal || jest.fn()}
|
||||
onGoBack={props.onGoBack || jest.fn()}
|
||||
activeFilterHandler={props.activeFilterHandler || jest.fn()}
|
||||
deleteFilter={props.deleteFilter || jest.fn()}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
it('should check the Cancel button click', () => {
|
||||
const cancelMock = jest.fn();
|
||||
setUpComponent({ closeModal: cancelMock });
|
||||
userEvent.click(screen.getByText(/cancel/i));
|
||||
expect(cancelMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should check on go back button click', () => {
|
||||
const onGoBackMock = jest.fn();
|
||||
setUpComponent({ onGoBack: onGoBackMock });
|
||||
userEvent.click(screen.getByText(/back to custom filters/i));
|
||||
expect(onGoBackMock).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('Empty Filters Rendering', () => {
|
||||
beforeEach(() => {
|
||||
setUpComponent({ filters: [] });
|
||||
});
|
||||
it('should check the rendering of the empty filter', () => {
|
||||
expect(screen.getByText(/no saved filter/i)).toBeInTheDocument();
|
||||
expect(screen.queryByRole('savedFilter')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Saved Filters Deleting Editing', () => {
|
||||
const onEditMock = jest.fn();
|
||||
const activeFilterMock = jest.fn();
|
||||
const cancelMock = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
setUpComponent({
|
||||
onEdit: onEditMock,
|
||||
activeFilterHandler: activeFilterMock,
|
||||
closeModal: cancelMock,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
onEditMock.mockClear();
|
||||
activeFilterMock.mockClear();
|
||||
cancelMock.mockClear();
|
||||
});
|
||||
|
||||
it('should check the normal data rendering', () => {
|
||||
expect(screen.getAllByRole('savedFilter')).toHaveLength(
|
||||
mockFilters.length
|
||||
);
|
||||
expect(screen.getByText(mockFilters[0].name)).toBeInTheDocument();
|
||||
expect(screen.getByText(mockFilters[1].name)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should check the Filter edit Button works', () => {
|
||||
const savedFilters = screen.getAllByRole('savedFilter');
|
||||
userEvent.hover(savedFilters[0]);
|
||||
userEvent.click(within(savedFilters[0]).getByText(/edit/i));
|
||||
expect(onEditMock).toHaveBeenCalled();
|
||||
|
||||
userEvent.hover(savedFilters[1]);
|
||||
userEvent.click(within(savedFilters[1]).getByText(/edit/i));
|
||||
expect(onEditMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should check the select filter', () => {
|
||||
const selectFilterButton = screen.getByText(/Select filter/i);
|
||||
|
||||
userEvent.click(selectFilterButton);
|
||||
expect(activeFilterMock).not.toHaveBeenCalled();
|
||||
|
||||
const savedFilterElement = screen.getAllByRole('savedFilter');
|
||||
userEvent.click(savedFilterElement[0]);
|
||||
userEvent.click(selectFilterButton);
|
||||
|
||||
expect(activeFilterMock).toHaveBeenCalled();
|
||||
expect(cancelMock).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Saved Filters Deletion', () => {
|
||||
const deleteMock = jest.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
setUpComponent({ deleteFilter: deleteMock });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
deleteMock.mockClear();
|
||||
});
|
||||
|
||||
it('Open Confirmation for the deletion modal', () => {
|
||||
const savedFilters = screen.getAllByRole('savedFilter');
|
||||
const deleteIcons = screen.getAllByTestId('deleteIcon');
|
||||
userEvent.hover(savedFilters[0]);
|
||||
userEvent.click(deleteIcons[0]);
|
||||
const modelDialog = screen.getByRole('dialog');
|
||||
expect(modelDialog).toBeInTheDocument();
|
||||
expect(
|
||||
within(modelDialog).getByText(/Confirm deletion/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Close Confirmations deletion modal with button', () => {
|
||||
const savedFilters = screen.getAllByRole('savedFilter');
|
||||
const deleteIcons = screen.getAllByTestId('deleteIcon');
|
||||
|
||||
userEvent.hover(savedFilters[0]);
|
||||
userEvent.click(deleteIcons[0]);
|
||||
|
||||
const modelDialog = screen.getByRole('dialog');
|
||||
expect(modelDialog).toBeInTheDocument();
|
||||
const cancelButton = within(modelDialog).getByRole('button', {
|
||||
name: /Cancel/i,
|
||||
});
|
||||
userEvent.click(cancelButton);
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Delete the saved filter', () => {
|
||||
const savedFilters = screen.getAllByRole('savedFilter');
|
||||
const deleteIcons = screen.getAllByTestId('deleteIcon');
|
||||
|
||||
userEvent.hover(savedFilters[0]);
|
||||
userEvent.click(deleteIcons[0]);
|
||||
|
||||
userEvent.click(screen.getByRole('button', { name: /Delete/i }));
|
||||
expect(deleteMock).toHaveBeenCalledTimes(1);
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -9,15 +9,17 @@ export interface ConfirmationModalProps {
|
|||
onConfirm(): void;
|
||||
onCancel(): void;
|
||||
isConfirming?: boolean;
|
||||
submitBtnText?: string;
|
||||
}
|
||||
|
||||
const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
||||
isOpen,
|
||||
children,
|
||||
title,
|
||||
title = 'Confirm the action',
|
||||
onCancel,
|
||||
onConfirm,
|
||||
isConfirming = false,
|
||||
submitBtnText = 'Submit',
|
||||
}) => {
|
||||
const cancelHandler = React.useCallback(() => {
|
||||
if (!isConfirming) {
|
||||
|
@ -30,7 +32,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
|||
<div onClick={cancelHandler} aria-hidden="true" />
|
||||
<div>
|
||||
<header>
|
||||
<p>{title || 'Confirm the action'}</p>
|
||||
<p>{title}</p>
|
||||
</header>
|
||||
<section>{children}</section>
|
||||
<footer>
|
||||
|
@ -51,7 +53,7 @@ const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
|
|||
type="button"
|
||||
disabled={isConfirming}
|
||||
>
|
||||
Submit
|
||||
{submitBtnText}
|
||||
</Button>
|
||||
</footer>
|
||||
</div>
|
||||
|
|
|
@ -9,7 +9,7 @@ import theme from 'theme/theme';
|
|||
const confirmMock = jest.fn();
|
||||
const cancelMock = jest.fn();
|
||||
const body = 'Please Confirm the action!';
|
||||
describe('ConfiramationModal', () => {
|
||||
describe('ConfirmationModal', () => {
|
||||
const setupWrapper = (props: Partial<ConfirmationModalProps> = {}) => (
|
||||
<ThemeProvider theme={theme}>
|
||||
<ConfirmationModal
|
||||
|
@ -49,6 +49,12 @@ describe('ConfiramationModal', () => {
|
|||
title
|
||||
);
|
||||
});
|
||||
|
||||
it('Check the text on the submit button default behavior', () => {
|
||||
const wrapper = mount(setupWrapper({ isOpen: true }));
|
||||
expect(wrapper.exists({ children: 'Submit' })).toBeTruthy();
|
||||
});
|
||||
|
||||
it('handles onConfirm when user clicks confirm button', () => {
|
||||
const wrapper = mount(setupWrapper({ isOpen: true }));
|
||||
const confirmBtn = wrapper.find({ children: 'Submit' });
|
||||
|
@ -57,6 +63,12 @@ describe('ConfiramationModal', () => {
|
|||
expect(confirmMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Check the text on the submit button', () => {
|
||||
const submitBtnText = 'Submit btn Text';
|
||||
const wrapper = mount(setupWrapper({ isOpen: true, submitBtnText }));
|
||||
expect(wrapper.exists({ children: submitBtnText })).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('cancellation', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
|
||||
|
|
29
kafka-ui-react-app/src/components/common/Icons/SavedIcon.tsx
Normal file
29
kafka-ui-react-app/src/components/common/Icons/SavedIcon.tsx
Normal file
|
@ -0,0 +1,29 @@
|
|||
import React, { FC } from 'react';
|
||||
import { useTheme } from 'styled-components';
|
||||
|
||||
const SavedIcon: FC = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="20"
|
||||
viewBox="0 0 18 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M16 2H2L2 17.9873L7.29945 15.4982C8.3767 14.9922 9.6233 14.9922 10.7005 15.4982L16 17.9873V2ZM2 0C0.895431 0 0 0.895431 0 2V17.9873C0 19.4527 1.5239 20.4206 2.85027 19.7976L8.14973 17.3085C8.68835 17.0555 9.31165 17.0555 9.85027 17.3085L15.1497 19.7976C16.4761 20.4206 18 19.4527 18 17.9873V2C18 0.895431 17.1046 0 16 0H2Z"
|
||||
fill={theme.icons.savedIcon}
|
||||
/>
|
||||
<path
|
||||
d="M9 4L10.4401 7.01791L13.7553 7.45492L11.3301 9.75709L11.9389 13.0451L9 11.45L6.06107 13.0451L6.66991 9.75709L4.24472 7.45492L7.55993 7.01791L9 4Z"
|
||||
fill={theme.icons.savedIcon}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default SavedIcon;
|
66
kafka-ui-react-app/src/lib/hooks/__tests__/useModal.spec.ts
Normal file
66
kafka-ui-react-app/src/lib/hooks/__tests__/useModal.spec.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import useModal from 'lib/hooks/useModal';
|
||||
|
||||
describe('useModal CustomHook', () => {
|
||||
it('should check true initial values', () => {
|
||||
let initialValue = true;
|
||||
const { result, rerender } = renderHook(() => useModal(initialValue));
|
||||
expect(result.current.isOpen).toBe(initialValue);
|
||||
initialValue = false;
|
||||
rerender();
|
||||
// because state is in useState
|
||||
expect(result.current.isOpen).not.toBe(initialValue);
|
||||
});
|
||||
|
||||
it('should check false initial values', () => {
|
||||
let initialValue = false;
|
||||
const { result, rerender } = renderHook(() => useModal(initialValue));
|
||||
expect(result.current.isOpen).toBe(initialValue);
|
||||
|
||||
initialValue = true;
|
||||
rerender();
|
||||
// because state is in useState
|
||||
expect(result.current.isOpen).not.toBe(initialValue);
|
||||
});
|
||||
|
||||
it('should check setOpen function', () => {
|
||||
const { result } = renderHook(() => useModal());
|
||||
expect(result.current.isOpen).toBeFalsy();
|
||||
act(() => {
|
||||
result.current.setOpen();
|
||||
});
|
||||
expect(result.current.isOpen).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should check setClose function', () => {
|
||||
const { result } = renderHook(() => useModal());
|
||||
|
||||
expect(result.current.isOpen).toBeFalsy();
|
||||
act(() => {
|
||||
result.current.setOpen();
|
||||
});
|
||||
|
||||
expect(result.current.isOpen).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
result.current.setClose();
|
||||
});
|
||||
expect(result.current.isOpen).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should check setToggle function', () => {
|
||||
const { result } = renderHook(() => useModal());
|
||||
|
||||
expect(result.current.isOpen).toBeFalsy();
|
||||
act(() => {
|
||||
result.current.toggle();
|
||||
});
|
||||
|
||||
expect(result.current.isOpen).toBeTruthy();
|
||||
|
||||
act(() => {
|
||||
result.current.toggle();
|
||||
});
|
||||
expect(result.current.isOpen).toBeFalsy();
|
||||
});
|
||||
});
|
32
kafka-ui-react-app/src/lib/hooks/useModal.ts
Normal file
32
kafka-ui-react-app/src/lib/hooks/useModal.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
import { useCallback, useState } from 'react';
|
||||
|
||||
interface UseModalReturn {
|
||||
isOpen: boolean;
|
||||
setOpen(): void;
|
||||
setClose(): void;
|
||||
toggle(): void;
|
||||
}
|
||||
const useModal = (initialModalState?: boolean): UseModalReturn => {
|
||||
const [modalOpen, setModalOpen] = useState<boolean>(!!initialModalState);
|
||||
|
||||
const setOpen = useCallback(() => {
|
||||
setModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const setClose = useCallback(() => {
|
||||
setModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const toggle = useCallback(() => {
|
||||
setModalOpen((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isOpen: modalOpen,
|
||||
setOpen,
|
||||
setClose,
|
||||
toggle,
|
||||
};
|
||||
};
|
||||
|
||||
export default useModal;
|
|
@ -489,6 +489,7 @@ const theme = {
|
|||
},
|
||||
newFilterIcon: Colors.brand[50],
|
||||
closeModalIcon: Colors.neutral[25],
|
||||
savedIcon: Colors.brand[50],
|
||||
},
|
||||
viewer: {
|
||||
wrapper: Colors.neutral[3],
|
||||
|
|
Loading…
Add table
Reference in a new issue