Abhinandan-Kushwaha / react-native-gifted-charts

The most complete library for Bar, Line, Area, Pie, Donut, Stacked Bar and Population Pyramid charts in React Native. Allows 2D, 3D, gradient, animations and live data updates.
https://www.npmjs.com/package/react-native-gifted-charts
MIT License
772 stars 152 forks source link

PieChart focusOnPress stops working when onPress prop is provided #564

Closed sclavijo93 closed 3 months ago

sclavijo93 commented 8 months ago

Description

When the onPress prop is uncommented and passed to the PieChart component, the focusOnPress functionality ceases to work. This means the pie chart section does not focus as expected when pressed. However, if the onPress prop is commented out, the focus functionality works correctly again. The desired outcome is for the focused pie chart section to update the center content based on the selected item, while also maintaining the visual focus effect.

Steps to reproduce:

  1. Implement a PieChart component with focusOnPress, data, and other required props.
  2. Provide an onPress handler function to the PieChart component to update the state with the selected item's data.
  3. Run the application and observe that pressing on a pie chart section updates the state but does not visually focus the pressed section.
  4. Comment out the onPress prop in the PieChart component.
  5. Observe that the focus functionality works correctly without the onPress prop.

Expected behavior:

When a section of the pie chart is pressed, the application should both focus on the pressed section visually and update the center content based on the selected item.

Example code ```tsx import React from 'react'; import { View } from 'react-native'; import { PieChart, pieDataItem } from 'react-native-gifted-charts'; import { SafeAreaView } from 'react-native-safe-area-context'; import { Card, Text } from '@atoms'; import styles from './styles'; import { ReportCreditLineGraphicScreenProps } from './types'; const ReportCreditLineGraphicScreen: React.FC< ReportCreditLineGraphicScreenProps > = () => { const [selectedData, setSelectedData] = React.useState({ value: 40, color: '#93FCF8', gradientCenterColor: '#3BE9DE', text: 'Good', }); const handlePieChartItemPress = (item: pieDataItem, index: number) => { setSelectedData(item); }; const pieData = [ { value: 47, color: '#009FFF', gradientCenterColor: '#006DFF', text: 'Excellent', }, { value: 40, color: '#93FCF8', gradientCenterColor: '#3BE9DE', focused: true, text: 'Good', }, { value: 16, color: '#BDB2FA', gradientCenterColor: '#8F80F3', text: 'Okay', }, { value: 3, color: '#FFA5BA', gradientCenterColor: '#FF7F97', text: 'Poor', }, ]; const renderDot = (color) => { return ( ); }; const renderLegendComponent = () => { return ( <> {renderDot('#006DFF')} Excellent: 47% {renderDot('#8F80F3')} Okay: 16% {renderDot('#3BE9DE')} Good: 40% {renderDot('#FF7F97')} Poor: 3% ); }; return ( Performance { return ( {selectedData?.value}% {selectedData?.text} ); }} // onPress={handlePieChartItemPress} /> {renderLegendComponent()} ); }; export default ReportCreditLineGraphicScreen; ```

Platform:

react-native: 0.71.16 react-native-gifted-charts version: 1.4.7

denlahodnyi commented 7 months ago

I have the same issue. onPress always returns a 0 index after selecting an already focused section. The index param is correct only for the newly selected section.

conorwaltersUno commented 6 months ago

+1 experiencing same issue

Tried using the labelOnPress but it is not registering this event at all with focusOnPress being passed in

cyril-apart commented 5 months ago

Hi,

i don't understand why the props.onPress() in src/PieChart/main.tsx (line 193) override the expected behaviour.

Here is a patch where expected behaviours works

react-native-gifted-charts+1.4.10.patch

christophby commented 5 months ago

For me adding zIndex: -1, to the <View> component react-native-gifted-charts/src/PieChart/index.tsx fixed it for Android (iOS was working before)

image

-> I created a PR #635

Abhinandan-Kushwaha commented 3 months ago

Hi @sclavijo93 👋 In your example, the focus is not working properly when used along with the onPress prop. This is because you are updating the state in the onPress event (through setSelectedData). This results in the re-render of the entire chart and setting the selectedIndex to -1.

You can fix the issue by making the pieData a state variable and updating the focused property in the pieData in the onPress event. Basically, you need to add the below code in your handlePieChartItemPress method-

setPieData([...pieData].map((item, ind) => ({...item, focused: ind===index})))

