From bc1d8e743e75dab7c809557d4b65914030209182 Mon Sep 17 00:00:00 2001 From: Bastien DUMONT <bdumont@grandlyon.com> Date: Thu, 4 Apr 2024 13:21:19 +0000 Subject: [PATCH] refactor: remove prop drilling --- .../Consumption/ConsumptionView.tsx | 13 +-- .../ConsumptionVisualizer.tsx | 10 +- .../DataloadConsumptionVisualizer.spec.tsx | 9 -- .../DataloadConsumptionVisualizer.tsx | 8 +- .../DataloadNoValue.spec.tsx | 98 +++++++++++-------- .../ConsumptionVisualizer/DataloadNoValue.tsx | 18 ++-- src/components/FluidChart/FluidChart.tsx | 9 +- src/components/FluidChart/FluidChartSlide.tsx | 4 +- src/components/FluidChart/FluidChartSwipe.tsx | 8 +- .../Konnector/KonnectorViewerCard.tsx | 30 +++--- src/models/chart.model.ts | 2 + src/store/chart/chart.slice.spec.ts | 14 +++ src/store/chart/chart.slice.ts | 5 + tests/__mocks__/store/chart.state.mock.ts | 1 + 14 files changed, 109 insertions(+), 120 deletions(-) diff --git a/src/components/Consumption/ConsumptionView.tsx b/src/components/Consumption/ConsumptionView.tsx index 6fcb13418..ea5922bba 100644 --- a/src/components/Consumption/ConsumptionView.tsx +++ b/src/components/Consumption/ConsumptionView.tsx @@ -51,7 +51,6 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { releaseNotes.show ) - const [active, setActive] = useState<boolean>(false) const [openExpiredConsentModal, setOpenExpiredConsentModal] = useState<boolean>(true) const [consentExpiredFluids, setConsentExpiredFluids] = useState<FluidType[]>( @@ -229,19 +228,12 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { )} {showOfflineData && ( <> - <FluidChart - fluidType={fluidType} - setActive={setActive} - key={lastDataDateKey} - /> + <FluidChart fluidType={fluidType} key={lastDataDateKey} /> <ConsumptionDetails fluidType={fluidType} /> {!isMulti && ( <KonnectorViewerCard fluidType={fluidType} - isDisconnected={false} showOfflineData={true} - setActive={setActive} - active={active} key={fluidType} /> )} @@ -254,10 +246,7 @@ const ConsumptionView = ({ fluidType }: { fluidType: FluidType }) => { ) : ( <KonnectorViewerCard fluidType={fluidType} - isDisconnected={true} showOfflineData={false} - setActive={setActive} - active={active} /> )} </div> diff --git a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx index 3abf83ce6..ab0e5d35e 100644 --- a/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/ConsumptionVisualizer.tsx @@ -7,14 +7,7 @@ import { useAppSelector } from 'store/hooks' import InfoDataConsumptionVisualizer from './InfoDataConsumptionVisualizer' import './consumptionVisualizer.scss' -interface ConsumptionVisualizerProps { - fluidType: FluidType - setActive: React.Dispatch<React.SetStateAction<boolean>> -} -const ConsumptionVisualizer = ({ - fluidType, - setActive, -}: ConsumptionVisualizerProps) => { +const ConsumptionVisualizer = ({ fluidType }: { fluidType: FluidType }) => { const { chart: { currentDatachart, currentDatachartIndex }, global: { fluidStatus, fluidTypes }, @@ -53,7 +46,6 @@ const ConsumptionVisualizer = ({ fluidType={fluidType} dataload={dataload} compareDataload={compareDataload} - setActive={setActive} /> <div className="consumptionvisualizer-info"> <InfoDataConsumptionVisualizer diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx index b3c3b46ea..e0afcdfbd 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx @@ -51,7 +51,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.ELECTRICITY} dataload={baseDataLoad} compareDataload={baseDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -65,7 +64,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.ELECTRICITY} dataload={null as unknown as Dataload} compareDataload={baseDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -81,7 +79,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.ELECTRICITY} dataload={{ ...baseDataLoad, date: DateTime.local(9999, 1, 1) }} compareDataload={baseDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -99,7 +96,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.ELECTRICITY} dataload={{ ...baseDataLoad, state: DataloadState.MISSING }} compareDataload={baseDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -116,7 +112,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.MULTIFLUID} dataload={baseMultiFluidDataLoad} compareDataload={null} - setActive={jest.fn()} /> </BrowserRouter> </Provider> @@ -135,7 +130,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.WATER} dataload={baseDataLoad} compareDataload={emptyDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -154,7 +148,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.WATER} dataload={baseDataLoad} compareDataload={baseDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -168,7 +161,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.MULTIFLUID} dataload={dataLoadWithValueDetailEmpty} compareDataload={emptyDataLoad} - setActive={jest.fn()} /> </Provider> ) @@ -191,7 +183,6 @@ describe('Dataload consumption visualizer component', () => { fluidType={FluidType.MULTIFLUID} dataload={dataLoadWithValueDetail} compareDataload={emptyDataLoad} - setActive={jest.fn()} /> </BrowserRouter> </Provider> diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx index b7bc960a3..e28a077ee 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx @@ -12,13 +12,11 @@ interface DataloadConsumptionVisualizerProps { fluidType: FluidType dataload: Dataload compareDataload: Dataload | null - setActive: React.Dispatch<React.SetStateAction<boolean>> } const DataloadConsumptionVisualizer = ({ fluidType, dataload, compareDataload, - setActive, }: DataloadConsumptionVisualizerProps) => { const { showCompare } = useAppSelector(state => state.ecolyo.chart) const [openEstimationModal, setOpenEstimationModal] = useState<boolean>(false) @@ -39,11 +37,7 @@ const DataloadConsumptionVisualizer = ({ ) { return ( <div className="dataloadvisualizer-root"> - <DataloadNoValue - dataload={dataload} - setActive={setActive} - fluidType={fluidType} - /> + <DataloadNoValue dataload={dataload} fluidType={fluidType} /> </div> ) } diff --git a/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx b/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx index 404530032..4545f197f 100644 --- a/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx +++ b/src/components/ConsumptionVisualizer/DataloadNoValue.spec.tsx @@ -2,17 +2,30 @@ import { render, screen } from '@testing-library/react' import { userEvent } from '@testing-library/user-event' import { DataloadState, FluidType } from 'enums' import React from 'react' +import { Provider } from 'react-redux' +import * as storeHooks from 'store/hooks' import { baseDataLoad } from 'tests/__mocks__/chartData.mock' +import { createMockEcolyoStore } from 'tests/__mocks__/store' import DataloadNoValue from './DataloadNoValue' +const mockDispatch = jest.fn() +jest.spyOn(storeHooks, 'useAppDispatch').mockImplementation(() => mockDispatch) + describe('DataloadNoValue component', () => { + const store = createMockEcolyoStore() + + beforeEach(() => { + jest.clearAllMocks() + }) + it('should render correctly', () => { const { container } = render( - <DataloadNoValue - dataload={baseDataLoad} - fluidType={FluidType.ELECTRICITY} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={baseDataLoad} + fluidType={FluidType.ELECTRICITY} + /> + </Provider> ) expect(container).toMatchSnapshot() }) @@ -21,11 +34,12 @@ describe('DataloadNoValue component', () => { it('case state EMPTY', () => { const mockDataLoad = { ...baseDataLoad, state: DataloadState.EMPTY } render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.ELECTRICITY} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={mockDataLoad} + fluidType={FluidType.ELECTRICITY} + /> + </Provider> ) expect( screen.getByText('consumption_visualizer.no_data') @@ -34,11 +48,12 @@ describe('DataloadNoValue component', () => { it('case state HOLE', () => { const mockDataLoad = { ...baseDataLoad, state: DataloadState.HOLE } render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.ELECTRICITY} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={mockDataLoad} + fluidType={FluidType.ELECTRICITY} + /> + </Provider> ) expect( screen.getByText('consumption_visualizer.no_data') @@ -50,11 +65,12 @@ describe('DataloadNoValue component', () => { state: DataloadState.AGGREGATED_EMPTY, } render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.MULTIFLUID} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={mockDataLoad} + fluidType={FluidType.MULTIFLUID} + /> + </Provider> ) expect( screen.getByText('consumption_visualizer.no_data') @@ -66,11 +82,12 @@ describe('DataloadNoValue component', () => { it('case state MISSING', () => { const mockDataLoad = { ...baseDataLoad, state: DataloadState.MISSING } render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.ELECTRICITY} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={mockDataLoad} + fluidType={FluidType.ELECTRICITY} + /> + </Provider> ) expect( screen.getByText('consumption_visualizer.missing_data') @@ -82,11 +99,12 @@ describe('DataloadNoValue component', () => { state: DataloadState.AGGREGATED_HOLE_OR_MISSING, } render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.MULTIFLUID} - setActive={jest.fn()} - /> + <Provider store={store}> + <DataloadNoValue + dataload={mockDataLoad} + fluidType={FluidType.MULTIFLUID} + /> + </Provider> ) expect( screen.getByText('consumption_visualizer.missing_data') @@ -94,17 +112,19 @@ describe('DataloadNoValue component', () => { }) }) - it('should call setActive when missing message is clicked', async () => { - const mockSetActive = jest.fn() - const mockDataLoad = { ...baseDataLoad, state: DataloadState.MISSING } + it('should call dispatch to show konnector details when missing message is clicked', async () => { render( - <DataloadNoValue - dataload={mockDataLoad} - fluidType={FluidType.ELECTRICITY} - setActive={mockSetActive} - /> + <Provider store={store}> + <DataloadNoValue + dataload={{ ...baseDataLoad, state: DataloadState.MISSING }} + fluidType={FluidType.ELECTRICITY} + /> + </Provider> ) await userEvent.click(screen.getByRole('button')) - expect(mockSetActive).toHaveBeenCalledWith(true) + expect(mockDispatch).toHaveBeenCalledWith({ + type: 'chart/setShowConnectionDetails', + payload: true, + }) }) }) diff --git a/src/components/ConsumptionVisualizer/DataloadNoValue.tsx b/src/components/ConsumptionVisualizer/DataloadNoValue.tsx index 3c55e9ed0..77b90b3d4 100644 --- a/src/components/ConsumptionVisualizer/DataloadNoValue.tsx +++ b/src/components/ConsumptionVisualizer/DataloadNoValue.tsx @@ -3,23 +3,21 @@ import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { DataloadState, FluidType } from 'enums' import { Dataload } from 'models' import React, { useCallback } from 'react' +import { setShowConnectionDetails } from 'store/chart/chart.slice' +import { useAppDispatch } from 'store/hooks' import './consumptionVisualizer.scss' interface DataloadNoValueProps { dataload: Dataload fluidType: FluidType - setActive: React.Dispatch<React.SetStateAction<boolean>> } -const DataloadNoValue = ({ - dataload, - fluidType, - setActive, -}: DataloadNoValueProps) => { +const DataloadNoValue = ({ dataload, fluidType }: DataloadNoValueProps) => { const { t } = useI18n() + const dispatch = useAppDispatch() - const handleToggleKonnectionCard = useCallback(() => { - setActive(true) + const handleToggleKonnectorCard = useCallback(() => { + dispatch(setShowConnectionDetails(true)) const app = document.querySelector('.app-content') const content = document.querySelector('.content-view') if (content && app) { @@ -37,7 +35,7 @@ const DataloadNoValue = ({ }) }, 300) } - }, [setActive]) + }, [dispatch]) if ( dataload.state === DataloadState.EMPTY || @@ -65,7 +63,7 @@ const DataloadNoValue = ({ ) { return ( <Button - onClick={handleToggleKonnectionCard} + onClick={handleToggleKonnectorCard} classes={{ root: 'btnText', label: 'text-22-normal' }} > {t('consumption_visualizer.missing_data')} diff --git a/src/components/FluidChart/FluidChart.tsx b/src/components/FluidChart/FluidChart.tsx index 2b5c2e45a..2e87f46b2 100644 --- a/src/components/FluidChart/FluidChart.tsx +++ b/src/components/FluidChart/FluidChart.tsx @@ -25,12 +25,7 @@ import HalfHourUpcoming from './HalfHourUpcoming/HalfHourUpcoming' import TimeStepSelector from './TimeStepSelector/TimeStepSelector' import './fluidChart.scss' -interface FluidChartProps { - fluidType: FluidType - setActive: React.Dispatch<React.SetStateAction<boolean>> -} - -const FluidChart = ({ fluidType, setActive }: FluidChartProps) => { +const FluidChart = ({ fluidType }: { fluidType: FluidType }) => { const { t } = useI18n() const client = useClient() const { @@ -166,7 +161,7 @@ const FluidChart = ({ fluidType, setActive }: FluidChartProps) => { return ( <> <div className="fluidchart-content"> - <FluidChartSwipe fluidType={fluidType} setActive={setActive} /> + <FluidChartSwipe fluidType={fluidType} /> </div> {showCompare && currentTimeStep !== TimeStep.YEAR && ( <Slide direction="right" in={showCompare}> diff --git a/src/components/FluidChart/FluidChartSlide.tsx b/src/components/FluidChart/FluidChartSlide.tsx index 2bae0adb8..f63e06da3 100644 --- a/src/components/FluidChart/FluidChartSlide.tsx +++ b/src/components/FluidChart/FluidChartSlide.tsx @@ -18,7 +18,6 @@ interface FluidChartSlideProps { width: number height: number isSwitching: boolean - setActive: React.Dispatch<React.SetStateAction<boolean>> } const FluidChartSlide = ({ @@ -27,7 +26,6 @@ const FluidChartSlide = ({ width, height, isSwitching, - setActive, }: FluidChartSlideProps) => { const client = useClient() const dispatch = useAppDispatch() @@ -119,7 +117,7 @@ const FluidChartSlide = ({ </div> ) : ( <> - <ConsumptionVisualizer fluidType={fluidType} setActive={setActive} /> + <ConsumptionVisualizer fluidType={fluidType} /> <BarChart chartData={chartData} fluidType={fluidType} diff --git a/src/components/FluidChart/FluidChartSwipe.tsx b/src/components/FluidChart/FluidChartSwipe.tsx index 0e5b1693b..934e86468 100644 --- a/src/components/FluidChart/FluidChartSwipe.tsx +++ b/src/components/FluidChart/FluidChartSwipe.tsx @@ -12,12 +12,7 @@ import './fluidChartSwipe.scss' const VirtualizeSwipeableViews = virtualize(SwipeableViews) -interface FluidChartSwipeProps { - fluidType: FluidType - setActive: React.Dispatch<React.SetStateAction<boolean>> -} - -const FluidChartSwipe = ({ fluidType, setActive }: FluidChartSwipeProps) => { +const FluidChartSwipe = ({ fluidType }: { fluidType: FluidType }) => { const dispatch = useAppDispatch() const { currentIndex, currentTimeStep, selectedDate } = useAppSelector( state => state.ecolyo.chart @@ -76,7 +71,6 @@ const FluidChartSwipe = ({ fluidType, setActive }: FluidChartSwipeProps) => { width={width} height={height} isSwitching={isSwitching} - setActive={setActive} /> ) diff --git a/src/components/Konnector/KonnectorViewerCard.tsx b/src/components/Konnector/KonnectorViewerCard.tsx index 9c0de9fd0..14f9c4f71 100644 --- a/src/components/Konnector/KonnectorViewerCard.tsx +++ b/src/components/Konnector/KonnectorViewerCard.tsx @@ -42,7 +42,11 @@ import DateChartService from 'services/dateChart.service' import FluidService from 'services/fluid.service' import PartnersInfoService from 'services/partnersInfo.service' import { setChallengeConsumption } from 'store/challenge/challenge.slice' -import { setSelectedDate, setShowOfflineData } from 'store/chart/chart.slice' +import { + setSelectedDate, + setShowConnectionDetails, + setShowOfflineData, +} from 'store/chart/chart.slice' import { setFluidStatus, setLastEpglLogin, @@ -57,25 +61,20 @@ import ConnectionResult from './ConnectionResult/ConnectionResult' import './konnectorViewerCard.scss' interface KonnectorViewerCardProps { - isDisconnected: boolean showOfflineData: boolean - active: boolean fluidType: FluidType - setActive: React.Dispatch<React.SetStateAction<boolean>> } const KonnectorViewerCard = ({ - isDisconnected, showOfflineData, - active, fluidType, - setActive, }: KonnectorViewerCardProps) => { const { t } = useI18n() const client = useClient() - const dispatch = useAppDispatch() const navigate = useNavigate() + const dispatch = useAppDispatch() const { + chart: { showConnectionDetails }, challenge: { currentChallenge }, global: { fluidStatus, shouldRefreshConsent, partnersInfo }, } = useAppSelector(state => state.ecolyo) @@ -107,7 +106,7 @@ const KonnectorViewerCard = ({ const iconType = getParamPicto(currentFluidStatus.fluidType) const toggleAccordion = () => { - setActive(prev => !prev) + dispatch(setShowConnectionDetails(!showConnectionDetails)) } const updateGlobalFluidStatus = useCallback(async (): Promise< @@ -159,11 +158,10 @@ const KonnectorViewerCard = ({ const updatedFluidStatus = await fluidService.getFluidStatus(partnersInfo) dispatch(setFluidStatus(updatedFluidStatus)) } - setActive(false) + dispatch(setShowConnectionDetails(false)) }, [ refreshChallengeState, updateGlobalFluidStatus, - setActive, partnersInfoService, fluidService, dispatch, @@ -215,8 +213,7 @@ const KonnectorViewerCard = ({ await fluidService.getFluidStatus(partnersInfo) dispatch(setFluidStatus(updatedFluidStatus)) } - - setActive(false) + dispatch(setShowConnectionDetails(false)) setOpenModal(false) // TODO null state seems to be read before modal closing and display a success icon in modal setKonnectorState(null) @@ -228,7 +225,6 @@ const KonnectorViewerCard = ({ currentFluidStatus, isUpdating, fluidType, - setActive, fluidSlug, client, handleAccountDeletion, @@ -490,12 +486,12 @@ const KonnectorViewerCard = ({ return ( <div className="konnector-section-root"> - {isDisconnected && ( + {!showOfflineData && ( <AccordionDetails>{getConnectionCard()}</AccordionDetails> )} - {!isDisconnected && ( + {showOfflineData && ( <Accordion - expanded={active} + expanded={showConnectionDetails} onChange={toggleAccordion} classes={{ root: `expansion-panel-root ${ diff --git a/src/models/chart.model.ts b/src/models/chart.model.ts index b9cfb1b97..b0e24c8de 100644 --- a/src/models/chart.model.ts +++ b/src/models/chart.model.ts @@ -10,4 +10,6 @@ export interface ChartState { selectedDate: DateTime showCompare: boolean showOfflineData: boolean + /** For KonnectorViewerCard Accordion */ + showConnectionDetails: boolean } diff --git a/src/store/chart/chart.slice.spec.ts b/src/store/chart/chart.slice.spec.ts index 0ab8ecdd7..02f68b639 100644 --- a/src/store/chart/chart.slice.spec.ts +++ b/src/store/chart/chart.slice.spec.ts @@ -10,6 +10,7 @@ import { setCurrentTimeStep, setSelectedDate, setShowCompare, + setShowConnectionDetails, } from './chart.slice' describe('chart reducer', () => { @@ -108,4 +109,17 @@ describe('chart reducer', () => { }) }) }) + + describe('setShowConnectionDetails', () => { + it('should handle setShowConnectionDetails', () => { + const state = chartSlice.reducer( + mockChartState, + setShowConnectionDetails(true) + ) + expect(state).toEqual({ + ...mockChartState, + showConnectionDetails: true, + }) + }) + }) }) diff --git a/src/store/chart/chart.slice.ts b/src/store/chart/chart.slice.ts index 92bec2bdd..558e752a3 100644 --- a/src/store/chart/chart.slice.ts +++ b/src/store/chart/chart.slice.ts @@ -13,6 +13,7 @@ const initialState: ChartState = { currentDatachartIndex: 0, showCompare: false, showOfflineData: false, + showConnectionDetails: false, } export const chartSlice = createSlice({ @@ -43,6 +44,9 @@ export const chartSlice = createSlice({ setShowOfflineData: (state, action: PayloadAction<boolean>) => { state.showOfflineData = action.payload }, + setShowConnectionDetails: (state, action: PayloadAction<boolean>) => { + state.showConnectionDetails = action.payload + }, }, }) @@ -54,4 +58,5 @@ export const { setSelectedDate, setShowCompare, setShowOfflineData, + setShowConnectionDetails, } = chartSlice.actions diff --git a/tests/__mocks__/store/chart.state.mock.ts b/tests/__mocks__/store/chart.state.mock.ts index 337d9d464..76b75c599 100644 --- a/tests/__mocks__/store/chart.state.mock.ts +++ b/tests/__mocks__/store/chart.state.mock.ts @@ -12,4 +12,5 @@ export const mockChartState: ChartState = { currentDatachartIndex: 0, showCompare: false, showOfflineData: false, + showConnectionDetails: false, } -- GitLab