Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Domain padding is not working for custom chart #2806

Closed
2 tasks done
smilemittal opened this issue Feb 15, 2024 · 8 comments
Closed
2 tasks done

Domain padding is not working for custom chart #2806

smilemittal opened this issue Feb 15, 2024 · 8 comments
Labels
Issue: Stale This issue is marked as stale and will close in 14 days Status: Needs More Info ✋ A question or report that needs more info to be addressable Type: Question ❔ Asking a question or asking for help

Comments

@smilemittal
Copy link

Is there an existing issue for this?

  • I have searched the existing issues

Code of Conduct

  • I agree to follow this project's Code of Conduct

Question

Hi 
I have followed the below link and created a custom chart with a line, bar, and secondary axis.
https://commerce.nearform.com/open-source/victory/guides/custom-charts

But my bars overlap in every case and also bar width is not working if I pass values using functions.

I have attached my code below

     <div className="">
        <ChartContainer>
          <VictoryChart
            theme={VictoryTheme.material}
            width={chartArea.width}
            height={chartArea.height}
            scale={{ x: "time", y: "linear" }}
            padding={{
              left: chartArea.paddingLeft,
              right: chartArea.paddingRight,
            }}
            domainPadding={20} 
            containerComponent={
              <VictoryZoomContainer
                allowPan={zoom.allowPan}
                allowZoom={zoom.allowPan}
                minimumZoom={{ x: zoom.minZoom, y: 0 }}
                zoomDimension={chartArea.zoomDimension}
                zoomDomain={chartState.zoomDomain}
                onZoomDomainChange={handleZoom}
                preserveAspectRatio="none"
                responsive
              />
            }
            animate={{
              duration: animation.duration,
              onLoad: { duration: animation.onloadDuration },
            }}
          >
            <g
              transform="translate(0, 0)"
              width={chartArea.width}
              height={chartArea.height}
              // role="presentation"
            >
              {/* X Axis Data */}
              <VictoryAxis
                scale="time"
                tickValues={xAxisValues}
                tickFormat={(tick) => {
                  return formatDate(tick, interval || "");
                }}
                orientation={barsAxis.gridAxisOrientation}
                fixLabelOverlap={chartArea.fixLabelOverlap}
                offsetY={barsAxis.gridAxisOffsetY}
                offsetX={0}
                style={{
                  axis: {
                    strokeWidth: barsAxis.strokeWidth,
                    stroke: barsAxis.gridStroke,
                  },
                  ticks: {
                    size: barsAxis.tickSize,
                    strokeWidth: barsAxis.strokeWidth,
                  },
                  tickLabels: {
                    fontSize: barsAxis.tickFontSize,
                    padding: barsAxis.tickPadding,
                    fill: barsAxis.tickFill,
                    textAnchor: barsAxis.tickAnchor,
                    dy: barsAxis.tickDy,
                  },
                  grid: {
                    strokeWidth: barsAxis.strokeWidth,
                    stroke: barsAxis.gridStroke,
                    strokeDasharray: barsAxis.gridStrokeDasharray,
                  },
                }}
                standalone={false}
                domain={{
                  x: [chartState.zoomDomain.x[0], chartState.zoomDomain.x[1]],
                }}
              />

              {/* Primary Data Set */}

              <VictoryAxis
                dependentAxis
                offsetX={barsAxis.dependentAxisOffsetY}
                crossAxis={false}
                orientation="left"
                tickFormat={(tick) => formatYAxisTicks(tick)}
                style={{
                  axis: {
                    strokeWidth: barsAxis.strokeWidth,
                    stroke: brushBars?.fill,
                  },
                  ticks: {
                    size: barsAxis.tickSize,
                    strokeWidth: barsAxis.strokeWidth,
                  },
                  tickLabels: {
                    fontSize: barsAxis.tickFontSize,
                    padding: barsAxis.tickPadding,
                    fill: barsAxis.tickFill,
                  },
                  grid: {
                    strokeWidth: barsAxis.dependentStrokeWidth,
                    stroke: barsAxis.gridStroke,
                    strokeDasharray: barsAxis.gridStrokeDasharray,
                  },
                }}
                standalone={false}
                domain={[
                  getChartYValue(primaryData || [], "lowest"),
                  getChartYValue(primaryData || [], "highest"),
                ]}
              />

              <VictoryGroup offset={16} standalone={false}>
                {primaryData?.map(
                  (category) =>
                    category.visible === true &&
                    category.chart_type === "bar" && (
                      <VictoryBar
                        key={category.name}
                        // barWidth={calculateBarWidth(
                        //   updatedData || [],
                        //   chartState.zoomDomain,
                        //   chartArea,
                        // )}
                        barWidth={2}
                        barRatio={bars?.barRatio}
                        alignment={bars?.alignment}
                        cornerRadius={{
                          topLeft: bars?.cornerTopLeftRadius,
                          topRight: bars?.cornerTopRightRadius,
                        }}
                        style={{
                          data: {
                            fill: category.color,
                          },
                        }}
                        // data={category?.transactions}
                        data={category?.transactions?.map((datum) => {
                          return {
                            ...datum,
                            formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                          };
                        })}
                        labels={({ datum }: { datum: { amount: number } }) =>
                          `${category.name}: ${convertToLocaleString(
                            datum.amount,
                            "USD",
                          )}`
                        }
                        labelComponent={
                          <VictoryTooltip
                            flyoutStyle={{
                              fill: "#474D66",
                              color: "#fff",
                            }}
                            style={{ fontSize: 4, fill: "#fff" }}
                          />
                        }
                        x={chartArea.xAxis}
                        y={chartArea.yAxis}
                        standalone={false}
                        scale={{ x: "time", y: "linear" }}
                        domain={{
                          x: [
                            chartState.zoomDomain.x[0],
                            chartState.zoomDomain.x[1],
                          ],
                          y: [
                            getChartYValue(primaryData || [], "lowest"),
                            getChartYValue(primaryData || [], "highest"),
                          ],
                        }}
                      />
                    ),
                )}
              </VictoryGroup>

              {primaryData?.map(
                (category, categoryIndex: number) =>
                  category.visible === true &&
                  category.chart_type === "line" && (
                    <VictoryLine
                      key={categoryIndex}
                      interpolation={line?.interpolation}
                      style={{
                        data: {
                          stroke: category.color,
                          strokeWidth: line?.strokeWidth,
                        },
                      }}
                      // data={category?.transactions}
                      data={category?.transactions?.map((datum) => {
                        return {
                          ...datum,
                          formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                        };
                      })}
                      x={chartArea.xAxis}
                      y={chartArea.yAxis}
                      standalone={false}
                      scale={{ x: "time", y: "linear" }}
                      domain={{
                        // x: [xAxisValues[0], xAxisValues[xAxisValues.length - 1]],
                        x: [
                          chartState.zoomDomain.x[0],
                          chartState.zoomDomain.x[1],
                        ],
                        y: [
                          getChartYValue(primaryData || [], "lowest"),
                          getChartYValue(primaryData || [], "highest"),
                        ],
                      }}
                    />
                  ),
              )}

              {primaryData?.map(
                (category, categoryIndex: number) =>
                  category.visible === true &&
                  category.chart_type === "line" && (
                    <VictoryScatter
                      key={categoryIndex}
                      style={{
                        data: { fill: category.color },
                      }}
                      // data={category?.transactions}
                      data={category?.transactions?.map((datum) => {
                        return {
                          ...datum,
                          formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                        };
                      })}
                      x={chartArea.xAxis}
                      y={chartArea.yAxis}
                      size={line?.scatter}
                      labels={({ datum }: { datum: { amount: number } }) =>
                        `${category.name}: ${convertToLocaleString(
                          datum.amount,
                          "USD",
                        )}`
                      }
                      labelComponent={
                        <VictoryTooltip
                          flyoutStyle={{
                            fill: "#474D66",
                            color: "#fff",
                          }}
                          style={{ fontSize: 4, fill: "#fff" }}
                        />
                      }
                      standalone={false}
                      scale={{ x: "time", y: "linear" }}
                      domain={{
                        x: [
                          chartState.zoomDomain.x[0],
                          chartState.zoomDomain.x[1],
                        ],
                        y: [
                          getChartYValue(primaryData || [], "lowest"),
                          getChartYValue(primaryData || [], "highest"),
                        ],
                      }}
                    />
                  ),
              )}

              {/* Secondary Data Set */}

              <VictoryAxis
                dependentAxis
                orientation="right"
                crossAxis={false}
                tickFormat={(tick) => formatYAxisTicks(tick)}
                style={{
                  axis: {
                    strokeWidth: barsAxis.strokeWidth,
                    stroke: brushBars?.fill,
                  },
                  ticks: {
                    size: barsAxis.tickSize,
                    strokeWidth: barsAxis.strokeWidth,
                  },
                  tickLabels: {
                    fontSize: barsAxis.tickFontSize,
                    padding: barsAxis.tickPadding,
                    fill: barsAxis.tickFill,
                  },
                  // grid: {
                  //   strokeWidth: barsAxis.dependentStrokeWidth,
                  //   stroke: barsAxis.gridStroke,
                  //   strokeDasharray: barsAxis.gridStrokeDasharray,
                  // },
                }}
                standalone={false}
                domain={[
                  getChartYValue(secondaryData || [], "lowest"),
                  getChartYValue(secondaryData || [], "highest"),
                ]}
              />

              <VictoryGroup offset={16} standalone={false}>
                {secondaryData?.map(
                  (category) =>
                    category.visible === true &&
                    category.chart_type === "bar" && (
                      <VictoryBar
                        key={category.name}
                        // barWidth={calculateBarWidth(
                        //   updatedData || [],
                        //   chartState.zoomDomain,
                        //   chartArea,
                        // )}
                        barWidth={2}
                        barRatio={bars?.barRatio}
                        alignment={bars?.alignment}
                        cornerRadius={{
                          topLeft: bars?.cornerTopLeftRadius,
                          topRight: bars?.cornerTopRightRadius,
                        }}
                        style={{
                          data: {
                            fill: category.color,
                          },
                        }}
                        // data={category?.transactions}
                        data={category?.transactions?.map((datum) => {
                          return {
                            ...datum,
                            formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                          };
                        })}
                        labels={({ datum }: { datum: { amount: number } }) =>
                          `${category.name}: ${convertToLocaleString(
                            datum.amount,
                            "USD",
                          )}`
                        }
                        labelComponent={
                          <VictoryTooltip
                            flyoutStyle={{
                              fill: "#474D66",
                              color: "#fff",
                            }}
                            style={{ fontSize: 4, fill: "#fff" }}
                          />
                        }
                        x={chartArea.xAxis}
                        y={chartArea.yAxis}
                        scale={{ x: "time", y: "linear" }}
                        standalone={false}
                        domain={{
                          x: [
                            chartState.zoomDomain.x[0],
                            chartState.zoomDomain.x[1],
                          ],
                          y: [
                            getChartYValue(secondaryData || [], "lowest"),
                            getChartYValue(secondaryData || [], "highest"),
                          ],
                        }}
                      />
                    ),
                )}
              </VictoryGroup>

              {secondaryData?.map(
                (category, categoryIndex: number) =>
                  category.visible === true &&
                  category.chart_type === "line" && (
                    <VictoryLine
                      key={categoryIndex}
                      interpolation={line?.interpolation}
                      style={{
                        data: {
                          stroke: category.color,
                          strokeWidth: line?.strokeWidth,
                        },
                      }}
                      data={category?.transactions?.map((datum) => {
                        return {
                          ...datum,
                          formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                        };
                      })}
                      x={chartArea.xAxis}
                      y={chartArea.yAxis}
                      scale={{ x: "time", y: "linear" }}
                      standalone={false}
                      domain={{
                        x: [
                          chartState.zoomDomain.x[0],
                          chartState.zoomDomain.x[1],
                        ],
                        y: [
                          getChartYValue(secondaryData || [], "lowest"),
                          getChartYValue(secondaryData || [], "highest"),
                        ],
                      }}
                    />
                  ),
              )}

              {secondaryData?.map(
                (category, categoryIndex: number) =>
                  category.visible === true &&
                  category.chart_type === "line" && (
                    <VictoryScatter
                      key={categoryIndex}
                      style={{
                        data: { fill: category.color },
                      }}
                      data={category?.transactions?.map((datum) => {
                        return {
                          ...datum,
                          formattedPeriod: interval == 'quarterly' ? new Date(datum.period)  : new Date(datum.formattedPeriod),
                        };
                      })}
                      x={chartArea.xAxis}
                      y={chartArea.yAxis}
                      size={line?.scatter}
                      labels={({ datum }: { datum: { amount: number } }) =>
                        `${category.name}: ${convertToLocaleString(
                          datum.amount,
                          "USD",
                        )}`
                      }
                      labelComponent={
                        <VictoryTooltip
                          flyoutStyle={{
                            fill: "#474D66",
                            color: "#fff",
                          }}
                          style={{ fontSize: 4, fill: "#fff" }}
                        />
                      }
                      standalone={false}
                      scale={{ x: "time", y: "linear" }}
                      domain={{
                        x: [
                          chartState.zoomDomain.x[0],
                          chartState.zoomDomain.x[1],
                        ],
                        y: [
                          getChartYValue(secondaryData || [], "lowest"),
                          getChartYValue(secondaryData || [], "highest"),
                        ],
                      }}
                    />
                  ),
              )}
            </g>
          </VictoryChart>
        </ChartContainer>

        <VictoryChart
          width={brushArea.width}
          height={brushArea.height}
          scale={{ x: "time" }}
          padding={{
            top: brushArea.paddingTop,
            left: brushArea.paddingLeft,
            right: brushArea.paddingRight,
            bottom: brushArea.paddingBottom,
          }}
          domainPadding={{ y: brushArea.domainPaddingY }}
          domain={{
            x: [xAxisValues[0], xAxisValues[xAxisValues.length - 1]],
            y: [
              getChartYValue(updatedData || [], "lowest"),
              getChartYValue(updatedData || [], "highest"),
            ],
          }}
          theme={VictoryTheme.material}
          containerComponent={
            <VictoryBrushContainer
              width={brushArea.width}
              height={brushArea.height}
              allowDrag={brush.allowDrag}
              allowDraw={brush.allowDraw}
              allowResize={brush.allowResize}
              brushDimension={chartArea.zoomDimension}
              brushDomain={chartState.selectedDomain}
              onBrushDomainChange={handleBrush}
              brushStyle={{
                stroke: brush.stroke,
                fill: brush.fill,
                fillOpacity: brush.fillOpacity,
                strokeWidth: brush.strokeWidth,
              }}
              handleWidth={brush.handleWidth}
              handleComponent={
                <svg
                  width="100%"
                  height="100%"
                  fill="none"
                  viewBox="0 0 16 18"
                  xmlns="http://www.w3.org/2000/svg"
                  style={{ display: "block" }}
                >
                  <rect
                    x="3"
                    y="1"
                    width="10"
                    height="15"
                    fill="#F9F9F9"
                    stroke="#D6D6D6"
                  />
                  <line x1="6.5" y1="5" x2="6.5" y2="13" stroke="#D6D6D6" />
                  <line x1="9.5" y1="5" x2="9.5" y2="13" stroke="#D6D6D6" />
                </svg>
              }
            />
          }
        >
          <VictoryAxis
            dependentAxis
            orientation={brushAxis.orientation}
            offsetY={brushAxis.offsetY}
            tickValues={[0]}
            tickFormat={() => "0"}
            standalone={false}
            style={{
              axis: {
                strokeWidth: brushAxis.strokeWidth,
                stroke: brushAxis.stroke,
              },
              ticks: {
                size: brushAxis.tickSize,
                strokeWidth: brushAxis.tickWidth,
              },
              grid: {
                strokeWidth: brushAxis.gridWidth,
              },
            }}
          />

          <VictoryAxis
            scale="time"
            tickValues={xAxisValues}
            standalone={false}
            tickFormat={(x) => {
              return formatDate(x, interval || "");
            }}
            fixLabelOverlap={chartArea.fixLabelOverlap}
            orientation={brushXAxis.orientation}
            offsetY={brushXAxis.offsetY}
            style={{
              axis: {
                strokeWidth: brushXAxis.strokeWidth,
                stroke: brushAxis.stroke,
              },
              ticks: {
                size: brushAxis.tickSize,
                strokeWidth: barsAxis.strokeWidth,
              },
              tickLabels: {
                fontSize: brushXAxis.fontSize,
                padding: brushXAxis.padding,
                fill: brushXAxis.fill,
                textAnchor: brushXAxis.anchor,
              },
              grid: {
                strokeWidth: brushXAxis.strokeWidth,
                stroke: brushAxis.stroke,
                strokeDasharray: barsAxis.gridStrokeDasharray,
              },
            }}
          />
        </VictoryChart>

        <Header
          display="flex"
          alignItems="start"
          justifyContent="space-between"
          flexGap="48px"
          padding="10px 40px"
          margin="20px 0 0 0"
        >
          <Header display="flex" flexGap="1.5rem" flexWrap="wrap">
            <Header
              display="flex"
              flexGap="2rem"
              flexWrap="wrap"
              flexDirection="column"
            >
              <Typography variant="caption" color="#595E71" weight="normal">
                Primary Axis
              </Typography>
              {updatedData?.map((category: GeneralCategory, index: number) =>
                category.secondary_axis == false ? (
                  <VictoryCheckbox
                    key={index}
                    checked={category.visible}
                    onChange={() => handleCheckboxChange(category)}
                    label={category.name || undefined}
                    color={category.color}
                    type={category.chart_type === "line" ? "dot" : "rect"}
                  />
                ) : null,
              )}
            </Header>
            <Header
              display="flex"
              flexGap="2rem"
              flexWrap="wrap"
              flexDirection="column"
            >
              <Typography variant="caption" color="#595E71" weight="normal">
                Secondary Axis
              </Typography>
              {updatedData?.map((category: GeneralCategory, index: number) =>
                category.secondary_axis == true ? (
                  <VictoryCheckbox
                    key={index}
                    checked={category.visible}
                    onChange={() => handleCheckboxChange(category)}
                    label={category.name || undefined}
                    color={category.color}
                    type={category.chart_type === "line" ? "dot" : "rect"}
                  />
                ) : null,
              )}
            </Header>
          </Header>
          <VictoryButton
            onScrollLeft={handleScrollLeft}
            onScrollRight={handleScrollRight}
            onZoomIn={handleZoomIn}
            onZoomOut={handleZoomOut}
            disableLeft={disabledState.disableLeft}
            disableRight={disabledState.disableRight}
            disableZoomIn={disabledState.disableZoomIn}
            disableZoomOut={disabledState.disableZoomOut}
          />
        </Header>
      </div>