So your final code should be-

import React, {useState} from 'react';
import {View} from 'react-native';
import {PieChart, pieDataItem} from 'react-native-gifted-charts';
import {SafeAreaView} from 'react-native-safe-area-context';

import {Card, Text} from '@atoms';

import styles from './styles';
import {ReportCreditLineGraphicScreenProps} from './types';

const ReportCreditLineGraphicScreen: React.FC<
  ReportCreditLineGraphicScreenProps
> = () => {
  const [selectedData, setSelectedData] = React.useState<pieDataItem>({
    value: 40,
    color: '#93FCF8',
    gradientCenterColor: '#3BE9DE',
    text: 'Good',
  });

  const handlePieChartItemPress = (item: pieDataItem, index: number) => {
    setSelectedData(item);
    setPieData(
      [...pieData].map((item, ind) => ({...item, focused: ind === index})),
    );
  };

  const [pieData, setPieData] = useState([
    {
      value: 47,
      color: '#009FFF',
      gradientCenterColor: '#006DFF',
      text: 'Excellent',
    },
    {
      value: 40,
      color: '#93FCF8',
      gradientCenterColor: '#3BE9DE',
      focused: true,
      text: 'Good',
    },
    {
      value: 16,
      color: '#BDB2FA',
      gradientCenterColor: '#8F80F3',
      text: 'Okay',
    },
    {
      value: 3,
      color: '#FFA5BA',
      gradientCenterColor: '#FF7F97',
      text: 'Poor',
    },
  ]);

  const renderDot = color => {
    return (
      <View
        style={{
          height: 10,
          width: 10,
          borderRadius: 5,
          backgroundColor: color,
          marginRight: 10,
        }}
      />
    );
  };

  const renderLegendComponent = () => {
    return (
      <>
        <View
          style={{
            flexDirection: 'row',
            justifyContent: 'center',
            marginBottom: 10,
          }}>
          <View
            style={{
              flexDirection: 'row',
              alignItems: 'center',
              width: 120,
              marginRight: 20,
            }}>
            {renderDot('#006DFF')}
            <Text style={{color: 'white'}}>Excellent: 47%</Text>
          </View>
          <View
            style={{flexDirection: 'row', alignItems: 'center', width: 120}}>
            {renderDot('#8F80F3')}
            <Text style={{color: 'white'}}>Okay: 16%</Text>
          </View>
        </View>
        <View style={{flexDirection: 'row', justifyContent: 'center'}}>
          <View
            style={{
              flexDirection: 'row',
              alignItems: 'center',
              width: 120,
              marginRight: 20,
            }}>
            {renderDot('#3BE9DE')}
            <Text style={{color: 'white'}}>Good: 40%</Text>
          </View>
          <View
            style={{flexDirection: 'row', alignItems: 'center', width: 120}}>
            {renderDot('#FF7F97')}
            <Text style={{color: 'white'}}>Poor: 3%</Text>
          </View>
        </View>
      </>
    );
  };

  return (
    <View
      style={{
        paddingVertical: 100,
        backgroundColor: '#34448B',
        flex: 1,
      }}>
      <View
        style={{
          margin: 20,
          padding: 16,
          borderRadius: 20,
          backgroundColor: '#232B5D',
        }}>
        <Text style={{color: 'white', fontSize: 16, fontWeight: 'bold'}}>
          Performance
        </Text>
        <View style={{padding: 20, alignItems: 'center'}}>
          <PieChart
            focusOnPress
            data={pieData}
            donut
            showGradient
            sectionAutoFocus
            radius={90}
            innerRadius={60}
            innerCircleColor={'#232B5D'}
            centerLabelComponent={() => {
              return (
                <View style={{justifyContent: 'center', alignItems: 'center'}}>
                  <Text
                    style={{
                      fontSize: 22,
                      color: 'white',
                      fontWeight: 'bold',
                    }}>
                    {selectedData?.value}%
                  </Text>
                  <Text style={{fontSize: 14, color: 'white'}}>
                    {selectedData?.text}
                  </Text>
                </View>
              );
            }}
            // onPress={handlePieChartItemPress}
          />
        </View>
        {renderLegendComponent()}
      </View>
    </View>
  );
};

export default ReportCreditLineGraphicScreen;

The output of the above code is-

https://github.com/user-attachments/assets/3dbfcd35-4b03-411f-91d5-0adee393a808