diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..45157fff4f2026e7a3b5e2e1ec6ffc0db711570c --- /dev/null +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.spec.tsx @@ -0,0 +1,191 @@ +import React from 'react' +import { mount } from 'enzyme' +import { Provider } from 'react-redux' + +import configureStore from 'redux-mock-store' +import { + mockInitialChartState, + mockInitialEcolyoState, +} from '../../../tests/__mocks__/store' +import DataloadConsumptionVisualizer from './DataloadConsumptionVisualizer' +import { FluidType } from 'enum/fluid.enum' +import { DateTime } from 'luxon' +import { baseDataLoad } from '../../../tests/__mocks__/datachartData.mock' +import { Dataload } from 'models' +import { BrowserRouter as Router } from 'react-router-dom' +import UsageEventService from 'services/usageEvent.service' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +const mockStore = configureStore([]) +const mockChartStateLoaded = { ...mockInitialChartState, loading: false } +const emptyDataLoad = { ...baseDataLoad, value: -1 } +const dataLoadWithValueDetailEmpty: Dataload = { + ...baseDataLoad, + valueDetail: [], +} +const dataLoadWithValueDetail: Dataload = { + ...baseDataLoad, + valueDetail: [0.612216, 0.675242, 0.6779610000000003], +} + +describe('Dataload consumption visualizer component', () => { + it('should render correctly', () => { + const store = mockStore({ + ecolyo: mockInitialEcolyoState, + }) + const wrapper = mount( + <Provider store={store}> + <DataloadConsumptionVisualizer + fluidType={FluidType.MULTIFLUID} + dataload={baseDataLoad} + showCompare={false} + compareDataload={baseDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Provider> + ) + expect(wrapper).toMatchSnapshot() + }) + it('should render with single fluid', () => { + const store = mockStore({ + ecolyo: { + chart: mockChartStateLoaded, + }, + }) + const wrapper = mount( + <Provider store={store}> + <DataloadConsumptionVisualizer + fluidType={FluidType.ELECTRICITY} + dataload={baseDataLoad} + showCompare={false} + compareDataload={baseDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Provider> + ) + expect(wrapper).toMatchSnapshot() + }) + it('should render with no value to compare available', async () => { + const store = mockStore({ + ecolyo: { + chart: mockChartStateLoaded, + }, + }) + const wrapper = mount( + <Provider store={store}> + <DataloadConsumptionVisualizer + fluidType={FluidType.WATER} + dataload={baseDataLoad} + showCompare={true} + compareDataload={emptyDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Provider> + ) + expect(wrapper.find('.dataloadvisualizer-novalue').exists()).toBeTruthy() + }) + it('should render with water comparison data', async () => { + const store = mockStore({ + ecolyo: { + chart: mockChartStateLoaded, + }, + }) + const wrapper = mount( + <Provider store={store}> + <DataloadConsumptionVisualizer + fluidType={FluidType.WATER} + dataload={baseDataLoad} + showCompare={true} + compareDataload={baseDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Provider> + ) + expect(wrapper.find('.water-compare').exists()).toBeTruthy() + }) + it('should render multifluid with no compare and display estimation modal', async () => { + const store = mockStore({ + ecolyo: { + chart: mockChartStateLoaded, + }, + }) + + const wrapper = mount( + <Provider store={store}> + <DataloadConsumptionVisualizer + fluidType={FluidType.MULTIFLUID} + dataload={dataLoadWithValueDetailEmpty} + showCompare={false} + compareDataload={emptyDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Provider> + ) + expect( + wrapper + .find('.estimated') + .first() + .simulate('click') + ) + }) + it('should render multifluid with no compare and navigate to singleFluid page', async () => { + const store = mockStore({ + ecolyo: { + chart: mockChartStateLoaded, + }, + }) + const mockLoadToEuro = jest.fn() + jest.mock('services/converter.service', () => { + return jest.fn(() => { + return { + LoadToEuro: mockLoadToEuro, + } + }) + }) + + const wrapper = mount( + <Provider store={store}> + <Router> + <DataloadConsumptionVisualizer + fluidType={FluidType.MULTIFLUID} + dataload={dataLoadWithValueDetail} + showCompare={false} + compareDataload={emptyDataLoad} + lastDataDate={DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + })} + /> + </Router> + </Provider> + ) + jest.mock('services/usageEvent.service') + const mockAddEvent = jest.fn() + UsageEventService.addEvent = mockAddEvent + + //Render Navlinks to fluids + wrapper + .find('.dataloadvisualizer-euro-fluid') + .first() + .simulate('click') + expect(mockAddEvent).toHaveBeenCalled() + }) +}) diff --git a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx index 17493c2a34d164719f85d97eb7f4a487c4590fec..549df9a7dc6857a387daf13e8858afc163ee67df 100644 --- a/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx +++ b/src/components/ConsumptionVisualizer/DataloadConsumptionVisualizer.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useState } from 'react' import './dataloadConsumptionVisualizer.scss' import { useI18n } from 'cozy-ui/transpiled/react/I18n' import { useSelector } from 'react-redux' @@ -17,6 +17,7 @@ import Icon from 'cozy-ui/transpiled/react/Icon' import { useClient } from 'cozy-client' import { UsageEventType } from 'enum/usageEvent.enum' import UsageEventService from 'services/usageEvent.service' +import EstimatedConsumptionModal from './EstimatedConsumptionModal' interface DataloadConsumptionVisualizerProps { fluidType: FluidType @@ -35,6 +36,7 @@ const DataloadConsumptionVisualizer = ({ const { t } = useI18n() const { loading } = useSelector((state: AppStore) => state.ecolyo.chart) const client = useClient() + const [openEstimationModal, setOpenEstimationModal] = useState<boolean>(false) const converterService = new ConverterService() const fluidStyle = fluidType === FluidType.MULTIFLUID ? 'MULTIFLUID' : FluidType[fluidType] @@ -50,6 +52,10 @@ const DataloadConsumptionVisualizer = ({ }, [client] ) + const toggleEstimationModal = useCallback(() => { + setOpenEstimationModal(prev => !prev) + }, []) + return ( <div className="dataloadvisualizer-root"> {!loading && dataload && dataload.value > -1 ? ( @@ -110,9 +116,17 @@ const DataloadConsumptionVisualizer = ({ ) : ( <> {formatNumberValues(dataload.value)} - <span className="text-18-normal"> + <sup className="text-18-normal euroUnit"> {`${t('FLUID.' + fluidStyle + '.UNIT')}`} - </span> + </sup> + {multiFluid && ( + <sup + className="text-14-normal estimated" + onClick={toggleEstimationModal} + > + {t('consumption_visualizer.estimated')} + </sup> + )} </> )} </div> @@ -214,6 +228,10 @@ const DataloadConsumptionVisualizer = ({ )} </> )} + <EstimatedConsumptionModal + open={openEstimationModal} + handleCloseClick={toggleEstimationModal} + /> </div> ) } diff --git a/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.spec.tsx b/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.spec.tsx new file mode 100644 index 0000000000000000000000000000000000000000..12f759dc55119d8b30ae673e2f712832d2745a5f --- /dev/null +++ b/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.spec.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import { mount } from 'enzyme' +import { Provider } from 'react-redux' + +import configureStore from 'redux-mock-store' +import { mockInitialEcolyoState } from '../../../tests/__mocks__/store' +import EstimatedConsumptionModal from './EstimatedConsumptionModal' + +jest.mock('cozy-ui/transpiled/react/I18n', () => { + return { + useI18n: jest.fn(() => { + return { + t: (str: string) => str, + } + }), + } +}) + +const mockStore = configureStore([]) +const mockHandleClose = jest.fn() +describe('EstimatedConsumptionModal component', () => { + it('should render correctly', () => { + const store = mockStore({ + ecolyo: mockInitialEcolyoState, + }) + const wrapper = mount( + <Provider store={store}> + <EstimatedConsumptionModal + open={true} + handleCloseClick={mockHandleClose} + /> + </Provider> + ) + expect(wrapper).toMatchSnapshot() + }) +}) diff --git a/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.tsx b/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.tsx new file mode 100644 index 0000000000000000000000000000000000000000..04f81e6284d171f5fbef6162fcd76a75f20227de --- /dev/null +++ b/src/components/ConsumptionVisualizer/EstimatedConsumptionModal.tsx @@ -0,0 +1,82 @@ +import React from 'react' +import { useI18n } from 'cozy-ui/transpiled/react/I18n' +import Dialog from '@material-ui/core/Dialog' +import { FluidType } from 'enum/fluid.enum' +import CloseIcon from 'assets/icons/ico/close.svg' +import Icon from 'cozy-ui/transpiled/react/Icon' +import { IconButton } from '@material-ui/core' +import './estimatedConsumptionModal.scss' + +interface EstimatedConsumptionModalProps { + open: boolean + handleCloseClick: () => void +} + +const EstimatedConsumptionModal: React.FC<EstimatedConsumptionModalProps> = ({ + open, + handleCloseClick, +}: EstimatedConsumptionModalProps) => { + const { t } = useI18n() + + return ( + <Dialog + open={open} + onClose={handleCloseClick} + aria-labelledby={'accessibility-title'} + classes={{ + root: 'modal-root', + paper: 'modal-paper', + }} + > + <div id={'accessibility-title'}> + {t('consumption_visualizer.modal.window_title')} + </div> + <IconButton + aria-label={t('consumption_visualizer.modal.close')} + className="modal-paper-close-button" + onClick={handleCloseClick} + > + <Icon icon={CloseIcon} size={16} /> + </IconButton> + <div className="estimation-modal"> + <div className="text-20-normal modal-title"> + {t('consumption_visualizer.modal.title')} + </div> + <div className="text-16-normal"> + {t('consumption_visualizer.modal.part1')} + </div> + <br /> + <div className="text-16-normal"> + {t('consumption_visualizer.modal.part2')} + </div> + <ul> + <li> + <span + className={`${FluidType[FluidType.ELECTRICITY].toLowerCase()}`} + > + {t(`FLUID.${FluidType[FluidType.ELECTRICITY]}.LABEL`)} + </span> + {t('consumption_visualizer.modal.list1')} + </li> + <li> + <span className={`${FluidType[FluidType.GAS].toLowerCase()}`}> + {t(`FLUID.${FluidType[FluidType.GAS]}.LABEL`)} + </span> + {t('consumption_visualizer.modal.list2')} + </li> + <li> + <span className={`${FluidType[FluidType.WATER].toLowerCase()}`}> + {t(`FLUID.${FluidType[FluidType.WATER]}.LABEL`)} + </span> + {t('consumption_visualizer.modal.list3')} + </li> + </ul> + <div className="text-16-normal"> + {t('consumption_visualizer.modal.part3')} + </div> + </div> + </Dialog> + ) +} + +export default EstimatedConsumptionModal diff --git a/src/components/ConsumptionVisualizer/__snapshots__/DataloadConsumptionVisualizer.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/DataloadConsumptionVisualizer.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..e3a2075d632b0ac68586d2d92b6c0711aab83ec2 --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/DataloadConsumptionVisualizer.spec.tsx.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dataload consumption visualizer component should render correctly 1`] = `ReactWrapper {}`; + +exports[`Dataload consumption visualizer component should render with single fluid 1`] = `ReactWrapper {}`; diff --git a/src/components/ConsumptionVisualizer/__snapshots__/EstimatedConsumptionModal.spec.tsx.snap b/src/components/ConsumptionVisualizer/__snapshots__/EstimatedConsumptionModal.spec.tsx.snap new file mode 100644 index 0000000000000000000000000000000000000000..70af07cd53f5d83d0e2ed9c6d147ed2c7c74b50e --- /dev/null +++ b/src/components/ConsumptionVisualizer/__snapshots__/EstimatedConsumptionModal.spec.tsx.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EstimatedConsumptionModal component should render correctly 1`] = `ReactWrapper {}`; diff --git a/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss b/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss index 8bbae91de29b9039cf72feb6ebee8d0fbbc48764..e61efb30a7159d75c93c7aa6dcd8fc423c0ba239 100644 --- a/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss +++ b/src/components/ConsumptionVisualizer/dataloadConsumptionVisualizer.scss @@ -33,6 +33,19 @@ align-self: flex-end; margin-left: 0.5em; } + .euroUnit { + margin-left: 0.4em; + position: relative; + top: -12px; + } + .estimated { + cursor: pointer; + font-weight: 500; + text-decoration: underline; + margin-left: 0.5rem; + position: relative; + top: -12px; + } } .electricity { color: $elec-color; diff --git a/src/components/ConsumptionVisualizer/estimatedConsumptionModal.scss b/src/components/ConsumptionVisualizer/estimatedConsumptionModal.scss new file mode 100644 index 0000000000000000000000000000000000000000..612e26b271b22a8ed0dcee6854b4e988acee99b8 --- /dev/null +++ b/src/components/ConsumptionVisualizer/estimatedConsumptionModal.scss @@ -0,0 +1,24 @@ +@import 'src/styles/base/color'; + +.estimation-modal { + padding: 0.5rem; + color: $grey-bright; + .modal-title { + color: $gold-shadow; + font-weight: bold; + text-align: center; + margin-bottom: 0.5rem; + } + ul { + padding-left: 1rem; + } + .electricity { + color: $elec-color; + } + .water { + color: $water-color; + } + .gas { + color: $gas-color; + } +} diff --git a/src/locales/fr.json b/src/locales/fr.json index 54aa6dadd3b949d7f2d6ab67b5090c4803b25307..05962015123d3036ad3901278724bf39197daf82 100644 --- a/src/locales/fr.json +++ b/src/locales/fr.json @@ -238,7 +238,19 @@ "no_data": "Pas de données", "last_data": "Dernières données", "last_valid_data": "Dernières données valides", - "data_to_come": "à venir" + "data_to_come": "à venir", + "estimated": "estimés", + "modal": { + "window_title": "info estimation des prix", + "title": "Comment sont estimés les prix ?", + "close": "Fermer la fenêtre", + "part1": "Le prix de votre électricité ou de votre gaz dépend de l'offre choisie auprès de votre fournisseur. N’ayant pas accès à cette information, Ecolyo affiche un prix moyen par fluide. ", + "part2": "Tarifs moyens retenus :", + "list1": " : 1 kWh = 0,1546 €TTC (tarif réglementé d’EDF au 01/02/2020 pour une puissance souscrite de 3 ou 6 kVA et hors offre heure pleine/creuse)", + "list2": " : 1 kWh = 0,0793 €TTC (tarif réglementé de vente au 01/12/2019 pour un consommateur soutirant moins de 6 MWh par an - usages cuisson ou mixte)", + "list3": " : 1 litre d’eau = 0,0031 € TTC (prix de l'eau par Eau du Grand Lyon)", + "part3": "Le coût de votre abonnement et des taxes (pouvant représenter jusqu'à 66% de votre facture) n’est pas affiché dans Ecolyo." + } }, "duel": { "global_error": "Oups. Une erreur est survenue. Veuillez retourner à l'écran d’accueil des défis", diff --git a/tests/__mocks__/datachartData.mock.ts b/tests/__mocks__/datachartData.mock.ts index f39c12c163c03822a58d640503f16df420f04af6..53627571cae7fb30a28dac0bfa2e574bd295826b 100644 --- a/tests/__mocks__/datachartData.mock.ts +++ b/tests/__mocks__/datachartData.mock.ts @@ -1,4 +1,4 @@ -import { Datachart } from 'models' +import { Datachart, Dataload } from 'models' import { DateTime } from 'luxon' export const graphData: Datachart = { @@ -49,6 +49,13 @@ export const graphData: Datachart = { }, ], } +export const baseDataLoad: Dataload = { + date: DateTime.fromISO('2021-09-23T00:00:00.000Z', { + zone: 'utc', + }), + value: 12, + valueDetail: null, +} export const graphMonthData: Datachart = { actualData: [