mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.57k stars 1.35k forks source link

Automatically Save / Restore State #10508

Closed lisaWriteJava closed 1 year ago

lisaWriteJava commented 1 year ago

The problem in depth 🔍

Looking at the options in the demo https://mui.com/x/react-data-grid/state/#save-and-restore-the-stateto save and restore state, there isn't anything that shows this being able to be automatic once the user sorts/filters, etc. also can the state be restored on refreshes and page open and closes

Your environment 🌎

`npx @mui/envinfo` ``` Don't forget to mention which browser you used. Output from `npx @mui/envinfo` goes here. ```

Search keywords: automatically save and restore the datagrid state Order ID: 66765

MBilalShafi commented 1 year ago

there isn't anything that shows this being able to be automatic once the user sorts/filters, etc

Could you elaborate more a bit the requirement? You want to save a state snapshot on every sort or filter operation? Is the rationale to revert to the current state if the user reopens/refreshes the page?

I believe you can achieve that by:

  1. Listening to the variable specific changes you are interested in (for example onFilterModelChange, onSortModelChange)
  2. Updating the state snapshot when a change event fires, which you can store in localStorage, db, etc.
  3. Revert to the saved state using initialState or apiRef.current.restoreState on page load (or whenever you want)

About supporting it out of the box: I am not in favor of it:

  1. There could be tons of ways to manage the storage of exported state depending on user's needs
  2. This is a rarely used use-case and could easily be achieved by adding some custom code (as I mentioned above)

Let me know if the suggested method would work in your use-case.

Thanks

lisaWriteJava commented 1 year ago

So I was trying to figure out a way that if a user sorts the columns, and/or reorders the columns, or pagination we can save that state without the need to create that view. Also, I want the state to be maintained if the user clicks goes to another page or refreshes. Essentially I'd like to persist the state of the datagrid.

how can I do this.

MBilalShafi commented 1 year ago

I've prepared a demo implementation that saves the exported state on every state change to localStorage and reloads it on page reload. Have a look at it: https://codesandbox.io/s/datagridpremium-basic-forked-5542fr?file=/src/App.tsx

Design details:

  1. The demo is designed keeping in mind the state has to be persisted locally, if you want to persist it for multiple people/sessions/devices you may need to replace localStorage with a database layer.
  2. Right now, it uses the onStateChange event handler which fires a lot of times based on different internal state changes, it may cause performance issues, especially if you decide to switch to API for storing the state, I'd recommend doing it in a more "controlled" way by limiting the state export to specific event handlers you are interested in, like onFilterModelChange for example.
  3. It doesn't persist and recover everything, only those which are supported by apiRef.current.exportState and accepted by initialState prop.

Let me know if the demo helps you achieve your required use case.

Thanks


I think this use case (persist and recover state locally) could be a good candidate for a recipe and could be useful for certain desktop-oriented apps. @cherniavskii Do you think it's worth it to add to the Recipes umbrella issue?

lisaWriteJava commented 1 year ago

thank you so much. I gave it a quick try and it's doing what I'd like to achieve. I'll let the powers that be know about the caveat no. 3. Honestly I think it would be great to add to the recipies. I went looking outside of MUI for this behavior and I saw something sort of like this but they couldn't get it to work with as many states and so I had to open a ticket. This is beyond helpful. Thank you so much.

lisaWriteJava commented 1 year ago

Just one question, if I have an initial state set for a grid ie sort order is it possible to have both with this scenario

MBilalShafi commented 1 year ago

Just one question, if I have an initial state set for a grid ie sort order is it possible to have both with this scenario

Sure thing, feel free to update the initialState populating logic as per your requirement!

lisaWriteJava commented 1 year ago

Just to confirm something like below is fine:

initialState={{
    ...cachedInitialState,
    sorting: {
        sortModel: sortModel
    }
}}
MBilalShafi commented 1 year ago

Just to confirm something like below is fine:

Sure, you can override the sortModel if the intention is to never persist it and always use a specific initialValue for it. But if the intention is to do this only for the first time and let the user change the sortModel afterwards, you can return this as a default value from getStateSnapshotFromLocalStorage method instead of {}

lisaWriteJava commented 1 year ago

ahh ok great. thanks for everything this was a big help

lisaWriteJava commented 1 year ago

Hello,

While your code standbox worked for me, When I incorporated it into my code the state didn't save. I'm workign up the sandbox

lisaWriteJava commented 1 year ago

ok here's an example of the state not saving when implemented in my code

https://codesandbox.io/s/datagridpremium-basic-forked-pk9s6y

MBilalShafi commented 1 year ago

The codesandbox link you shared appears to be broken, here's the error it's throwing

image

lisaWriteJava commented 1 year ago

This is the updated sandbox: https://codesandbox.io/s/datagridpremium-basic-forked-pk9s6y?file=/src/CommonGrid.jsx

saving the sandbox was a challenge just in case the first link above doesn't work I also forked it to save here https://codesandbox.io/s/datagridpremium-basic-forked-6zpflz

@MBilalShafi

lisaWriteJava commented 1 year ago

Just in case there is an issue viewing the sand box @MBilalShafi here is the code:

import { Box } from "@mui/material";
import CircularProgress from "@mui/material/CircularProgress";
import {
  DataGridPremium,
  GridToolbarContainer,
  GridToolbarColumnsButton,
  GridToolbarFilterButton,
  GridToolbarExport,
  GridToolbarDensitySelector,
  gridClasses,
  useGridApiRef,
  GRID_DETAIL_PANEL_TOGGLE_COL_DEF
} from "@mui/x-data-grid-premium";
import PropTypes from "prop-types";
import { alpha } from "@mui/material/styles";
import { styled } from "@mui/material/styles";

const ODD_OPACITY = 0.2;

//class actions
function saveStateSnapshotToLocalStorage(stateSnapshot) {
  console.info(
    "State update: Update the state snap in localStorage",
    stateSnapshot
  );
  localStorage.setItem("dataGridState", JSON.stringify(stateSnapshot));
}

function getStateSnapshotFromLocalStorage() {
  const stringifiedState = localStorage.getItem("dataGridState");
  return new Promise((resolve) => {
    if (!stringifiedState) {
      return resolve({});
    }
    const stateSnapshot = JSON.parse(stringifiedState);
    return resolve(stateSnapshot);
  });
}

function CustomToolbar() {
  return (
    <GridToolbarContainer
      sx={{
        width: "100%",
        backgroundColor: "#4d7496",
        color: "#fff !important",
        fontSize: 13,
        fontWeight: "bolder"
      }}
    >
      <GridToolbarColumnsButton
        sx={{
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        }}
      />
      <GridToolbarFilterButton
        sx={{
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        }}
      />
      <GridToolbarDensitySelector
        sx={{
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        }}
      />
      <GridToolbarExport
        sx={{
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        }}
      />
    </GridToolbarContainer>
  );
}