export const calculateBarWidth = (
  categories: GeneralCategory[],
  zoomDomain?: any,
  _chartArea?: any,
) => {
  const catagoriesCount = categories?.length || 0;
  const visiblePeriods = zoomDomain.x
    ? zoomDomain.x[1] - zoomDomain.x[0]
    : catagoriesCount;
  const totalBars = catagoriesCount * visiblePeriods;

  const maxWidth = _chartArea.width / (totalBars * 5); // Adjust 1.5 based on desired maximum width
  const minWidth = _chartArea.width / (totalBars * 10); // Adjust 3 based on desired minimum width
  let calculatedWidth;
  if (visiblePeriods < 5) {
    calculatedWidth = maxWidth * 10; // Increase the width for fewer periods
  } else {
    // Standard calculation
    calculatedWidth = Math.min(maxWidth, _chartArea.width / visiblePeriods);
  }
  return Math.max(calculatedWidth, minWidth);
};
@smilemittal smilemittal added the Type: Question ❔ Asking a question or asking for help label Feb 15, 2024
@smilemittal
Copy link
Author

image

see also bars overlap on secondary axis.

@carbonrobot
Copy link
Contributor

We really appreciate you taking the time to report an issue. Is it possible to to reduce your example code to just the minimum to reproduce? This would help us triage the issue quicker.

