Repository: recharts/recharts
File: src/util/YAxisUtils.tsx (TypeScript)

Code before:
import * as React from 'react';

/**
 * Calculates the width of the Y-axis based on the tick labels and the axis label.
 * @param {Object} params - The parameters object.
 * @param {React.RefObject<any>} params.cartesianAxisRef - The ref to the CartesianAxis component.
 * @param {React.RefObject<Element>} params.labelRef - The ref to the label element.
 * @param {number} [params.labelGapWithTick=5] - The gap between the label and the tick.
 * @returns {number} The calculated width of the Y-axis.
 */
export const getCalculatedYAxisWidth = ({
  cartesianAxisRef,
  labelRef,
  labelGapWithTick = 5, // Default gap between label and tick
}: {
  cartesianAxisRef: React.RefObject<any>;
  labelRef?: React.RefObject<Element>;
  labelGapWithTick?: number;
}): number => {
  if (!cartesianAxisRef || !cartesianAxisRef.current) {
    return 0; // Return 0 if the ref is not set or current is null
  }

  // Access the tickRefs from the CartesianAxis component
  const axisComponent = cartesianAxisRef.current;
  const tickNodes = axisComponent?.tickRefs?.current;

  // find the max width of the tick labels
  let maxTickWidth = 0;
  if (tickNodes && tickNodes.length > 0) {
    tickNodes.forEach((tickNode: Element) => {
      if (tickNode) {
        const bbox = tickNode.getBoundingClientRect();

        if (bbox.width > maxTickWidth) {
          maxTickWidth = bbox.width;
        }
      }
    });

    // calculate width of the axis label
    const labelNode = labelRef?.current;
    const labelWidth = labelNode ? labelNode.getBoundingClientRect().width : 0;

    // calculate tick width: tickSize + tickMargin
    const { tickSize = 0, tickMargin = 0 } = axisComponent.props || {};
    const tickWidth = tickSize + tickMargin;

    // calculate the updated width of the y-axis
    const updatedYAxisWidth = maxTickWidth + tickWidth + labelWidth + (labelNode ? labelGapWithTick : 0);


Review comment (suggestion):
Let's not parse the internal structure of a component from outside of that component. Can we pass in the required data as props instead?

```
type IGetBoundingClient = Pick<Element, 'getBoundingClientRect'>

export const getCalculatedYAxisWidth = ({
  ticks,
  labelRef,
  labelGapWithTick = 5, // Default gap between label and tick in pixels
  tickSize = 0,
  tickMargin = 0
}: {
  ticks: ReadonlyArray<IGetBoundingClient> | undefined;
  labe: IGetBoundingClient | undefined;
  labelGapWithTick: number | undefined;
  tickSize: number | undefined;
  tickMargin: number | undefined;
}): number => {
```

That way we don't depend on React, we don't depend on other component internal structure, and it's way easier to mock in tests.

Also, please prefer `x | undefined` instead of `prop?:`. The question mark syntax makes it easy to forget that the function expects a particular input. The `| undefined` forces people to always pass in the parameter - and it handles undefined just the same. Impossible to forget.

Code after:
type IGetBoundingClient = Pick<Element, 'getBoundingClientRect'>;

/**
 * Calculates the width of the Y-axis based on the tick labels and the axis label.
 * @param {Object} params - The parameters object.
 * @param {React.RefObject<any>} params.cartesianAxisRef - The ref to the CartesianAxis component.
 * @param {React.RefObject<Element>} params.labelRef - The ref to the label element.
 * @param {number} [params.labelGapWithTick=5] - The gap between the label and the tick.
 * @returns {number} The calculated width of the Y-axis.
 */
export const getCalculatedYAxisWidth = ({
  ticks,
  label,
  labelGapWithTick = 5, // Default gap between label and tick
  tickSize = 0,
  tickMargin = 0,
}: {
  ticks: ReadonlyArray<IGetBoundingClient> | undefined;
  label: IGetBoundingClient | undefined;
  labelGapWithTick: number | undefined;
  tickSize: number | undefined;
  tickMargin: number | undefined;
}): number => {
  // find the max width of the tick labels
  let maxTickWidth = 0;
  if (ticks) {
    ticks.forEach((tickNode: Element) => {
      if (tickNode) {
        const bbox = tickNode.getBoundingClientRect();

        if (bbox.width > maxTickWidth) {
          maxTickWidth = bbox.width;
        }
      }
    });

    // calculate width of the axis label
    const labelWidth = label ? label.getBoundingClientRect().width : 0;

    const tickWidth = tickSize + tickMargin;

    // calculate the updated width of the y-axis
    const updatedYAxisWidth = maxTickWidth + tickWidth + labelWidth + (label ? labelGapWithTick : 0);

    return Math.round(updatedYAxisWidth);
  }

  return 0;
};