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

[data grid] Missing MuiDataGrid-cell--withRightBorder class on last cell in row #15950

Open
dhsj8405 opened this issue Dec 20, 2024 · 2 comments
Labels
bug 🐛 Something doesn't work component: data grid This is the name of the generic UI component, not the React module! customization: css Design CSS customizability status: waiting for author Issue with insufficient information

Comments

@dhsj8405
Copy link

dhsj8405 commented Dec 20, 2024

Steps to reproduce

Code example
import { Stack } from "@mui/material";
import { GridAlignment, GridColumnGroupingModel } from "@mui/x-data-grid";
import { DataGridPremium, GridColDef } from "@mui/x-data-grid-premium";
import { useEffect, useState } from "react";

type ReportMap = {
  id: number;
  tranKindName: string;
  grade1Name: string;
  grade2Name: string;
  grade3Name: string;
  grade4Name: string;
  grade5Name: string;
  grade6Name: string;
  TotalCount: number;
  TotalAmount: number;
};

type TotalRowType = ReportMap & {
  id: string | number;
};

const initialColumns: GridColDef[] = [
  {
    field: "tranKindName",
    headerName: "종류",
    minWidth: 100,
    flex: 2,
    sortable: false,
  },
  {
    field: "grade3Name",
    headerName: "deptA",
    minWidth: 100,
    flex: 2,
    sortable: false,
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "grade3Name"),
  },
  {
    field: "grade4Name",
    headerName: "deptB",
    minWidth: 100,
    flex: 2,
    sortable: false,
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "grade4Name"),
  },
  {
    field: "grade5Name",
    headerName: "deptC",
    minWidth: 100,
    flex: 2,
    sortable: false,
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "grade5Name"),
  },
  {
    field: "grade6Name",
    headerName: "deptD",
    minWidth: 100,
    flex: 2,
    sortable: false,
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "grade6Name"),
  },

  {
    field: "TotalCount",
    headerName: "총 건수",
    minWidth: 70,
    flex: 1,
    align: "right",
    sortable: false,
    type: "number",
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "TotalCount"),
  },
  {
    field: "TotalAmount",
    headerName: "총 금액",
    minWidth: 110,
    flex: 2,
    align: "right",
    sortable: false,
    type: "number",
    rowSpanValueGetter: (value, row) =>
      getRowSpanValue(value, row, "TotalAmount"),
  },
];

const totalSumColumnGroupModel: GridColumnGroupingModel = [
  {
    groupId: "sumTot",
    headerName: "총 합계",
    description: "",
    children: [{ field: "TotalCount" }, { field: "TotalAmount" }],
  },
];

const getRowSpanValue = (value, row, currentField) => {
  if (!row) return value;

  const fields = Object.keys(row)
    .filter((key) => key !== "id")
    .slice(0, Object.keys(row).indexOf(currentField));

  return fields.map((field) => row[field]).join("-");
};

const now = new Date();
const startDate = new Date();
startDate.setDate(now.getDate() - 7); // 현재 날짜에서 7일을 뺌