const StyledGridOverlay = styled("div")(({ theme }) => ({
  display: "flex",
  flexDirection: "column",
  alignItems: "center",
  justifyContent: "center",
  height: "100%",
  "& .ant-empty-img-1": {
    fill: "#262626"
  },
  "& .ant-empty-img-2": {
    fill: "#595959"
  },
  "& .ant-empty-img-3": {
    fill: "#434343"
  },
  "& .ant-empty-img-4": {
    fill: "#1c1c1c"
  },
  "& .ant-empty-img-5": {
    fillOpacity: "0.08",
    fill: "#fff"
  }
}));

function CustomNoRowsOverlay() {
  return (
    <StyledGridOverlay>
      <svg
        width="120"
        height="100"
        viewBox="0 0 184 152"
        aria-hidden
        focusable="false"
      >
        <g fill="none" fillRule="evenodd">
          <g transform="translate(24 31.67)">
            <ellipse
              className="ant-empty-img-5"
              cx="67.797"
              cy="106.89"
              rx="67.797"
              ry="12.668"
            />
            <path
              className="ant-empty-img-1"
              d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
            />
            <path
              className="ant-empty-img-2"
              d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
            />
            <path
              className="ant-empty-img-3"
              d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
            />
          </g>
          <path
            className="ant-empty-img-3"
            d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
          />
          <g className="ant-empty-img-4" transform="translate(149.65 15.383)">
            <ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815" />
            <path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z" />
          </g>
        </g>
      </svg>
      <Box sx={{ mt: 1, fontSize: 16, fontWeight: "bolder" }}>No Data</Box>
    </StyledGridOverlay>
  );
}

const mockViolationColumns = [
  {
    id: "user",
    headerName: "User",
    field: "user",
    type: "string",
    flex: 1,
    aggregable: false,
    groupable: false,
    headerAlign: "center",
    valueGetter: (params) => {
      //  console.log('in value getting for User params: ', params)

      //Asided from everything else, must set somethign in maybe name or a field to do the filter on
      return params.value.name;
    },
    renderCell: (rowInfo) => {
      // console.log('valueFormatter for User: ', rowInfo)  //you need to verify values below
      if (rowInfo.row.user.name) {
        return <div> placeHolder </div>;
      } else {
        return <span>Missing Name Info 2</span>;
      }
    }
  },
  {
    id: "violation_type",
    headerName: "Violation Type",
    field: "is_role",
    flex: 1,
    headerAlign: "center",
    aggregable: false,
    groupable: false,
    //Change to value gettter
    renderCell: (rowInfo) => {
      return rowInfo.value;
    },
    valueGetter: (params) => {
      // console.log('The violation type value getter ', params, ' \n params.row.is_role: ', params.row.is_role, ' trying value too: ', params.value)
      if (params.row.is_role) {
        return "Unqualified User (Role)";
      } else {
        return "Unassigned User (Accessor)";
      }
    }
  },
  {
    field: "accessor_or_role",
    headerName: "Accessor or Role",
    flex: 1,
    headerAlign: "center",
    aggregable: false,
    groupable: false,
    //This allows for rendering a react component
    renderCell: (rowInfo) => {
      //  console.log(' Ist accessor or role: ', rowInfo)
      if (rowInfo.row.is_role) {
        return <div> placeHolder </div>;
      }
      return <div> placeHolder </div>;
    },
    valueGetter: (params) => {
      // console.log('The violation type value getter ', params, ' \n params.row.is_role: ', params.row.is_role, ' trying value too: ', params.value)
      if (params.row.is_role) {
        return params.row.role.name;
      } else {
        return params.row.accessor.name;
      }
    }
  },
  {
    id: "detected_on",
    headerName: "Detected On",
    field: "created_date",
    flex: 1,
    type: "date",
    headerAlign: "center",
    aggregable: false,
    groupable: false,
    valueFormatter: (params) => {
      //console.log('VF The created_date on params: ', params)

      if (params.value == null) {
        return "";
      }
      return params.value;
    },
    valueGetter: (params) => {
      //  console.log('VG the created_date in params: ', params)
      if (params.row.created_date == null) {
        return "";
      }
      // console.log('created_date The last modified from row: ', params.row.created_date)
      const date = new Date(params.row.created_date);
      // console.log('to return the created_date in getter: ', date)
      return date;
    }
  },
  {
    id: "last_updated",
    headerName: "Last Updated",
    field: "last_modified",
    flex: 1,
    type: "date",
    headerAlign: "center",
    aggregable: false,
    groupable: false,
    valueFormatter: (params) => {
      //  console.log('VF The last_modified on params: ', params)

      if (params.value == null) {
        return "";
      }
      return params.value;
    },
    valueGetter: (params) => {
      // console.log('VG the last_modified in params: ', params)
      if (params.row.last_modified == null) {
        return "";
      }
      //console.log('last_modified The last modified from row: ', params.row.last_modified)
      const date = new Date(params.row.last_modified);
      //console.log('to return the last_modified in getter: ', date)
      return date;
    }
  },
  {
    headerName: "Suspension",
    type: "string",
    field: "rolesuspension",
    flex: 1,
    aggregable: false,
    groupable: false,
    headerAlign: "center",
    renderCell: (rowInfo) => {
      //  console.log('Row info rolesuspension to view: ', rowInfo)
      if (!rowInfo.row.is_role) {
        return (
          <span>
            <i>N/A</i>
          </span>
        );
      }
      const roleSus = rowInfo.row.rolesuspension;
      //console.log(rowInfo, ' = the rolesuspension  ', roleSus)
      if (!roleSus || roleSus.cleared_date) {
        return <span>None</span>;
      }
      if (roleSus.suspension_activated_date) {
        return (
          <span>
            <span
              className="fa fa-minus-circle text-danger"
              role="img"
              aria-hidden={true}
            />{" "}
            Suspended {roleSus.suspension_activated_date}
          </span>
        );
      }
      const daysRemaining = 4;
      return (
        <span>
          <span
            className="fa fa-hourglass-half"
            role="img"
            aria-hidden={true}
          />{" "}
          {daysRemaining} day{daysRemaining != 1 && "s"} until suspension
        </span>
      );
    },
    valueGetter: (rowInfo) => {
      // console.log('Row info rolesuspension: ', rowInfo)
      if (!rowInfo.row.is_role) {
        //may need to use the is_role property  This needs to be checked HS and maybe a more examples ffor this poroperty
        return "N/A";
      }
      const roleSus = rowInfo.value;
      if (!roleSus || roleSus.cleared_date) {
        return "None";
      }
      if (roleSus.suspension_activated_date) {
        return "Suspended" + roleSus.suspension_activated_date;
      }
      const daysRemaining = 4;
      let daysVerbiage = "";
      if (daysRemaining > 1) {
        daysVerbiage = daysRemaining + " days until suspension";
      } else {
        daysVerbiage = daysRemaining + " day until suspension";
      }

      return daysVerbiage;
    }
  },
  {
    ...GRID_DETAIL_PANEL_TOGGLE_COL_DEF,
    renderHeader: () => "+/-",
    headerAlign: "center",
    sortable: false,
    filterable: false,
    aggregable: false,
    groupable: false,
    valueGetter: (params) => {
      //  console.log('VG the GRID_DETAIL_PANEL_TOGGLE_COL_DEF in params: ', params)

      // console.log('return the GRID_DETAIL_PANEL_TOGGLE_COL_DEF in params: ', params.row.id + "-" + params.row.name)
      return params.row.id + "-" + params.row.url;
    },
    renderCell: (params) => {
      // console.log('renderCell the GRID_DETAIL_PANEL_TOGGLE_COL_DEF in params: ', params)
      return <div>test</div>;
    }
  }
];