@smilemittal
Copy link
Author

image
CustomComboChart.txt
I have made an example here, you can check
see secondary axis and bars go overlap, and bar width did not change
Some times bars also go overlap

@smilemittal
Copy link
Author

We really appreciate you taking the time to report an issue. Is it possible to to reduce your example code to just the minimum to reproduce? This would help us triage the issue quicker.

wait for your response....

@smilemittal
Copy link
Author

@carbonrobot any update on this, still waiting for you............

@carbonrobot
Copy link
Contributor

@smilemittal Thanks for the code snippet, however it contains imports from your project code and is not runnable by our team.

If you can use our Codesandbox to create a minimal example to reproduce the problem, we can help you much quicker.

https://codesandbox.io/p/sandbox/victory-starter-dj4f7t?file=%2Fsrc%2FApp.tsx%3A8%2C5

@carbonrobot carbonrobot added the Status: Needs More Info ✋ A question or report that needs more info to be addressable label Feb 22, 2024
@carbonrobot carbonrobot mentioned this issue Feb 23, 2024
2 tasks
Copy link
Contributor

This issue is stale because it has been open for 90 days with no activity. If there is no activity in the next 7 days, the issue will be closed.

@github-actions github-actions bot added the Issue: Stale This issue is marked as stale and will close in 14 days label May 23, 2024
Copy link
Contributor

This issue was closed because it has been inactive for 7 days since being marked as stale. Please open a new issue if you believe you are encountering a related problem.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale May 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Issue: Stale This issue is marked as stale and will close in 14 days Status: Needs More Info ✋ A question or report that needs more info to be addressable Type: Question ❔ Asking a question or asking for help
Projects
None yet
Development

No branches or pull requests

2 participants