const Page = () => {
  const [originRows, setOriginRows] = useState<ReportMap[]>([]);
  const [rows, setRows] = useState<ReportMap[]>([]);
  const [columns, setColumns] = useState<GridColDef[]>([]);
  const [gradeLevel, setGradeLevel] = useState(6);
  const [loading, setLoading] = useState(false);
  const [slotDays, setSlotDays] = useState<string[]>([]);
  const [totalRow, setTotalRow] = useState<TotalRowType>({
    id: "totalRowId",
    tranKindName: "",
    grade1Name: "",
    grade2Name: "",
    grade3Name: "",
    grade4Name: "",
    grade5Name: "",
    grade6Name: "",
    TotalCount: 0,
    TotalAmount: 0,
  });
  const [columnGroupingModel, setColumnGroupingModel] =
    useState<GridColumnGroupingModel>([]);

  useEffect(() => {
    setRows(originRows);
  }, [originRows]);

  const generateGrades = (departmentId, departments) => {
    const departmentMap = departments.reduce((map, dept) => {
      map[dept.id] = dept;
      return map;
    }, {});

    const grades = [];
    let currentDept = departmentMap[departmentId];

    // 상위 부서부터 추적하여 grades 배열에 추가
    while (currentDept) {
      grades.unshift(currentDept.departmentName);
      currentDept = currentDept.parentId
        ? departmentMap[currentDept.parentId]
        : null;
    }

    return {
      grade3Name: grades[0] || "",
      grade4Name: grades[1] || "",
      grade5Name: grades[2] || "",
      grade6Name: grades[3] || "",
    };
  };

  useEffect(() => {
    // 동적 컬럼생성
    if (rows.length > 0) {
      setSlotDays(makeDateColumns(rows[0]));
    } else {
      setSlotDays([]);
    }
  }, [rows]);

  useEffect(() => {
    const dynamicColumns: GridColDef[] = slotDays
      .map((key) => [
        {
          field: `Count_${key}`,
          headerName: "건수",
          flex: 1,
          minWidth: 70,
          align: "right" as GridAlignment,
          sortable: false,
          type: "number",
          rowSpanValueGetter: (value, row) =>
            getRowSpanValue(value, row, `Count_${key}`),
        } as GridColDef,
        {
          field: `Amount_${key}`,
          headerName: "금액",
          flex: 2,
          minWidth: 110,
          align: "right" as GridAlignment,
          sortable: false,
          type: "number",
          rowSpanValueGetter: (value, row) =>
            getRowSpanValue(value, row, `Amount_${key}`),
        } as GridColDef,
      ])
      .flat();
    const dateColumnGroupingModel: GridColumnGroupingModel = [
      ...slotDays.map((key) => ({
        groupId: key,
        headerName: key,
        description: "",
        children: [{ field: `Count_${key}` }, { field: `Amount_${key}` }],
      })),
    ];

    const filteredColumns = [...initialColumns, ...dynamicColumns].filter(
      (column) => {
        if (gradeLevel >= 6) {
          return true;
        } else if (gradeLevel >= 5) {
          return column.field !== "grade6Name";
        } else if (gradeLevel >= 4) {
          return column.field !== "grade6Name" && column.field !== "grade5Name";
        } else if (gradeLevel >= 3) {
          return (
            column.field !== "grade6Name" &&
            column.field !== "grade5Name" &&
            column.field !== "grade4Name"
          );
        } else {
          return (
            column.field !== "grade6Name" &&
            column.field !== "grade5Name" &&
            column.field !== "grade4Name" &&
            column.field !== "grade3Name"
          );
        }
      },
    );
    const filteredFields: string[] = filteredColumns.map((col) => col.field);

    const filteredDateColumnGroupModel = dateColumnGroupingModel
      .map((group) => ({
        ...group,
        children: group.children.filter(
          (child) => "field" in child && filteredFields.includes(child.field),
        ),
      }))
      .filter((group) => group.children.length > 0);

    setColumns(filteredColumns);

    setTotalRow({
      ...calculateSummaryRow(rows, filteredColumns),
    });
    setColumnGroupingModel([
      ...totalSumColumnGroupModel,
      ...filteredDateColumnGroupModel,
    ]);
  }, [slotDays]);

  const calculateSummaryRow = (
    rows: any[],
    filteredColumns: any[],
  ): TotalRowType => {
    const summaryRow: TotalRowType = {
      id: "totalRowId",
      tranKindName: "",
      grade1Name: "",
      grade2Name: "",
      grade3Name: "",
      grade4Name: "",
      grade5Name: "",
      grade6Name: "",
      TotalCount: 0,
      TotalAmount: 0,
    };

    filteredColumns.forEach((column, idx) => {
      if (column.type === "number") {
        summaryRow[column.field] = rows.reduce(
          (acc, row) => acc + (row[column.field] || 0),
          0,
        );
      } else {
        if (idx === 0) {
          summaryRow[column.field] = "합계";
        }
      }
    });

    return summaryRow;
  };

  useEffect(() => {
    getList();
  }, []);

  const getList = async () => {
    setLoading(true);

    const responseData = {
      reportMaps: [
        {
          id: 1,
          tranKindName: "test1",
          grade1Name: "",
          grade2Name: "",
          grade3Name: "",
          grade4Name: "",
          grade5Name: "",
          grade6Name: "",
          TotalCount: 30,
          TotalAmount: 1020000.0,
          "Count_2024-11-21": 3,
          "Amount_2024-11-21": 140000.0,
          "ReceiptIds_2024-11-21": "",
          "Count_2024-11-20": 1,
          "Amount_2024-11-20": 40000.0,
          "ReceiptIds_2024-11-20": "",
          "Count_2024-11-30": 2,
          "Amount_2024-11-30": 80000.0,
          "ReceiptIds_2024-11-30": "",
          "Count_2024-11-19": 1,
          "Amount_2024-11-19": 40000.0,
          "ReceiptIds_2024-11-19": "",
          "Count_2024-11-18": 2,
          "Amount_2024-11-18": 40000.0,
          "ReceiptIds_2024-11-18": "",
          "Count_2024-11-16": 1,
          "Amount_2024-11-16": 0.0,
          "ReceiptIds_2024-11-16": "",
          "Count_2024-11-15": 3,
          "Amount_2024-11-15": 100000.0,
          "ReceiptIds_2024-11-15": "",
          "Count_2024-11-14": 4,
          "Amount_2024-11-14": 140000.0,
          "ReceiptIds_2024-11-14": "",
          "Count_2024-12-04": 1,
          "Amount_2024-12-04": 60000.0,
          "ReceiptIds_2024-12-04": "",
          "Count_2024-11-13": 5,
          "Amount_2024-11-13": 140000.0,
          "ReceiptIds_2024-11-13": "",
          "Count_2024-12-03": 1,
          "Amount_2024-12-03": 60000.0,
          "ReceiptIds_2024-12-03": "",
          "Count_2024-11-12": 3,
          "Amount_2024-11-12": 60000.0,
          "ReceiptIds_2024-11-12": "",
          "Count_2024-12-02": 1,
          "Amount_2024-12-02": 60000.0,
          "ReceiptIds_2024-12-02": "",
          "Count_2024-11-22": 1,
          "Amount_2024-11-22": 0.0,
          "ReceiptIds_2024-11-22": "",
          "Count_2024-12-01": 1,
          "Amount_2024-12-01": 60000.0,
          "ReceiptIds_2024-12-01": "",
        },
      ],
      departmentMaps: [
        {
          departmentName: "부서1",
          id: 1,
          parentId: null,
          order: 1,
        },
        {
          departmentName: "부서2",
          id: 2,
          parentId: 1,
          order: 2,
        },
        {
          departmentName: "부서3",
          id: 3,
          parentId: 2,
          order: 3,
        },
      ],
    };

    setLoading(false);

    const reportDatas = responseData.reportMaps;
    setOriginRows(reportDatas);
  };

  const makeDateColumns = (data: Record<string, any>): string[] => {
    return Object.keys(data)
      .filter((key) => key.startsWith("Amount_")) // "Amount_"로 시작하는 키만 필터링
      .map((key) => key.replace("Amount_", "")) // "Amount_"를 제거한 키 생성
      .sort(); // 문자열 순서대로 정렬
  };

  return (
    <>
      <Stack spacing={0}>
        <DataGridPremium
          columns={columns}
          rows={rows}
          loading={loading}
          disableColumnReorder
          disableColumnMenu
          unstable_rowSpanning
          columnGroupingModel={columnGroupingModel}
          hideFooter
          showColumnVerticalBorder
          showCellVerticalBorder
          pinnedColumns={{
            left: [
              "tranKindName",
              "grade1Name",
              "grade2Name",
              "grade3Name",
              "grade4Name",
              "grade5Name",
              "grade6Name",
            ],
          }}
          pinnedRows={{
            bottom: [{ ...totalRow }],
          }}
        />
      </Stack>
    </>
  );
};