const mockUserMembershipViolations = {
  data: {
    violations: [
      {
        id: 1,
        uuid: "123",
        user: {
          sid: "1234",
          name: "Apple Cider 2",
          first_name: "Apple",
          last_name: "Cider 2",
          email: "fererer",
          is_active: true,
          user_type: "user",
          duty_org: "NSA",
          org: "trrere",
          has_adm_account: true,
          url: "www.google.com",
          uuid: "fferer",
          my_permissions: {
            create: true,
            update: true,
            destroy: true,
            write: true,
            read: true
          },
          objectType: "user",
          entity_types: []
        },
        adm_account: false,
        role: {
          name: "April is A ROLE",
          description: "#f00",
          url: "www.twitter.com",
          uuid: "7890",
          parent_object: null,
          userCount: 4,
          my_permissions: [
            {
              add_users: true,
              create: true,
              read: true,
              update: true,
              delete: true,
              grant_user_suspensions: true,
              set_rules: true
            }
          ]
        },
        accessor: {
          sid: "1234",
          name: "An accessor Name",
          first_name: " bfgr",
          last_name: "fererer",
          email: "fererer",
          is_active: true,
          user_type: "user",
          duty_org: "NSA",
          org: "trrere",
          has_adm_account: true,
          url: "www.google.com",
          uuid: "fferer",
          my_permissions: {
            create: true,
            update: true,
            destroy: true,
            write: true,
            read: true
          },
          objectType: "user",
          entity_types: []
        },
        is_role: true,
        created_date: "2023-02-11T14:51:39Z",
        cleared_date: "2023-06-11T14:51:39Z",
        url: "www.fake.com",
        last_modified: "2023-03-11T14:51:39Z",
        rolesuspension: null
      },
      {
        id: 2,
        uuid: "123ab",
        user: {
          sid: "1234",
          name: "Samy Zebra",
          first_name: "Samy",
          last_name: "Zebra",
          email: "fererer",
          is_active: true,
          user_type: "user",
          duty_org: "NSA",
          org: "trrere",
          has_adm_account: true,
          url: "www.google.com",
          uuid: "fferer",
          my_permissions: {
            create: true,
            update: true,
            destroy: true,
            write: true,
            read: true
          },
          objectType: "user",
          entity_types: []
        },
        adm_account: false,
        role: {
          name: "April Role",
          description: "#f00",
          url: "www.twitter.com",
          uuid: "7890",
          parent_object: null,
          userCount: 4,
          my_permissions: [
            {
              add_users: true,
              create: true,
              read: true,
              update: true,
              delete: true,
              grant_user_suspensions: true,
              set_rules: true
            }
          ]
        },
        accessor: {
          sid: "1234",
          name: "Not a Role Accessor Name BOO",
          first_name: " bfgr",
          last_name: "fererer",
          email: "fererer",
          is_active: true,
          user_type: "user",
          duty_org: "NSA",
          org: "trrere",
          has_adm_account: true,
          url: "www.google.com",
          uuid: "fferer",
          my_permissions: {
            create: true,
            update: true,
            destroy: true,
            write: true,
            read: true
          },
          objectType: "user",
          entity_types: []
        },
        is_role: false,
        created_date: "2023-01-11T14:51:39Z",
        cleared_date: "2023-06-11T14:51:39Z",
        url: "www.fake.com",
        last_modified: "2023-05-11T14:51:39Z",
        rolesuspension: null
      }
    ],
    users: [
      {
        sid: "1234",
        name: "geger",
        first_name: " bfgr",
        last_name: "fererer",
        email: "fererer",
        is_active: true,
        user_type: "user",
        duty_org: "NSA",
        org: "trrere",
        has_adm_account: true,
        url: "www.google.com",
        uuid: "fferer",
        my_permissions: {
          create: true,
          update: true,
          destroy: true,
          write: true,
          read: true
        },
        objectType: "user",
        entity_types: []
      },
      {
        sid: "1tt234",
        name: "2geger",
        first_name: " bfgr",
        last_name: "fererer",
        email: "fererer",
        is_active: true,
        user_type: "user",
        duty_org: "NSA",
        org: "trrere",
        has_adm_account: true,
        url: "www.google.com",
        uuid: "fferer",
        my_permissions: {
          create: true,
          update: true,
          destroy: true,
          write: true,
          read: true
        },
        objectType: "user",
        entity_types: []
      }
    ],
    roles: [
      {
        name: "April",
        description: "#f00",
        url: "www.twitter.com",
        uuid: "7890",
        parent_object: null,
        userCount: 4,
        my_permissions: [
          {
            add_users: true,
            create: true,
            read: true,
            update: true,
            delete: true,
            grant_user_suspensions: true,
            set_rules: true
          }
        ]
      }
    ],
    accessors: [
      {
        sid: "1234",
        name: "geger",
        first_name: " bfgr",
        last_name: "fererer",
        email: "fererer",
        is_active: true,
        user_type: "user",
        duty_org: "NSA",
        org: "trrere",
        has_adm_account: true,
        url: "www.google.com",
        uuid: "fferer",
        my_permissions: {
          create: true,
          update: true,
          destroy: true,
          write: true,
          read: true
        },
        objectType: "accessor",
        entity_types: []
      }
    ]
  },
  isFetching: false,
  fetched: false,
  hasError: true,
  errorObj: {}
};

