From 58f9df748eb685cea27ff11214122aad9bd71f8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 14:33:36 +0300 Subject: [PATCH 1/6] feat: added fast filters --- .../FilterSearch/FastFilters/index.tsx | 100 +++++++++ .../SideBar/FilterSearch/NodeTypes/index.tsx | 155 +++++++++++++ .../App/SideBar/FilterSearch/index.tsx | 203 ++++-------------- .../App/SideBar/RegularView/index.tsx | 28 +-- 4 files changed, 295 insertions(+), 191 deletions(-) create mode 100644 src/components/App/SideBar/FilterSearch/FastFilters/index.tsx create mode 100644 src/components/App/SideBar/FilterSearch/NodeTypes/index.tsx diff --git a/src/components/App/SideBar/FilterSearch/FastFilters/index.tsx b/src/components/App/SideBar/FilterSearch/FastFilters/index.tsx new file mode 100644 index 000000000..933fcf0c8 --- /dev/null +++ b/src/components/App/SideBar/FilterSearch/FastFilters/index.tsx @@ -0,0 +1,100 @@ +import { useState } from 'react' +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import { colors } from '~/utils' +import { PopoverBody } from '..' + +type Props = { + handleFastFiltersSelect: (types: string[]) => void +} + +const FAST_FILTERS: { [key: string]: string[] } = { + Monitoring: ['Bugevent', 'Trace', 'Application', 'Report', 'Stacktrace'], +} + +export const FastFilters = ({ handleFastFiltersSelect }: Props) => { + const [selected, setSelected] = useState('') + + const handleSelection = (filter: string) => { + if (selected === filter) { + handleFastFiltersSelect([]) + setSelected('') + + return + } + + if (FAST_FILTERS[filter]) { + handleFastFiltersSelect(FAST_FILTERS[filter]) + setSelected(filter) + } + } + + return ( + <> + +
Fast Filters
+
+ + + {Object.keys(FAST_FILTERS).map((filter) => ( + handleSelection(filter)}> + {filter} + + ))} + + + + ) +} + +const PopoverHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 8px; + font-family: Barlow; + font-size: 18px; + font-weight: 500; +` + +const SchemaTypeWrapper = styled(Flex).attrs({ + align: 'center', + direction: 'row', + grow: 1, + justify: 'flex-start', +})` + flex-wrap: wrap; + gap: 10px; + max-height: 400px; + overflow-y: auto; + padding-right: 10px; + margin-right: calc(0px - 16px); +` + +const SchemaType = styled(Flex).attrs({ + align: 'center', + direction: 'row', + justify: 'flex-start', +})<{ isSelected: boolean }>` + color: ${({ isSelected }) => (isSelected ? colors.black : colors.white)}; + background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; + padding: 6px 10px 6px 8px; + font-family: Barlow; + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: 15px; + letter-spacing: 0.78px; + margin: 0 3px; + border-radius: 200px; + cursor: pointer; + + &:hover { + background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; + } + + &:active { + background: ${colors.white}; + color: ${colors.black}; + } +` diff --git a/src/components/App/SideBar/FilterSearch/NodeTypes/index.tsx b/src/components/App/SideBar/FilterSearch/NodeTypes/index.tsx new file mode 100644 index 000000000..4944d5be1 --- /dev/null +++ b/src/components/App/SideBar/FilterSearch/NodeTypes/index.tsx @@ -0,0 +1,155 @@ +import { useState } from 'react' +import styled from 'styled-components' +import { Flex } from '~/components/common/Flex' +import PlusIcon from '~/components/Icons/PlusIcon' +import { SchemaExtended } from '~/components/ModalsContainer/BlueprintModal/types' +import { colors } from '~/utils' +import { PopoverBody } from '..' + +type Props = { + handleSchemaTypeClick: (type: string) => void + selectedTypes: string[] + schemaAll: SchemaExtended[] +} + +export const NodeTypes = ({ handleSchemaTypeClick, selectedTypes, schemaAll }: Props) => { + const [showAllSchemas, setShowAllSchemas] = useState(false) + + const uniqueSchemas = (showAllSchemas ? schemaAll : schemaAll.slice(0, 4)).filter( + (schema, index, self) => index === self.findIndex((s) => s.type === schema.type), + ) + + return ( + <> + +
Type
+ + {selectedTypes.length} + Selected + +
+ + + {uniqueSchemas.map((schema) => ( + handleSchemaTypeClick(schema?.type as string)} + > + {schema.type} + + ))} + + {!showAllSchemas && schemaAll.length > 4 && ( + setShowAllSchemas(true)}> + + View More + + + )} + + + ) +} + +const PopoverHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 8px; + font-family: Barlow; + font-size: 18px; + font-weight: 500; +` + +const CountSelectedWrapper = styled.div` + font-size: 13px; + display: flex; + align-items: center; +` + +const Count = styled.span` + color: ${colors.white}; +` + +const SelectedText = styled.span` + color: ${colors.GRAY3}; + margin-left: 4px; +` + +const PlusIconWrapper = styled.span` + display: flex; + justify-content: space-between; + align-items: center; + gap: 6px; + + svg { + width: 23px; + height: 23px; + fill: none; + margin-top: 2px; + } +` + +const SchemaTypeWrapper = styled(Flex).attrs({ + align: 'center', + direction: 'row', + grow: 1, + justify: 'flex-start', +})` + flex-wrap: wrap; + gap: 10px; + max-height: 400px; + overflow-y: auto; + padding-right: 10px; + margin-right: calc(0px - 16px); +` + +const SchemaType = styled(Flex).attrs({ + align: 'center', + direction: 'row', + justify: 'flex-start', +})<{ isSelected: boolean }>` + color: ${({ isSelected }) => (isSelected ? colors.black : colors.white)}; + background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; + padding: 6px 10px 6px 8px; + font-family: Barlow; + font-size: 13px; + font-style: normal; + font-weight: 500; + line-height: 15px; + letter-spacing: 0.78px; + margin: 0 3px; + border-radius: 200px; + cursor: pointer; + + &:hover { + background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; + } + + &:active { + background: ${colors.white}; + color: ${colors.black}; + } +` + +const ViewMoreButton = styled.button` + background: transparent; + color: ${colors.white}; + border: none; + padding: 6px 12px 6px 3px; + margin-top: 20px; + cursor: pointer; + border-radius: 4px; + font-family: Barlow; + font-size: 13px; + font-weight: 500; + + &:hover { + background: ${colors.BUTTON1_HOVER}; + } + + &:active { + background: ${colors.BUTTON1_PRESS}; + } +` diff --git a/src/components/App/SideBar/FilterSearch/index.tsx b/src/components/App/SideBar/FilterSearch/index.tsx index 71c4bc4fd..c80d8bfac 100644 --- a/src/components/App/SideBar/FilterSearch/index.tsx +++ b/src/components/App/SideBar/FilterSearch/index.tsx @@ -1,21 +1,20 @@ import { Button, Popper } from '@mui/material' -import { useState } from 'react' +import { useEffect, useState } from 'react' import styled from 'styled-components' import ClearIcon from '~/components/Icons/ClearIcon' -import PlusIcon from '~/components/Icons/PlusIcon' -import { SchemaExtended } from '~/components/ModalsContainer/BlueprintModal/types' import { Flex } from '~/components/common/Flex' +import { getSchemaAll } from '~/network/fetchSourcesData' import { useDataStore } from '~/stores/useDataStore' +import { useSchemaStore } from '~/stores/useSchemaStore' import { useUserStore } from '~/stores/useUserStore' import { colors } from '~/utils/colors' +import { FastFilters } from './FastFilters' import { Hops } from './Hops' import { MaxResults } from './MaxResults' +import { NodeTypes } from './NodeTypes' import { SourceNodes } from './SourceNodes' type Props = { - showAllSchemas: boolean - setShowAllSchemas: (value: boolean) => void - schemaAll: SchemaExtended[] anchorEl: HTMLElement | null setAnchorEl: (value: HTMLElement | null) => void onClose: () => void @@ -28,20 +27,8 @@ const defaultValues = { maxResults: 30, } -export const FilterSearch = ({ - showAllSchemas, - setShowAllSchemas, - schemaAll, - anchorEl, - setAnchorEl, - onClose, -}: Props) => { - const handleSchemaTypeClick = (type: string) => { - setSelectedTypes((prevSelectedTypes) => - prevSelectedTypes.includes(type) ? prevSelectedTypes.filter((t) => t !== type) : [...prevSelectedTypes, type], - ) - } - +export const FilterSearch = ({ anchorEl, setAnchorEl, onClose }: Props) => { + const [schemaAll, setSchemaAll] = useSchemaStore((s) => [s.schemas, s.setSchemas]) const { setFilters, fetchData, setAbortRequests } = useDataStore((s) => s) const { setBudget } = useUserStore((s) => s) const [selectedTypes, setSelectedTypes] = useState(defaultValues.selectedTypes) @@ -49,6 +36,30 @@ export const FilterSearch = ({ const [sourceNodes, setSourceNodes] = useState(defaultValues.sourceNodes) const [maxResults, setMaxResults] = useState(defaultValues.maxResults) + useEffect(() => { + const fetchSchemaData = async () => { + try { + const response = await getSchemaAll() + + setSchemaAll(response.schemas.filter((schema) => !schema.is_deleted)) + } catch (error) { + console.error('Error fetching schema:', error) + } + } + + fetchSchemaData() + }, [setSchemaAll]) + + const handleSchemaTypeClick = (type: string) => { + setSelectedTypes((prevSelectedTypes) => + prevSelectedTypes.includes(type) ? prevSelectedTypes.filter((t) => t !== type) : [...prevSelectedTypes, type], + ) + } + + const handleSchemaTypesSelect = (types: string[]) => { + setSelectedTypes(types) + } + const resetToDefaultValues = () => { setSelectedTypes(defaultValues.selectedTypes) setHops(defaultValues.hops) @@ -60,10 +71,6 @@ export const FilterSearch = ({ resetToDefaultValues() } - const handleViewMoreClick = () => { - setShowAllSchemas(true) - } - const handleFiltersApply = async () => { setFilters({ node_type: selectedTypes, @@ -78,10 +85,6 @@ export const FilterSearch = ({ await fetchData(setBudget, setAbortRequests) } - const uniqueSchemas = (showAllSchemas ? schemaAll : schemaAll.slice(0, 4)).filter( - (schema, index, self) => index === self.findIndex((s) => s.type === schema.type), - ) - return ( - -
Type
- - {selectedTypes.length} - Selected - -
- - - {uniqueSchemas.map((schema) => ( - handleSchemaTypeClick(schema?.type as string)} - > - {schema.type} - - ))} - - {!showAllSchemas && schemaAll.length > 4 && ( - - - View More - - - )} - + + + - - @@ -182,36 +159,6 @@ const SearchFilterPopover = styled(Popper)` } ` -const PopoverHeader = styled.div` - display: flex; - justify-content: space-between; - align-items: center; - padding-bottom: 8px; - font-family: Barlow; - font-size: 18px; - font-weight: 500; -` - -const CountSelectedWrapper = styled.div` - font-size: 13px; - display: flex; - align-items: center; -` - -const Count = styled.span` - color: ${colors.white}; -` - -const SelectedText = styled.span` - color: ${colors.GRAY3}; - margin-left: 4px; -` - -export const PopoverBody = styled.div` - padding: 13px 0; - position: relative; -` - const PopoverFooter = styled.div` display: flex; justify-content: space-between; @@ -225,83 +172,6 @@ const LineBar = styled.div` margin: 13px -16px; ` -const PlusIconWrapper = styled.span` - display: flex; - justify-content: space-between; - align-items: center; - gap: 6px; - - svg { - width: 23px; - height: 23px; - fill: none; - margin-top: 2px; - } -` - -const SchemaTypeWrapper = styled(Flex).attrs({ - align: 'center', - direction: 'row', - grow: 1, - justify: 'flex-start', -})` - flex-wrap: wrap; - gap: 10px; - max-height: 400px; - overflow-y: auto; - padding-right: 10px; - margin-right: calc(0px - 16px); -` - -const SchemaType = styled(Flex).attrs({ - align: 'center', - direction: 'row', - justify: 'flex-start', -})<{ isSelected: boolean }>` - color: ${({ isSelected }) => (isSelected ? colors.black : colors.white)}; - background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; - padding: 6px 10px 6px 8px; - font-family: Barlow; - font-size: 13px; - font-style: normal; - font-weight: 500; - line-height: 15px; - letter-spacing: 0.78px; - margin: 0 3px; - border-radius: 200px; - cursor: pointer; - - &:hover { - background: ${({ isSelected }) => (isSelected ? colors.white : colors.BUTTON1_PRESS)}; - } - - &:active { - background: ${colors.white}; - color: ${colors.black}; - } -` - -const ViewMoreButton = styled.button` - background: transparent; - color: ${colors.white}; - border: none; - padding: 6px 12px 6px 3px; - margin-top: 20px; - cursor: pointer; - border-radius: 4px; - font-family: Barlow; - font-size: 13px; - font-weight: 500; - - &:hover { - background: ${colors.BUTTON1_HOVER}; - } - - &:active { - background: ${colors.BUTTON1_PRESS}; - } -` - const ClearButton = styled(Button)` && { color: ${colors.white}; @@ -392,3 +262,8 @@ export const ButtonsWrapper = styled(Flex)` flex-direction: row; margin: 0 0 6px 8px; ` + +export const PopoverBody = styled.div` + padding: 13px 0; + position: relative; +` diff --git a/src/components/App/SideBar/RegularView/index.tsx b/src/components/App/SideBar/RegularView/index.tsx index e4c2fe061..bc4887847 100644 --- a/src/components/App/SideBar/RegularView/index.tsx +++ b/src/components/App/SideBar/RegularView/index.tsx @@ -13,12 +13,10 @@ import SearchIcon from '~/components/Icons/SearchIcon' import { SearchBar } from '~/components/SearchBar' import { Flex } from '~/components/common/Flex' import { FetchLoaderText } from '~/components/common/Loader' -import { getSchemaAll } from '~/network/fetchSourcesData' import { useAppStore } from '~/stores/useAppStore' import { useDataStore, useFilteredNodes } from '~/stores/useDataStore' import { useFeatureFlagStore } from '~/stores/useFeatureFlagStore' import { useUpdateSelectedNode } from '~/stores/useGraphStore' -import { useSchemaStore } from '~/stores/useSchemaStore' import { colors } from '~/utils/colors' import { LatestView } from '../Latest' import { Relevance } from '../Relevance' @@ -30,7 +28,6 @@ export const MENU_WIDTH = 390 // eslint-disable-next-line react/display-name export const RegularView = () => { const { isFetching: isLoading, setSidebarFilter } = useDataStore((s) => s) - const [schemaAll, setSchemaAll] = useSchemaStore((s) => [s.schemas, s.setSchemas]) const setSelectedNode = useUpdateSelectedNode() @@ -45,7 +42,6 @@ export const RegularView = () => { const [isScrolled, setIsScrolled] = useState(false) const [isFilterOpen, setIsFilterOpen] = useState(false) const [anchorEl, setAnchorEl] = useState(null) - const [showAllSchemas, setShowAllSchemas] = useState(false) useEffect(() => { setValue('search', searchFormValue) @@ -67,20 +63,6 @@ export const RegularView = () => { const typing = watch('search') - useEffect(() => { - const fetchSchemaData = async () => { - try { - const response = await getSchemaAll() - - setSchemaAll(response.schemas.filter((schema) => !schema.is_deleted)) - } catch (error) { - console.error('Error fetching schema:', error) - } - } - - fetchSchemaData() - }, [setSchemaAll]) - const handleFilterIconClick = (event: React.MouseEvent) => { if (isFilterOpen) { setAnchorEl(null) @@ -89,7 +71,6 @@ export const RegularView = () => { } setIsFilterOpen((prev) => !prev) - setShowAllSchemas(false) } const handleCloseFilterSearch = () => { @@ -139,14 +120,7 @@ export const RegularView = () => { {isFilterOpen ? : } - + {searchTerm && ( From c2d9853ef822e818917dce6b07bda8f281367ece Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 15:03:31 +0300 Subject: [PATCH 2/6] feat: added feature flag for fast filters --- src/components/App/SideBar/FilterSearch/index.tsx | 4 +++- src/components/Auth/index.tsx | 13 +++++-------- src/stores/useFeatureFlagStore/index.ts | 5 +++++ src/types/index.ts | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/App/SideBar/FilterSearch/index.tsx b/src/components/App/SideBar/FilterSearch/index.tsx index c80d8bfac..6e99fa858 100644 --- a/src/components/App/SideBar/FilterSearch/index.tsx +++ b/src/components/App/SideBar/FilterSearch/index.tsx @@ -5,6 +5,7 @@ import ClearIcon from '~/components/Icons/ClearIcon' import { Flex } from '~/components/common/Flex' import { getSchemaAll } from '~/network/fetchSourcesData' import { useDataStore } from '~/stores/useDataStore' +import { useFeatureFlagStore } from '~/stores/useFeatureFlagStore' import { useSchemaStore } from '~/stores/useSchemaStore' import { useUserStore } from '~/stores/useUserStore' import { colors } from '~/utils/colors' @@ -35,6 +36,7 @@ export const FilterSearch = ({ anchorEl, setAnchorEl, onClose }: Props) => { const [hops, setHops] = useState(defaultValues.hops) const [sourceNodes, setSourceNodes] = useState(defaultValues.sourceNodes) const [maxResults, setMaxResults] = useState(defaultValues.maxResults) + const { fastFiltersFeatureFlag } = useFeatureFlagStore((s) => s) useEffect(() => { const fetchSchemaData = async () => { @@ -100,7 +102,7 @@ export const FilterSearch = ({ anchorEl, setAnchorEl, onClose }: Props) => { open={Boolean(anchorEl)} placement="bottom-end" > - + {fastFiltersFeatureFlag && } diff --git a/src/components/Auth/index.tsx b/src/components/Auth/index.tsx index 98f9b3fbf..626d5ab73 100644 --- a/src/components/Auth/index.tsx +++ b/src/components/Auth/index.tsx @@ -19,19 +19,14 @@ export const AuthGuard = ({ children }: PropsWithChildren) => { const { splashDataLoading } = useDataStore((s) => s) const [renderMainPage, setRenderMainPage] = useState(false) - const [ + const { setTrendingTopicsFeatureFlag, setQueuedSourcesFeatureFlag, setCustomSchemaFeatureFlag, setRealtimeGraphFeatureFlag, setChatInterfaceFeatureFlag, - ] = useFeatureFlagStore((s) => [ - s.setTrendingTopicsFeatureFlag, - s.setQueuedSourcesFeatureFlag, - s.setCustomSchemaFeatureFlag, - s.setRealtimeGraphFeatureFlag, - s.setChatInterfaceFeatureFlag, - ]) + setFastFiltersFeatureFlag, + } = useFeatureFlagStore((s) => s) const handleAuth = useCallback(async () => { localStorage.removeItem('admin') @@ -81,6 +76,7 @@ export const AuthGuard = ({ children }: PropsWithChildren) => { setCustomSchemaFeatureFlag(res.data.customSchema) setRealtimeGraphFeatureFlag(res.data.realtimeGraph || false) setChatInterfaceFeatureFlag(res.data.chatInterface || false) + setFastFiltersFeatureFlag(res.data.fastFilters || false) } setIsAuthenticated(true) @@ -97,6 +93,7 @@ export const AuthGuard = ({ children }: PropsWithChildren) => { setCustomSchemaFeatureFlag, setRealtimeGraphFeatureFlag, setChatInterfaceFeatureFlag, + setFastFiltersFeatureFlag, setSwarmUiUrl, ]) diff --git a/src/stores/useFeatureFlagStore/index.ts b/src/stores/useFeatureFlagStore/index.ts index 1f91c34ca..e87cb560a 100644 --- a/src/stores/useFeatureFlagStore/index.ts +++ b/src/stores/useFeatureFlagStore/index.ts @@ -10,6 +10,7 @@ export type FeatureFlagStore = { realtimeGraphFeatureFlag: boolean userFeedbackFeatureFlag: boolean chatInterfaceFeatureFlag: boolean + fastFiltersFeatureFlag: boolean setTrendingTopicsFeatureFlag: (val: boolean) => void setV2FeatureFlag: (val: boolean) => void setQueuedSourcesFeatureFlag: (val: boolean) => void @@ -19,6 +20,7 @@ export type FeatureFlagStore = { setRealtimeGraphFeatureFlag: (val: boolean) => void setUserFeedbackFeatureFlag: (val: boolean) => void setChatInterfaceFeatureFlag: (val: boolean) => void + setFastFiltersFeatureFlag: (val: boolean) => void } const defaultData: Omit< @@ -32,6 +34,7 @@ const defaultData: Omit< | 'setRealtimeGraphFeatureFlag' | 'setUserFeedbackFeatureFlag' | 'setChatInterfaceFeatureFlag' + | 'setFastFiltersFeatureFlag' > = { trendingTopicsFeatureFlag: true, queuedSourcesFeatureFlag: false, @@ -42,6 +45,7 @@ const defaultData: Omit< realtimeGraphFeatureFlag: false, userFeedbackFeatureFlag: false, chatInterfaceFeatureFlag: false, + fastFiltersFeatureFlag: false, } export const useFeatureFlagStore = create((set) => ({ @@ -55,4 +59,5 @@ export const useFeatureFlagStore = create((set) => ({ setRealtimeGraphFeatureFlag: (realtimeGraphFeatureFlag) => set({ realtimeGraphFeatureFlag }), setUserFeedbackFeatureFlag: (userFeedbackFeatureFlag) => set({ userFeedbackFeatureFlag }), setChatInterfaceFeatureFlag: (chatInterfaceFeatureFlag) => set({ chatInterfaceFeatureFlag }), + setFastFiltersFeatureFlag: (fastFiltersFeatureFlag) => set({ fastFiltersFeatureFlag }), })) diff --git a/src/types/index.ts b/src/types/index.ts index 6a7dcf700..55cd48c9e 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -241,6 +241,7 @@ export type IsAdminResponse = { realtimeGraph: boolean chatInterface: boolean swarmUiUrl: string + fastFilters: boolean } success: boolean message: string From 723fb6b744315b664918f34ba36434607c9e225d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 15:05:37 +0300 Subject: [PATCH 3/6] feat: moved linebar under condition --- src/components/App/SideBar/FilterSearch/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/App/SideBar/FilterSearch/index.tsx b/src/components/App/SideBar/FilterSearch/index.tsx index 6e99fa858..8e18af0b0 100644 --- a/src/components/App/SideBar/FilterSearch/index.tsx +++ b/src/components/App/SideBar/FilterSearch/index.tsx @@ -102,8 +102,13 @@ export const FilterSearch = ({ anchorEl, setAnchorEl, onClose }: Props) => { open={Boolean(anchorEl)} placement="bottom-end" > - {fastFiltersFeatureFlag && } - + {fastFiltersFeatureFlag && ( + <> + + + + )} + From d8d2dea5ddd18b1cdd0fe63b7ea58145ac1972bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 17:15:13 +0300 Subject: [PATCH 4/6] feat: updated tests for filters --- .../SideBar/FilterSearch/__tests__/index.tsx | 194 ++++-------------- 1 file changed, 39 insertions(+), 155 deletions(-) diff --git a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx index c09d6a4c2..051c2e162 100644 --- a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx +++ b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx @@ -1,10 +1,12 @@ -/* eslint-disable padding-line-between-statements */ import { ThemeProvider } from '@mui/material' import '@testing-library/jest-dom' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import React from 'react' import { ThemeProvider as StyleThemeProvider } from 'styled-components' +import { getSchemaAll } from '~/network/fetchSourcesData' import { useDataStore } from '~/stores/useDataStore' +import { useFeatureFlagStore } from '~/stores/useFeatureFlagStore' +import { useSchemaStore } from '~/stores/useSchemaStore' import { colors } from '~/utils/colors' import { appTheme } from '../../../Providers' import { FilterSearch } from '../index' @@ -13,207 +15,89 @@ jest.mock('~/stores/useDataStore', () => ({ useDataStore: jest.fn(), })) +jest.mock('~/stores/useSchemaStore', () => ({ + useSchemaStore: jest.fn(), +})) + +jest.mock('~/stores/useFeatureFlagStore', () => ({ + useFeatureFlagStore: jest.fn(), +})) + +jest.mock('~/network/fetchSourcesData', () => ({ + getSchemaAll: jest.fn(), +})) + const mockSetFilters = jest.fn() -const mockSetShowAllSchemas = jest.fn() const mockSetAnchorEl = jest.fn() const mockFetchData = jest.fn() const mockSetAbortRequests = jest.fn() const mockOnClose = jest.fn() - -const mockSchemaAll = [{ type: 'Type1' }, { type: 'Type2' }, { type: 'Type3' }, { type: 'Type4' }, { type: 'Type5' }] +const mockSchemaAll = [{ type: 'Type1' }, { type: 'Type2' }, { type: 'Type3' }] describe('FilterSearch Component', () => { beforeEach(() => { jest.clearAllMocks() + + // ;(useDataStore as jest.Mock).mockReturnValue({ setFilters: mockSetFilters, fetchData: mockFetchData, setAbortRequests: mockSetAbortRequests, - onClose: mockOnClose, }) + ;(useSchemaStore as jest.Mock).mockReturnValue([mockSchemaAll, jest.fn()]) + ;(useFeatureFlagStore as jest.Mock).mockReturnValue({ fastFiltersFeatureFlag: true }) + ;(getSchemaAll as jest.Mock).mockResolvedValue({ schemas: mockSchemaAll }) }) - const renderComponent = (showAllSchemas = false) => + const renderComponent = () => render( - + , ) - it('should highlight node_type if selected and allow multiple selections', () => { + it('should fetch and display schema types', async () => { renderComponent() - const type1Pill = screen.getByText('Type1') - const type2Pill = screen.getByText('Type2') - - fireEvent.click(type1Pill) - fireEvent.click(type2Pill) - - expect(type1Pill).toHaveStyle(`background: ${colors.white}`) - expect(type1Pill).toHaveStyle(`color: ${colors.black}`) - expect(type2Pill).toHaveStyle(`background: ${colors.white}`) - expect(type2Pill).toHaveStyle(`color: ${colors.black}`) + await waitFor(() => { + mockSchemaAll.forEach((schema) => { + expect(screen.getByText(schema.type)).toBeInTheDocument() + }) + }) }) - it('should ensure node_type gets added to the request', async () => { + it('should highlight selected schema type when clicked', async () => { renderComponent() const type1Pill = screen.getByText('Type1') fireEvent.click(type1Pill) - const showResultsButton = screen.getByText('Show Results') - - fireEvent.click(showResultsButton) - - await waitFor(() => { - expect(mockSetFilters).toHaveBeenCalledWith( - expect.objectContaining({ - node_type: ['Type1'], - }), - ) - }) + expect(type1Pill).toHaveStyle(`background: ${colors.white}`) + expect(type1Pill).toHaveStyle(`color: ${colors.black}`) }) - it('should ensure all selected params in the filter get added correctly to the request', async () => { + it('should apply filters when "Show Results" is clicked', async () => { renderComponent() const type1Pill = screen.getByText('Type1') fireEvent.click(type1Pill) - const hopsCheckbox = screen.getByLabelText('2 hops away') - - fireEvent.click(hopsCheckbox) - - const maxResultsSlider = screen.getByTestId('max-results-slider') - fireEvent.mouseDown(maxResultsSlider) - fireEvent.mouseUp(maxResultsSlider, { clientX: 50 }) - - const sourceNodesSlider = screen.getByTestId('source-nodes-slider') - fireEvent.mouseDown(sourceNodesSlider) - fireEvent.mouseUp(sourceNodesSlider, { clientX: 20 }) - const showResultsButton = screen.getByText('Show Results') + fireEvent.click(showResultsButton) - waitFor(() => { + await waitFor(() => { expect(mockSetFilters).toHaveBeenCalledWith({ node_type: ['Type1'], - limit: '50', - depth: '2', - top_node_count: '20', + limit: 30, + depth: '1', + top_node_count: '10', }) }) }) - - it('should ensure slider is calculated from left to right correctly', () => { - renderComponent() - - const maxResultsSlider = screen.getByTestId('max-results-slider') - fireEvent.mouseDown(maxResultsSlider) - fireEvent.mouseUp(maxResultsSlider, { clientX: 50 }) - waitFor(() => { - expect(maxResultsSlider).toHaveValue(50) - }) - }) - - it('should ensure only 1 on the Hops item can be selected at once (default should be 1)', () => { - renderComponent() - - const directRelationshipCheckbox = screen.getByLabelText('Direct relationship') - const twoHopsAwayCheckbox = screen.getByLabelText('2 hops away') - - expect(directRelationshipCheckbox).toBeChecked() - expect(twoHopsAwayCheckbox).not.toBeChecked() - - fireEvent.click(twoHopsAwayCheckbox) - - expect(directRelationshipCheckbox).not.toBeChecked() - expect(twoHopsAwayCheckbox).toBeChecked() - }) - - it('should ensure the "View More" button works correctly', () => { - renderComponent() - - const viewMoreButton = screen.getByText('View More') - fireEvent.click(viewMoreButton) - - expect(mockSetShowAllSchemas).toHaveBeenCalledWith(true) - }) - - it('should ensure the "Clear" button works correctly', async () => { - renderComponent() - - const type1Pill = screen.getByText('Type1') - fireEvent.click(type1Pill) - - const clearButton = screen.getByText('Clear') - fireEvent.click(clearButton) - - waitFor(() => { - expect(screen.getByText('0')).toBeInTheDocument() - expect(type1Pill).toHaveStyle(`background: ${colors.BUTTON1_PRESS}`) - expect(type1Pill).toHaveStyle(`color: ${colors.white}`) - }) - }) - - it('should ensure the modal opens and closes correctly', () => { - const { rerender } = renderComponent(true) - - expect(screen.getByText('Type')).toBeInTheDocument() - - rerender( - - - - - , - ) - - expect(screen.queryByText('Type')).not.toBeInTheDocument() - }) - - it('should reset Source Nodes, Hops, and Max Results to default values when "Clear" button is clicked', async () => { - renderComponent() - - const type1Pill = screen.getByText('Type1') - fireEvent.click(type1Pill) - - const hopsCheckbox = screen.getByLabelText('2 hops away') - fireEvent.click(hopsCheckbox) - - const maxResultsSlider = screen.getByTestId('max-results-slider') - fireEvent.mouseDown(maxResultsSlider) - fireEvent.mouseUp(maxResultsSlider, { clientX: 50 }) - - const sourceNodesSlider = screen.getByTestId('source-nodes-slider') - fireEvent.mouseDown(sourceNodesSlider) - fireEvent.mouseUp(sourceNodesSlider, { clientX: 20 }) - - const clearButton = screen.getByText('Clear') - fireEvent.click(clearButton) - - waitFor(() => { - expect(screen.getByLabelText('Direct relationship')).toBeChecked() - expect(screen.getByTestId('max-results-slider')).toHaveValue(30) - expect(screen.getByTestId('source-nodes-slider')).toHaveValue(10) - }) - }) + // Add more specific tests as needed... }) From 5463402d99bae8cee0c6779417c7c4a4b3811341 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 17:22:09 +0300 Subject: [PATCH 5/6] feat: fix lint errors --- .../App/SideBar/FilterSearch/__tests__/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx index 051c2e162..cea9f97ec 100644 --- a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx +++ b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx @@ -44,8 +44,13 @@ describe('FilterSearch Component', () => { fetchData: mockFetchData, setAbortRequests: mockSetAbortRequests, }) - ;(useSchemaStore as jest.Mock).mockReturnValue([mockSchemaAll, jest.fn()]) - ;(useFeatureFlagStore as jest.Mock).mockReturnValue({ fastFiltersFeatureFlag: true }) + + // + ;(useSchemaStore as jest.Mock) + .mockReturnValue([mockSchemaAll, jest.fn()])(useFeatureFlagStore as jest.Mock) + .mockReturnValue({ fastFiltersFeatureFlag: true }) + + // ;(getSchemaAll as jest.Mock).mockResolvedValue({ schemas: mockSchemaAll }) }) From 7fa0b152229e41e82dfe90d1fe81ffb6eb819db3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=B0=D1=81=D1=83=D0=BB?= Date: Wed, 16 Oct 2024 17:45:19 +0300 Subject: [PATCH 6/6] feat: fix unit tests --- .../App/SideBar/FilterSearch/__tests__/index.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx index cea9f97ec..31d3fdd3f 100644 --- a/src/components/App/SideBar/FilterSearch/__tests__/index.tsx +++ b/src/components/App/SideBar/FilterSearch/__tests__/index.tsx @@ -32,9 +32,15 @@ const mockSetAnchorEl = jest.fn() const mockFetchData = jest.fn() const mockSetAbortRequests = jest.fn() const mockOnClose = jest.fn() -const mockSchemaAll = [{ type: 'Type1' }, { type: 'Type2' }, { type: 'Type3' }] + +jest.mock('~/stores/useSchemaStore', () => ({ + useSchemaStore: jest.fn(), +})) describe('FilterSearch Component', () => { + const mockSetSchemas = jest.fn() + const mockSchemaAll = [{ type: 'Type1' }, { type: 'Type2' }, { type: 'Type3' }] + beforeEach(() => { jest.clearAllMocks() @@ -46,9 +52,10 @@ describe('FilterSearch Component', () => { }) // - ;(useSchemaStore as jest.Mock) - .mockReturnValue([mockSchemaAll, jest.fn()])(useFeatureFlagStore as jest.Mock) - .mockReturnValue({ fastFiltersFeatureFlag: true }) + ;(useSchemaStore as jest.Mock).mockReturnValue([mockSchemaAll, mockSetSchemas]) // Return an array + + // + ;(useFeatureFlagStore as jest.Mock).mockReturnValue({ fastFiltersFeatureFlag: true }) // ;(getSchemaAll as jest.Mock).mockResolvedValue({ schemas: mockSchemaAll })