export default Page;

Current behavior

In the MUI DataGrid, the cells in the last column do not have the MuiDataGrid-cell--withRightBorder class applied. This results in the absence of a right-side border for those cells, despite showCellVerticalBorder being enabled.

Expected behavior

This issue is causing inconsistent styling in the DataGrid, particularly when vertical borders are expected to be displayed on all cells for a clean and structured layout. It negatively impacts the visual appearance of the grid, especially in applications that rely on strict UI alignment.

The issue persists regardless of whether columns are dynamically or statically defined. A prompt resolution or workaround would be greatly appreciated to maintain consistent UI behavior.

Context

This issue is causing inconsistent styling in the DataGrid, particularly when vertical borders are expected to be displayed on all cells for a clean and structured layout. It negatively impacts the visual appearance of the grid, especially in applications that rely on strict UI alignment.

The issue persists regardless of whether columns are dynamically or statically defined. A prompt resolution or workaround would be greatly appreciated to maintain consistent UI behavior.

image

Your environment

`npx @mui/envinfo`
npx @mui/envinfo
Need to install the following packages:
@mui/envinfo@2.0.30
Ok to proceed? (y) y

System:
OS: Windows 11 10.0.22631
Binaries:
 Node: 18.17.0 - C:\Program Files\nodejs\node.EXE
    npm: 9.6.7 - C:\Program Files\nodejs\npm.CMD
    pnpm: 9.4.0 - ~\AppData\Roaming\npm\pnpm.CMD
