<commit_before>import React, { useContext, useEffect, useRef, useState } from 'react';

import { ThemeContext } from 'styled-components';

import { normalizeColor, parseMetricToNum } from '../../utils';

import { StyledChart } from './StyledChart';
import { normalizeBounds, normalizeValues } from './utils';

const defaultSize = { height: 'small', width: 'medium' };

const Chart = React.forwardRef(
  (
    {
      bounds: propsBounds,
      color = 'accent-1',
      gap,
      justify = 'between',
      onClick,
      onHover,
      overflow = false,
      round,
      size: propsSize = defaultSize,
      thickness = 'medium',
      type = 'bar',
      values: propsValues = [],
      ...rest
    },
    ref,
  ) => {
    const theme = useContext(ThemeContext);
    const [values, setValues] = useState([]);
    const [bounds, setBounds] = useState([[0, 0], [0, 0]]);
    const [containerSize, setContainerSize] = useState([0, 0]);
    const [size, setSize] = useState([0, 0]);
    const [scale, setScale] = useState([1, 1]);
    const [strokeWidth, setStrokeWidth] = useState(0);
    const containerRef = ref || useRef();

    // calculations
    useEffect(() => {
      const nextValues = normalizeValues(propsValues);
      setValues(nextValues);

      const nextBounds = normalizeBounds(propsBounds, nextValues);
      setBounds(nextBounds);

      const nextStrokeWidth = parseMetricToNum(
        theme.global.edgeSize[thickness] || thickness,
      );
      setStrokeWidth(nextStrokeWidth);

      const gapWidth = gap
        ? parseMetricToNum(theme.global.edgeSize[gap] || gap)
        : nextStrokeWidth;

      // autoWidth is how wide we'd pefer
      const autoWidth =
        nextStrokeWidth * nextValues.length +
        (nextValues.length - 1) * gapWidth;

      const sizeWidth =
        typeof propsSize === 'string'
          ? propsSize
          : propsSize.width || defaultSize.width;
      let width;
      if (sizeWidth === 'full') {
        [width] = containerSize;
      } else if (sizeWidth === 'auto') {
        width = autoWidth;
      } else {
        width = parseMetricToNum(theme.global.size[sizeWidth] || sizeWidth);
      }

      const sizeHeight =
        typeof propsSize === 'string'
          ? propsSize
          : propsSize.height || defaultSize.height;
      let height;
      if (sizeHeight === 'full') {
        [, height] = containerSize;
      } else {
        height = parseMetricToNum(theme.global.size[sizeHeight] || sizeHeight);
      }

      setSize([width, height]);

      const nextScale = [
        (sizeWidth === 'auto' ? autoWidth : width) /
          (nextBounds[0][1] - nextBounds[0][0]),
        height / (nextBounds[1][1] - nextBounds[1][0]),
      ];
      setScale(nextScale);
    }, [
      containerSize,
      gap,
      justify,
      propsBounds,
      propsSize,
      propsValues,
      theme.global.edgeSize,
      theme.global.size,
      thickness,
    ]);

    // container size, if needed
    useEffect(() => {
      const onResize = () => {
        const containerNode = containerRef.current;
        if (containerNode) {
          const { parentNode } = containerNode;
          if (parentNode) {
            const rect = parentNode.getBoundingClientRect();
            setContainerSize([rect.width, rect.height]);
          }
        }
      };

      if (propsSize.width === 'full' || propsSize.height === 'full') {
        window.addEventListener('resize', onResize);
        onResize();
        return () => window.removeEventListener('resize', onResize);
      }
      return undefined;
    }, [containerRef, propsSize]);

    const renderBars = () =>
      (values || []).map((valueArg, index) => {
        const { label, onHover: valueOnHover, value, ...valueRest } = valueArg;

        const key = `p-${index}`;
        const bottom = value.length === 2 ? bounds[1][0] : value[1];
        const top = value.length === 2 ? value[1] : value[2];
        if (top !== 0) {
          const d =
            `M ${(value[0] - bounds[0][0]) * scale[0]},` +
            `${size[1] - (bottom - bounds[1][0]) * scale[1]}` +
            ` L ${(value[0] - bounds[0][0]) * scale[0]},` +
            `${size[1] - (top - bounds[1][0]) * scale[1]}`;

          let hoverProps;
          if (valueOnHover) {
            hoverProps = {<commit_msg>Changed Chart to fix an issue with re-rendering when no values are provided (#3570)<commit_after>import React, { useContext, useEffect, useRef, useState } from 'react';

import { ThemeContext } from 'styled-components';

import { normalizeColor, parseMetricToNum } from '../../utils';

import { StyledChart } from './StyledChart';
import { normalizeBounds, normalizeValues } from './utils';

// use constants so re-renders don't re-trigger effects
const defaultSize = { height: 'small', width: 'medium' };
const defaultValues = [];

const Chart = React.forwardRef(
  (
    {
      bounds: propsBounds,
      color = 'accent-1',
      gap,
      onClick,
      onHover,
      overflow = false,
      round,
      size: propsSize = defaultSize,
      thickness = 'medium',
      type = 'bar',
      values: propsValues = defaultValues,
      ...rest
    },
    ref,
  ) => {
    const theme = useContext(ThemeContext);
    const [values, setValues] = useState([]);
    const [bounds, setBounds] = useState([
      [0, 0],
      [0, 0],
    ]);
    const [containerSize, setContainerSize] = useState([0, 0]);
    const [size, setSize] = useState([0, 0]);
    const [scale, setScale] = useState([1, 1]);
    const [strokeWidth, setStrokeWidth] = useState(0);
    const containerRef = ref || useRef();

    // calculations
    useEffect(() => {
      const nextValues = normalizeValues(propsValues);
      setValues(nextValues);

      const nextBounds = normalizeBounds(propsBounds, nextValues);
      setBounds(nextBounds);

      const nextStrokeWidth = parseMetricToNum(
        theme.global.edgeSize[thickness] || thickness,
      );
      setStrokeWidth(nextStrokeWidth);

      const gapWidth = gap
        ? parseMetricToNum(theme.global.edgeSize[gap] || gap)
        : nextStrokeWidth;

      // autoWidth is how wide we'd pefer
      const autoWidth =
        nextStrokeWidth * nextValues.length +
        (nextValues.length - 1) * gapWidth;

      const sizeWidth =
        typeof propsSize === 'string'
          ? propsSize
          : propsSize.width || defaultSize.width;
      let width;
      if (sizeWidth === 'full') {
        [width] = containerSize;
      } else if (sizeWidth === 'auto') {
        width = autoWidth;
      } else {
        width = parseMetricToNum(theme.global.size[sizeWidth] || sizeWidth);
      }

      const sizeHeight =
        typeof propsSize === 'string'
          ? propsSize
          : propsSize.height || defaultSize.height;
      let height;
      if (sizeHeight === 'full') {
        [, height] = containerSize;
      } else {
        height = parseMetricToNum(theme.global.size[sizeHeight] || sizeHeight);
      }

      setSize([width, height]);

      const nextScale = [
        (sizeWidth === 'auto' ? autoWidth : width) /
          (nextBounds[0][1] - nextBounds[0][0]),
        height / (nextBounds[1][1] - nextBounds[1][0]),
      ];
      setScale(nextScale);
    }, [
      containerSize,
      gap,
      propsBounds,
      propsSize,
      propsValues,
      theme.global.edgeSize,
      theme.global.size,
      thickness,
    ]);

    // set container size when we get ref or when size changes
    if (
      containerRef.current &&
      propsSize &&
        (propsSize === 'full' ||
          propsSize.height === 'full' ||
          propsSize.width === 'full')
    ) {
      const containerNode = containerRef.current;
      if (containerNode) {
        const { parentNode } = containerNode;
        if (parentNode) {
          const rect = parentNode.getBoundingClientRect();
          if (
            rect.width !== containerSize[0] ||
            rect.height !== containerSize[1]
          ) {
            setContainerSize([rect.width, rect.height]);
          }
        }
      }
    }

    // container size, if needed
    useEffect(() => {
      const onResize = () => {
        const { parentNode } = containerRef.current;
        const rect = parentNode.getBoundingClientRect();
        setContainerSize([rect.width, rect.height]);
      };

      if (
        propsSize &&
        (propsSize === 'full' ||
          propsSize.width === 'full' ||
          propsSize.height === 'full')
      ) {
        window.addEventListener('resize', onResize);
        return () => window.removeEventListener('resize', onResize);
      }
      return undefined;
    }, [containerRef, propsSize]);

    const renderBars = () =>
      (values || []).map((valueArg, index) => {
        const { label, onHover: valueOnHover, value, ...valueRest } = valueArg;

        const key = `p-${index}`;
        const bottom = value.length === 2 ? bounds[1][0] : value[1];
        const top = value.length === 2 ? value[1] : value[2];
        if (top !== 0) {
          const d =
            `M ${(value[0] - bounds[0][0]) * scale[0]},` +
            `${size[1] - (bottom - bounds[1][0]) * scale[1]}` +
            ` L ${(value[0] - bounds[0][0]) * scale[0]},` +
            `${size[1] - (top - bounds[1][0]) * scale[1]}`;

          let hoverProps;
          if (valueOnHover) {
            hoverProps = {
{'fraction_non_alphanumeric': 0.1455689452916711, 'fraction_numerical': 0.010237815932601046, 'mean_word_length': 2.218256691832533, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2954634', 'n_tokens_mistral': 2781, 'n_tokens_neox': 2745, 'n_words': 632}