const CommonGrid = ({ ...other }) => {
  const apiRef = useGridApiRef();
  const [cachedInitialState, setCachedInitialState] = React.useState();

  const [sortModel, setSortModel] = React.useState([
    {
      field: "user",
      sort: "desc"
    },
    { field: "detected_on", desc: "desc" }
  ]);

  React.useEffect(() => {
    const getSnapshot = async () => {
      const snapshotFromLocalStorage = await getStateSnapshotFromLocalStorage();
      console.log(
        "Page Mount: Recovered state from localStorage",
        snapshotFromLocalStorage
      );
      setCachedInitialState(snapshotFromLocalStorage);
    };
    getSnapshot();
  }, []);

  const saveSnapshot = React.useCallback(() => {
    const stateSnapshot = apiRef.current.exportState();
    saveStateSnapshotToLocalStorage(stateSnapshot);
  }, [apiRef]);

  if (!cachedInitialState) {
    return <CircularProgress />;
  }

  return (
    <Box
      sx={{
        height: "100%",
        width: "100%",
        "& .datagridHeader": {
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        },
        "& .MuiDataGrid-footerContainer": {
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        },
        "& .MuiTablePagination-root": {
          backgroundColor: "#4d7496",
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        },
        "& .MuiTablePagination-displayedRows": {
          color: "#fff !important",
          fontSize: 13,
          fontWeight: "bolder"
        },
        "& .MuiSelect-standard": {
          fontSize: "13px !important",
          fontWeight: "bolder",
          color: "#fff !important"
        },
        "& .MuiTablePagination-actions": {
          fontSize: 13,
          fontWeight: "bolder",
          color: "#fff !important",
          "& .MuiButtonBase-root": {
            color: "#fff !important",
            fontSize: 13
          }
        },
        "& .MuiTablePagination-selectLabel": {
          fontSize: 13,
          fontWeight: "bolder",
          color: "#fff !important"
        },
        "& .MuiTablePagination-selectIcon": {
          fontSize: "13px !important",
          fontWeight: "bolder",
          color: "#fff !important"
        },
        "& .MuiTablePagination-selectRoot": {
          fontSize: "13px !important",
          fontWeight: "bolder",
          color: "#fff !important"
        },
        "& .MuiTablePagination-input": {
          fontSize: "13px !important",
          fontWeight: "bolder",
          color: "#fff !important"
        },
        "& .MuiDataGrid-columnHeader": {
          backgroundColor: "#4d7496",
          color: "#fff",
          fontSize: 13,
          fontWeight: 700
        },
        "& .MuiDataGrid-columnHeaderCheckbox": {
          backgroundColor: "#4d7496",
          color: "#fff",
          fontSize: 13,
          fontWeight: 700
        },
        "& .MuiDataGrid-cell": {
          border: "1px solid rgb(224, 224, 224)",
          fontSize: 13
        },
        "& .MuiDataGrid-root .MuiDataGrid-cell": {
          whiteSpace: "normal !important",
          wordWwrap: "break-word !important",
          textAlign: "left"
        },
        "& .MuiDataGrid-sortIcon": {
          backgroundColor: "#4d7496",
          color: "#fff",
          fontSize: 13,
          fontWeight: 700,
          opacity: "inherit !important"
        },
        "& .MuiDataGrid-filterIcon": {
          backgroundColor: "#4d7496",
          color: "#fff",
          fontSize: 13,
          fontWeight: 700,
          opacity: "inherit !important"
        },
        "& .MuiDataGrid-columnHeaderTitle": {
          fontWeight: "bolder"
        }
      }}
    >
      {"Tes"}
      <br />
      <br />
      <DataGridPremium
        apiRef={apiRef}
        onStateChange={saveSnapshot}
        rows={[
          {
            id: 1,
            uuid: "123",
            user: {
              sid: "1234",
              name: "Apple Cider 2",
              first_name: "Apple",
              last_name: "Cider 2",
              email: "fererer",
              is_active: true,
              user_type: "user",
              duty_org: "NSA",
              org: "trrere",
              has_adm_account: true,
              url: "www.google.com",
              uuid: "fferer",
              my_permissions: {
                create: true,
                update: true,
                destroy: true,
                write: true,
                read: true
              },
              objectType: "user",
              entity_types: []
            },
            adm_account: false,
            role: {
              name: "April is A ROLE",
              description: "#f00",
              url: "www.twitter.com",
              uuid: "7890",
              parent_object: null,
              userCount: 4,
              my_permissions: [
                {
                  add_users: true,
                  create: true,
                  read: true,
                  update: true,
                  delete: true,
                  grant_user_suspensions: true,
                  set_rules: true
                }
              ]
            },
            accessor: {
              sid: "1234",
              name: "An accessor Name",
              first_name: " bfgr",
              last_name: "fererer",
              email: "fererer",
              is_active: true,
              user_type: "user",
              duty_org: "NSA",
              org: "trrere",
              has_adm_account: true,
              url: "www.google.com",
              uuid: "fferer",
              my_permissions: {
                create: true,
                update: true,
                destroy: true,
                write: true,
                read: true
              },
              objectType: "user",
              entity_types: []
            },
            is_role: true,
            created_date: "2023-02-11T14:51:39Z",
            cleared_date: "2023-06-11T14:51:39Z",
            url: "www.fake.com",
            last_modified: "2023-03-11T14:51:39Z",
            rolesuspension: null
          },
          {
            id: 2,
            uuid: "123ab",
            user: {
              sid: "1234",
              name: "Samy Zebra",
              first_name: "Samy",
              last_name: "Zebra",
              email: "fererer",
              is_active: true,
              user_type: "user",
              duty_org: "NSA",
              org: "trrere",
              has_adm_account: true,
              url: "www.google.com",
              uuid: "fferer",
              my_permissions: {
                create: true,
                update: true,
                destroy: true,
                write: true,
                read: true
              },
              objectType: "user",
              entity_types: []
            },
            adm_account: false,
            role: {
              name: "April Role",
              description: "#f00",
              url: "www.twitter.com",
              uuid: "7890",
              parent_object: null,
              userCount: 4,
              my_permissions: [
                {
                  add_users: true,
                  create: true,
                  read: true,
                  update: true,
                  delete: true,
                  grant_user_suspensions: true,
                  set_rules: true
                }
              ]
            },
            accessor: {
              sid: "1234",
              name: "Not a Role Accessor Name BOO",
              first_name: " bfgr",
              last_name: "fererer",
              email: "fererer",
              is_active: true,
              user_type: "user",
              duty_org: "NSA",
              org: "trrere",
              has_adm_account: true,
              url: "www.google.com",
              uuid: "fferer",
              my_permissions: {
                create: true,
                update: true,
                destroy: true,
                write: true,
                read: true
              },
              objectType: "user",
              entity_types: []
            },
            is_role: false,
            created_date: "2023-01-11T14:51:39Z",
            cleared_date: "2023-06-11T14:51:39Z",
            url: "www.fake.com",
            last_modified: "2023-05-11T14:51:39Z",
            rolesuspension: null
          }
        ]}
        columns={mockViolationColumns}
        sx={{
          width: "100% !important",
          ".MuiDataGrid-iconButtonContainer": {
            visibility: "visible"
          },
          ".MuiDataGrid-sortIcon": {
            opacity: "inherit !important"
          },
          ".MuiTouchRipple-root": {
            opacity: "inherit !important",
            visibility: "visible !important"
          },
          ".MuiDataGrid-menuIconButton": {
            color: "#fff"
          },
          ".MuiDataGrid-columnHeaderTitleContainerContent .MuiCheckbox-root": {
            color: "#ffff !important",
            fontSize: 14,
            fontWeight: "bolder"
          },
          "& .MuiDataGrid-virtualScroller::-webkit-scrollbar": {
            width: "0.4em"
          },
          "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-track": {
            color: "#4d7496"
          },
          "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb": {
            backgroundColor: "#4d7496",
            color: "#fff"
          },
          "& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb:hover": {
            backgroundColor: "#4d7496",
            color: "#fff"
          },
          "& .MuiDataGrid-iconButtonContainer": {
            marginLeft: "25px",
            visibility: "visible !important",
            width: "auto !important"
          },
          "& .MuiDataGrid-cell--withRenderer": {
            fontSize: "13px !important"
          },
          "& .MuiDataGrid-menuIcon": {
            fontSize: 14,
            visibility: "visible !important",
            width: "auto !important",
            fontWeight: "bolder",
            "& .MuiSvgIcon-root": {
              fontSize: 14,
              visibility: "visible !important",
              width: "auto !important",
              fontWeight: "bolder"
            }
          }
        }}
        getDetailPanelContent={other.getDetailPanelContent}
        getDetailPanelHeight={() => "auto"}
        rowThreshold={other.rowThreshold}
        keepColumnPositionIfDraggedOutside={true}
        loading={false}
        paginationMode={"client"}
        slots={{
          noRowsOverlay: CustomNoRowsOverlay,
          toolbar: CustomToolbar
        }}
        checkboxSelection
        experimentalFeatures={{
          ariaV7: true,
          newEditingApi: true
        }}
        getRowClassName={(params) =>
          params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd"
        }
        initialState={{
          cachedInitialState,
          sorting: {
            sortModel: sortModel
          }
        }}
        sortingOrder={["desc", "asc"]}
        sortModel={sortModel}
        onSortModelChange={(model) => {
          // console.log('****************** The column sort order chagnes \n\n\n\n')
          setSortModel(model); //set the state
        }}
        pagination
      />
    </Box>
  );
};