Browsers:
Chrome: Not Found
Edge: Chromium (131.0.2903.70)
npmPackages:
@emotion/react: ^11.11.1 => 11.11.3
@emotion/styled: ^11.11.0 => 11.11.0
@mui/icons-material: ^5.14.8 => 5.15.7
@mui/lab: 5.0.0-alpha.120 => 5.0.0-alpha.120
@mui/material: ^5.15.14 => 5.15.14
@mui/styled-engine-sc: ^5.14.8 => 5.14.12
@mui/system: ^5.15.14 => 5.15.14
@mui/x-data-grid: ^7.22.3 => 7.22.3
@mui/x-data-grid-generator: ^6.13.0 => 6.19.3
@mui/x-data-grid-premium: ^7.22.3 => 7.22.3
@mui/x-data-grid-pro: ^7.22.3 => 7.22.3
@mui/x-date-pickers: ^6.14.0 => 6.19.3
@mui/x-date-pickers-pro: ^6.13.0 => 6.19.3
@mui/x-license: ^7.0.0 => 7.21.0
@mui/x-tree-view: ^7.14.0 => 7.20.0
@mui/x-tree-view-pro: ^7.14.0 => 7.20.0
@types/react: 18.0.28 => 18.0.28
react: 18.2.0 => 18.2.0
react-dom: 18.2.0 => 18.2.0
styled-components: ^5.3.11 => 5.3.11

Search keywords: withRightBorder, Last Column's Cells

@dhsj8405 dhsj8405 added bug 🐛 Something doesn't work status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Dec 20, 2024
@michelengelen
Copy link
Member

Hey @dhsj8405 ... the absence of the MuiDataGrid-cell--withRightBorder class is to prevent duplicate borders on the last cell in a row, since the data grid is wrapped in a border typically as well.

Additionally adding a border would mess with the virtualization logic and could lead to unwanted scrolling behavior.

@KenanYusuf do you know of a workaround we could provide here that's not messing with it?

@michelengelen michelengelen changed the title Missing MuiDataGrid-cell--withRightBorder Class on Last Column's Cells [data grid] Missing MuiDataGrid-cell--withRightBorder class on last cell in row Dec 20, 2024
@michelengelen michelengelen added component: data grid This is the name of the generic UI component, not the React module! customization: css Design CSS customizability status: waiting for author Issue with insufficient information and removed status: waiting for maintainer These issues haven't been looked at yet by a maintainer labels Dec 20, 2024
@KenanYusuf
Copy link
Member

@dhsj8405 would applying a border-right on the data grid fix your issue?

<DataGridPremium sx={{ borderRight: "1px solid e0e0e0" }} />

It's hard to advise on an exact solution without having a working reproduction. I put your code into CodeSandbox and it looks different to the image on the issue.

If the above solution does not work, please could you fork this example and update it to match what you have in the screenshot? https://codesandbox.io/p/sandbox/lucid-frog-yzfxsk

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🐛 Something doesn't work component: data grid This is the name of the generic UI component, not the React module! customization: css Design CSS customizability status: waiting for author Issue with insufficient information
Projects
None yet
Development

No branches or pull requests

3 participants