/* CommonGrid.propTypes = {
    columns: PropTypes.array,
    rows: PropTypes.array,
    tableTitle: PropTypes.string
}; */

export default CommonGrid;
lisaWriteJava commented 1 year ago

Hello @romgrk , please let me know what you need from me. I gave the code block above just in case the sandbox didn't load properly to show the issue.

cherniavskii commented 1 year ago

Hi @lisaWriteJava I believe there's a mistake in the initial state merging:

initialState={{
-  cachedInitialState,
+  ...cachedInitialState,
  sorting: {
    sortModel: sortModel
  }
}}

Here's a fixed demo: https://codesandbox.io/s/datagridpremium-basic-forked-w25xxc?file=/src/CommonGrid.jsx

This is a great candidate for the recipe as it's a pretty common pattern 👍🏻

lisaWriteJava commented 1 year ago

Thank you. I'll give this a try Yes I agree it's a good candidate.

lisaWriteJava commented 1 year ago

Ok @cherniavskii I gave it a try. Your sandbox worked perfectly but when I integrated it into our code base the state still wouldn't save. I've forked the sandbox to show how a reload isn't working using my code https://codesandbox.io/s/datagridpremium-basic-forked-khqvcm?file=/src/CommonGrid.jsx

just in case the sandbox has issues like it did in the past below is the code

import React from "react";
import { Box } from '@mui/material';
import CircularProgress from "@mui/material/CircularProgress";
import {
    DataGridPremium,
    GridToolbarContainer,
    GridToolbarColumnsButton,
    GridToolbarFilterButton,
    GridToolbarExport,
    GridToolbarDensitySelector,
    gridClasses,
    useGridApiRef,
} from '@mui/x-data-grid-premium';
import PropTypes from "prop-types";
import { alpha } from "@mui/material/styles";
import { styled } from "@mui/material/styles";

const ODD_OPACITY = 0.2;

//class actions
function saveStateSnapshotToLocalStorage(stateSnapshot) {
    console.info(
        "State update: Update the state snap in localStorage",
        stateSnapshot
    );
    localStorage.setItem("dataGridState", JSON.stringify(stateSnapshot));
}

function getStateSnapshotFromLocalStorage() {
    const stringifiedState = localStorage.getItem("dataGridState");
    return new Promise((resolve) => {
        if (!stringifiedState) {
            return resolve({});
        }
        const stateSnapshot = JSON.parse(stringifiedState);
        return resolve(stateSnapshot);
    });
}

const StripedDataGrid = styled(DataGridPremium)(({ theme }) => ({
    [`& .${gridClasses.row}.even`]: {
        backgroundColor: theme.palette.grey[200],
        "&:hover, &.Mui-hovered": {
            backgroundColor: alpha(theme.palette.primary.main, ODD_OPACITY),
            "@media (hover: none)": {
                backgroundColor: "transparent"
            }
        },
        "&.Mui-selected": {
            backgroundColor: alpha(
                theme.palette.primary.main,
                ODD_OPACITY + theme.palette.action.selectedOpacity
            ),
            "&:hover, &.Mui-hovered": {
                backgroundColor: alpha(
                    theme.palette.primary.main,
                    ODD_OPACITY +
                    theme.palette.action.selectedOpacity +
                    theme.palette.action.hoverOpacity
                ),
                // Reset on touch devices, it doesn't add specificity
                "@media (hover: none)": {
                    backgroundColor: alpha(
                        theme.palette.primary.main,
                        ODD_OPACITY + theme.palette.action.selectedOpacity
                    )
                }
            }
        }
    }
}));

const StyledGridOverlay = styled('div')(({ theme }) => ({
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    height: '100%',
    '& .ant-empty-img-1': {
        fill: theme.palette.mode === 'light' ? '#aeb8c2' : '#262626',
    },
    '& .ant-empty-img-2': {
        fill: theme.palette.mode === 'light' ? '#f5f5f7' : '#595959',
    },
    '& .ant-empty-img-3': {
        fill: theme.palette.mode === 'light' ? '#dce0e6' : '#434343',
    },
    '& .ant-empty-img-4': {
        fill: theme.palette.mode === 'light' ? '#fff' : '#1c1c1c',
    },
    '& .ant-empty-img-5': {
        fillOpacity: theme.palette.mode === 'light' ? '0.8' : '0.08',
        fill: theme.palette.mode === 'light' ? '#f5f5f5' : '#fff',
    },
}));

function CustomNoRowsOverlay() {
    return (
        <StyledGridOverlay>
            <svg
                width="120"
                height="100"
                viewBox="0 0 184 152"
                aria-hidden
                focusable="false"
            >
                <g fill="none" fillRule="evenodd">
                    <g transform="translate(24 31.67)">
                        <ellipse
                            className="ant-empty-img-5"
                            cx="67.797"
                            cy="106.89"
                            rx="67.797"
                            ry="12.668"
                        />
                        <path
                            className="ant-empty-img-1"
                            d="M122.034 69.674L98.109 40.229c-1.148-1.386-2.826-2.225-4.593-2.225h-51.44c-1.766 0-3.444.839-4.592 2.225L13.56 69.674v15.383h108.475V69.674z"
                        />
                        <path
                            className="ant-empty-img-2"
                            d="M33.83 0h67.933a4 4 0 0 1 4 4v93.344a4 4 0 0 1-4 4H33.83a4 4 0 0 1-4-4V4a4 4 0 0 1 4-4z"
                        />
                        <path
                            className="ant-empty-img-3"
                            d="M42.678 9.953h50.237a2 2 0 0 1 2 2V36.91a2 2 0 0 1-2 2H42.678a2 2 0 0 1-2-2V11.953a2 2 0 0 1 2-2zM42.94 49.767h49.713a2.262 2.262 0 1 1 0 4.524H42.94a2.262 2.262 0 0 1 0-4.524zM42.94 61.53h49.713a2.262 2.262 0 1 1 0 4.525H42.94a2.262 2.262 0 0 1 0-4.525zM121.813 105.032c-.775 3.071-3.497 5.36-6.735 5.36H20.515c-3.238 0-5.96-2.29-6.734-5.36a7.309 7.309 0 0 1-.222-1.79V69.675h26.318c2.907 0 5.25 2.448 5.25 5.42v.04c0 2.971 2.37 5.37 5.277 5.37h34.785c2.907 0 5.277-2.421 5.277-5.393V75.1c0-2.972 2.343-5.426 5.25-5.426h26.318v33.569c0 .617-.077 1.216-.221 1.789z"
                        />
                    </g>
                    <path
                        className="ant-empty-img-3"
                        d="M149.121 33.292l-6.83 2.65a1 1 0 0 1-1.317-1.23l1.937-6.207c-2.589-2.944-4.109-6.534-4.109-10.408C138.802 8.102 148.92 0 161.402 0 173.881 0 184 8.102 184 18.097c0 9.995-10.118 18.097-22.599 18.097-4.528 0-8.744-1.066-12.28-2.902z"
                    />
                    <g className="ant-empty-img-4" transform="translate(149.65 15.383)">
                        <ellipse cx="20.654" cy="3.167" rx="2.849" ry="2.815" />
                        <path d="M5.698 5.63H0L2.898.704zM9.259.704h4.985V5.63H9.259z" />
                    </g>
                </g>
            </svg>
            <Box sx={{ mt: 1, fontSize: 16, fontWeight: 'bolder' }}>No Data</Box>
        </StyledGridOverlay>
    );
}

const CommonGrid = ({ /*columns, rows, tableTitle,   sortingOrder, sortModel, onSortModelChange, */
    actionButton, handleSelectedItems, ...other }) => {
    const apiRef = useGridApiRef();
    const [cachedInitialState, setCachedInitialState] = React.useState();
    const [rowSelectionModel, setRowSelectionModel] = React.useState([]);
    const [checkboxSelection, setCheckboxSelection] = React.useState(true);
    const [sortModel, setSortModel] = React.useState([
        {
            field: "user",
            sort: "desc"
        },
        { field: "detected_on", desc: "desc" }
    ]);

    React.useEffect(() => {
        const getSnapshot = async () => {
            const snapshotFromLocalStorage = await getStateSnapshotFromLocalStorage();
            console.log(
                "Page Mount: Recovered state from localStorage",
                snapshotFromLocalStorage
            );
            setCachedInitialState(snapshotFromLocalStorage);
        };
        getSnapshot();
    }, []);

    const saveSnapshot = React.useCallback(() => {
        const stateSnapshot = apiRef.current.exportState();
        saveStateSnapshotToLocalStorage(stateSnapshot);
    }, [apiRef]);

    if (!cachedInitialState) {
        return <CircularProgress />;
    }

    function CustomToolbar() {
        return (
            <GridToolbarContainer sx={{
                width: "100%",
                backgroundColor: "#4d7496",
                color: "#fff !important",
                fontSize: 13,
                fontWeight: "bolder"
            }} >
                <GridToolbarColumnsButton sx={{
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                }} />
                <GridToolbarFilterButton sx={{
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                }} />
                <GridToolbarDensitySelector sx={{
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                }} />
                <GridToolbarExport sx={{
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                }} />

                {actionButton ? actionButton : null}
            </GridToolbarContainer>
        );
    }

    return (
        <Box
            sx={{
                height: "100%",
                width: "100%",
                "& .datagridHeader": {
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                },
                "& .MuiDataGrid-footerContainer": {
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                },
                "& .MuiTablePagination-root": {
                    backgroundColor: "#4d7496",
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder"
                },
                "& .MuiTablePagination-displayedRows": {
                    color: "#fff !important",
                    fontSize: 13,
                    fontWeight: "bolder",
                },
                "& .MuiSelect-standard": {
                    fontSize: "13px !important",
                    fontWeight: "bolder",
                    color: "#fff !important",
                },
                "& .MuiTablePagination-actions": {
                    fontSize: 13,
                    fontWeight: "bolder",
                    color: "#fff !important",
                    "& .MuiButtonBase-root": {
                        color: "#fff !important",
                        fontSize: 13,
                    },
                },
                "& .MuiTablePagination-selectLabel": {
                    fontSize: 13,
                    fontWeight: "bolder",
                    color: "#fff !important",
                },
                "& .MuiTablePagination-selectIcon": {
                    fontSize: "13px !important",
                    fontWeight: "bolder",
                    color: "#fff !important",
                },
                "& .MuiTablePagination-selectRoot": {
                    fontSize: "13px !important",
                    fontWeight: "bolder",
                    color: "#fff !important",
                },
                "& .MuiTablePagination-input": {
                    fontSize: "13px !important",
                    fontWeight: "bolder",
                    color: "#fff !important",
                },
                "& .MuiDataGrid-columnHeader": {
                    backgroundColor: "#4d7496",
                    color: "#fff",
                    fontSize: 13,
                    fontWeight: 700
                },
                "& .MuiDataGrid-columnHeaderCheckbox": {
                    backgroundColor: "#4d7496",
                    color: "#fff",
                    fontSize: 13,
                    fontWeight: 700
                },
                "& .MuiDataGrid-cell": {
                    border: "1px solid rgb(224, 224, 224)",
                    fontSize: 13
                },
                "& .MuiDataGrid-root .MuiDataGrid-cell": {
                    whiteSpace: "normal !important",
                    wordWwrap: "break-word !important",
                    textAlign: 'left'
                },
                "& .MuiDataGrid-sortIcon": {
                    backgroundColor: "#4d7496",
                    color: "#fff",
                    fontSize: 13,
                    fontWeight: 700,
                    opacity: "inherit !important"
                },
                "& .MuiDataGrid-filterIcon": {
                    backgroundColor: "#4d7496",
                    color: "#fff",
                    fontSize: 13,
                    fontWeight: 700,
                    opacity: "inherit !important"
                },
                "& .MuiDataGrid-columnHeaderTitle": {
                    fontWeight: 'bolder'
                }
            }} >
            {/* {tableTitle}  */}TESTING saving state
            <br />
            <br />
            <StripedDataGrid
                apiRef={apiRef}
                onStateChange={saveSnapshot}
                columns={[
                    {
                        id: "user",
                        headerName: "User",
                        field: "user",
                        type: "string",
                        flex: 1,
                        aggregable: false,
                        groupable: false,
                        headerAlign: "center",
                        valueGetter: (params) => {
                            //  console.log('in value getting for User params: ', params)

                            //Asided from everything else, must set somethign in maybe name or a field to do the filter on
                            return params.value.name;
                        },
                        renderCell: (rowInfo) => {
                            // console.log('valueFormatter for User: ', rowInfo)  //you need to verify values below
                            if (rowInfo.row.user.name) {
                                return <div> placeHolder </div>;
                            } else {
                                return <span>Missing Name Info 2</span>;
                            }
                        }
                    },
                    {
                        id: "violation_type",
                        headerName: "Violation Type",
                        field: "is_role",
                        flex: 1,
                        headerAlign: "center",
                        aggregable: false,
                        groupable: false,
                        //Change to value gettter
                        renderCell: (rowInfo) => {
                            return rowInfo.value;
                        },
                        valueGetter: (params) => {
                            // console.log('The violation type value getter ', params, ' \n params.row.is_role: ', params.row.is_role, ' trying value too: ', params.value)
                            if (params.row.is_role) {
                                return "Unqualified User (Role)";
                            } else {
                                return "Unassigned User (Accessor)";
                            }
                        }
                    },
                    {
                        field: "accessor_or_role",
                        headerName: "Accessor or Role",
                        flex: 1,
                        headerAlign: "center",
                        aggregable: false,
                        groupable: false,
                        //This allows for rendering a react component
                        renderCell: (rowInfo) => {
                            //  console.log(' Ist accessor or role: ', rowInfo)
                            if (rowInfo.row.is_role) {
                                return <div> placeHolder </div>;
                            }
                            return <div> placeHolder </div>;
                        },
                        valueGetter: (params) => {
                            // console.log('The violation type value getter ', params, ' \n params.row.is_role: ', params.row.is_role, ' trying value too: ', params.value)
                            if (params.row.is_role) {
                                return params.row.role.name;
                            } else {
                                return params.row.accessor.name;
                            }
                        }
                    },
                    {
                        id: "detected_on",
                        headerName: "Detected On",
                        field: "created_date",
                        flex: 1,
                        type: "date",
                        headerAlign: "center",
                        aggregable: false,
                        groupable: false,
                        valueFormatter: (params) => {
                            //console.log('VF The created_date on params: ', params)

                            if (params.value == null) {
                                return "";
                            }
                            return params.value;
                        },
                        valueGetter: (params) => {
                            //  console.log('VG the created_date in params: ', params)
                            if (params.row.created_date == null) {
                                return "";
                            }
                            // console.log('created_date The last modified from row: ', params.row.created_date)
                            const date = new Date(params.row.created_date);
                            // console.log('to return the created_date in getter: ', date)
                            return date;
                        }
                    },
                    {
                        id: "last_updated",
                        headerName: "Last Updated",
                        field: "last_modified",
                        flex: 1,
                        type: "date",
                        headerAlign: "center",
                        aggregable: false,
                        groupable: false,
                        valueFormatter: (params) => {
                            //  console.log('VF The last_modified on params: ', params)

                            if (params.value == null) {
                                return "";
                            }
                            return params.value;
                        },
                        valueGetter: (params) => {
                            // console.log('VG the last_modified in params: ', params)
                            if (params.row.last_modified == null) {
                                return "";
                            }
                            //console.log('last_modified The last modified from row: ', params.row.last_modified)
                            const date = new Date(params.row.last_modified);
                            //console.log('to return the last_modified in getter: ', date)
                            return date;
                        }
                    },
                    {
                        headerName: "Suspension",
                        type: "string",
                        field: "rolesuspension",
                        flex: 1,
                        aggregable: false,
                        groupable: false,
                        headerAlign: "center",
                        renderCell: (rowInfo) => {
                            //  console.log('Row info rolesuspension to view: ', rowInfo)
                            if (!rowInfo.row.is_role) {
                                return (
                                    <span>
                                        <i>N/A</i>
                                    </span>
                                );
                            }
                            const roleSus = rowInfo.row.rolesuspension;
                            //console.log(rowInfo, ' = the rolesuspension  ', roleSus)
                            if (!roleSus || roleSus.cleared_date) {
                                return <span>None</span>;
                            }
                            if (roleSus.suspension_activated_date) {
                                return (
                                    <span>
                                        <span
                                            className="fa fa-minus-circle text-danger"
                                            role="img"
                                            aria-hidden={true}
                                        />{" "}
                                        Suspended {roleSus.suspension_activated_date}
                                    </span>
                                );
                            }
                            const daysRemaining = 4;
                            return (
                                <span>
                                    <span
                                        className="fa fa-hourglass-half"
                                        role="img"
                                        aria-hidden={true}
                                    />{" "}
                                    {daysRemaining} day{daysRemaining != 1 && "s"} until suspension
                                </span>
                            );
                        },
                        valueGetter: (rowInfo) => {
                            // console.log('Row info rolesuspension: ', rowInfo)
                            if (!rowInfo.row.is_role) {
                                //may need to use the is_role property  This needs to be checked HS and maybe a more examples ffor this poroperty
                                return "N/A";
                            }
                            const roleSus = rowInfo.value;
                            if (!roleSus || roleSus.cleared_date) {
                                return "None";
                            }
                            if (roleSus.suspension_activated_date) {
                                return "Suspended" + roleSus.suspension_activated_date;
                            }
                            const daysRemaining = 4;
                            let daysVerbiage = "";
                            if (daysRemaining > 1) {
                                daysVerbiage = daysRemaining + " days until suspension";
                            } else {
                                daysVerbiage = daysRemaining + " day until suspension";
                            }

                            return daysVerbiage;
                        }
                    },
                ]}
                rows={[
                    {
                        id: 1,
                        uuid: "123",
                        user: {
                            sid: "1234",
                            name: "Apple Cider 2",
                            first_name: "Apple",
                            last_name: "Cider 2",
                            email: "fererer",
                            is_active: true,
                            user_type: "user",
                            duty_org: "NSA",
                            org: "trrere",
                            has_adm_account: true,
                            url: "www.google.com",
                            uuid: "fferer",
                            my_permissions: {
                                create: true,
                                update: true,
                                destroy: true,
                                write: true,
                                read: true
                            },
                            objectType: "user",
                            entity_types: []
                        },
                        adm_account: false,
                        role: {
                            name: "April is A ROLE",
                            description: "#f00",
                            url: "www.twitter.com",
                            uuid: "7890",
                            parent_object: null,
                            userCount: 4,
                            my_permissions: [
                                {
                                    add_users: true,
                                    create: true,
                                    read: true,
                                    update: true,
                                    delete: true,
                                    grant_user_suspensions: true,
                                    set_rules: true
                                }
                            ]
                        },
                        accessor: {
                            sid: "1234",
                            name: "An accessor Name",
                            first_name: " bfgr",
                            last_name: "fererer",
                            email: "fererer",
                            is_active: true,
                            user_type: "user",
                            duty_org: "NSA",
                            org: "trrere",
                            has_adm_account: true,
                            url: "www.google.com",
                            uuid: "fferer",
                            my_permissions: {
                                create: true,
                                update: true,
                                destroy: true,
                                write: true,
                                read: true
                            },
                            objectType: "user",
                            entity_types: []
                        },
                        is_role: true,
                        created_date: "2023-02-11T14:51:39Z",
                        cleared_date: "2023-06-11T14:51:39Z",
                        url: "www.fake.com",
                        last_modified: "2023-03-11T14:51:39Z",
                        rolesuspension: null
                    },
                    {
                        id: 2,
                        uuid: "123ab",
                        user: {
                            sid: "1234",
                            name: "Samy Zebra",
                            first_name: "Samy",
                            last_name: "Zebra",
                            email: "fererer",
                            is_active: true,
                            user_type: "user",
                            duty_org: "NSA",
                            org: "trrere",
                            has_adm_account: true,
                            url: "www.google.com",
                            uuid: "fferer",
                            my_permissions: {
                                create: true,
                                update: true,
                                destroy: true,
                                write: true,
                                read: true
                            },
                            objectType: "user",
                            entity_types: []
                        },
                        adm_account: false,
                        role: {
                            name: "April Role",
                            description: "#f00",
                            url: "www.twitter.com",
                            uuid: "7890",
                            parent_object: null,
                            userCount: 4,
                            my_permissions: [
                                {
                                    add_users: true,
                                    create: true,
                                    read: true,
                                    update: true,
                                    delete: true,
                                    grant_user_suspensions: true,
                                    set_rules: true
                                }
                            ]
                        },
                        accessor: {
                            sid: "1234",
                            name: "Not a Role Accessor Name BOO",
                            first_name: " bfgr",
                            last_name: "fererer",
                            email: "fererer",
                            is_active: true,
                            user_type: "user",
                            duty_org: "NSA",
                            org: "trrere",
                            has_adm_account: true,
                            url: "www.google.com",
                            uuid: "fferer",
                            my_permissions: {
                                create: true,
                                update: true,
                                destroy: true,
                                write: true,
                                read: true
                            },
                            objectType: "user",
                            entity_types: []
                        },
                        is_role: false,
                        created_date: "2023-01-11T14:51:39Z",
                        cleared_date: "2023-06-11T14:51:39Z",
                        url: "www.fake.com",
                        last_modified: "2023-05-11T14:51:39Z",
                        rolesuspension: null
                    }
                ]}
                sx={{
                    width: '100% !important',
                    ".MuiDataGrid-iconButtonContainer": {
                        visibility: "visible"
                    },
                    ".MuiDataGrid-sortIcon": {
                        opacity: "inherit !important"
                    },
                    ".MuiTouchRipple-root": {
                        opacity: "inherit !important",
                        visibility: "visible !important"
                    },
                    ".MuiDataGrid-menuIconButton": {
                        color: "#fff"
                    },
                    ".MuiDataGrid-columnHeaderTitleContainerContent .MuiCheckbox-root": {
                        color: '#ffff !important',
                        fontSize: 14,
                        fontWeight: 'bolder'
                    },
                    '& .MuiDataGrid-virtualScroller::-webkit-scrollbar': {
                        width: '0.4em',
                    },
                    '& .MuiDataGrid-virtualScroller::-webkit-scrollbar-track': {
                        color: '#4d7496',
                    },
                    '& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb': {
                        backgroundColor: '#4d7496',
                        color: '#fff'
                    },
                    '& .MuiDataGrid-virtualScroller::-webkit-scrollbar-thumb:hover': {
                        backgroundColor: '#4d7496',
                        color: '#fff'
                    },
                    '& .MuiDataGrid-iconButtonContainer': {
                        marginLeft: '25px',
                        visibility: 'visible !important',
                        width: 'auto !important',
                    },
                    "& .MuiDataGrid-cell--withRenderer": {
                        fontSize: "13px !important"
                    },
                    "& .MuiDataGrid-menuIcon": {
                        fontSize: 14,
                        visibility: 'visible !important',
                        width: 'auto !important',
                        fontWeight: 'bolder',
                        "& .MuiSvgIcon-root": {
                            fontSize: 14,
                            visibility: 'visible !important',
                            width: 'auto !important',
                            fontWeight: 'bolder',
                        }
                    },
                }}
                getDetailPanelContent={other.getDetailPanelContent}
                getDetailPanelHeight={() => 'auto'}
                rowThreshold={other.rowThreshold}
                keepColumnPositionIfDraggedOutside={true}
                loading={false}
                paginationMode={"client"}
                slots={{
                    noRowsOverlay: CustomNoRowsOverlay,
                    toolbar: CustomToolbar,
                }}
                checkboxSelection
                experimentalFeatures={{
                    ariaV7: true,
                    newEditingApi: true
                }}
                getRowClassName={(params) =>
                    params.indexRelativeToCurrentPage % 2 === 0 ? "even" : "odd"
                }
                initialState={{
                    ...cachedInitialState,
                    sorting: {
                        sortModel: sortModel
                    }
                }}
                sortingOrder={["desc", "asc"]}
                sortModel={sortModel}
                onSortModelChange={(model) => {
                    // console.log('****************** The column sort order chagnes \n\n\n\n')
                    setSortModel(model); //set the state
                }}
                pagination
            />
        </Box>
    );
};

/* CommonGrid.propTypes = {
    columns: PropTypes.array,
    rows: PropTypes.array,
    tableTitle: PropTypes.string
}; */

export default CommonGrid;
lisaWriteJava commented 1 year ago

So after fooling with this a bit more I got it working. Not sure why the example didn't work but after playing in the my code the state was saved and retrieved as expected.

lisaWriteJava commented 1 year ago

if using multiple grids in the same app, give the local storage a unique name for each. Otherwise the properties will get mingled.