1

I am trying to add a column to the react table using an add column button, but I am a little confused on how to implement this. I want to be able to add a column to the corresponding side that the button is clicked. Do I need to create a custom function and add it to my columns array or is there an easier way to implement this?

X-Matrix

Here is the code.

/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable guard-for-in */
/* eslint-disable no-restricted-syntax */
/* eslint-disable react/jsx-props-no-spreading */
import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { useTable } from 'react-table';
import data from '../Data/data.json';
import './Table.css';

const Styles = styled.div`

  table {
    border-spacing: 0;
    border: 1px solid black;
    width: 970px;

    tr {
      :last-child {
        td {
          border-bottom: 0;
          height: 500px;
        }
      }
    }

    th,

    td {
      margin: 0;
      padding: 0.5rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;

      :last-child {
        border-right: 0;
      }
    }
  }
`;

const HarveyBall = (initialValue) => {
  const [value, setValue] = useState(initialValue);
  
  const onClick = () => {
    if(value === 'Empty'){
      setValue('Quarter');
    }
    if(value === 'Quarter'){
      setValue('Half');
    }
    if(value === 'Half'){
      setValue('Three-Quarter');
    }
    if(value === 'Three-Quarter'){
      setValue('Full');
    }
    if(value === 'Full'){
      setValue('Empty');
    }
  };

  if(value === "Empty"){
    return (
      <div type="button" label="Empty" className="harvey none" onClick={onClick} />
    );
  }

  if(value === "Quarter"){
    return (
      <div type="button" label="Quarter" className="harvey quarters quarter" onClick={onClick} />
    );
  }

  if(value === "Half"){
    return (
      <div type="button" label="Half" className="harvey quarters half" onClick={onClick} />
    );
  }

  if(value === "Three-Quarter"){
    return (
      <div type="button" label="Three-Quarter" className="harvey quarters three-quarters" onClick={onClick} />
    );
  }

  if(value === "Full"){
    return (
      <div type="button" label="Full" className="harvey quarters full" onClick={onClick} />
    );
  }
  return null;
};

const defaultPropGetter = () => ({});

const EditableCell = ({
  value: initialValue,
  row: { index },
  column: { id },
  updateMyData, 
}) => {

  const [value, setValue] = React.useState(initialValue);

  const onChange = e => {
    setValue(e.target.value);
  };

  const onBlur = () => {
    updateMyData(index, id, value);
  };

  React.useEffect(() => {
    setValue(initialValue);
  }, [initialValue]);

  return id === "strategicInitiative" || index === 5 ? <textarea value={value} onChange={onChange} onBlur={onBlur} style={{ width: '100%', focus: 'none', outline: 'none', border: 'none',resize: 'none', expand: {height: '1em', width: '50%', padding: '3px'}}}/> : HarveyBall(initialValue);
 
};

const defaultColumn = {
  Cell: EditableCell,
};

function Table({ columns, getHeaderProps = defaultPropGetter, updateMyData, }) {
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow,
  } = useTable({
    columns,
    data,
    updateMyData,
    defaultColumn,
  });

  return (
    <table {...getTableProps()}>
      <thead>
        {headerGroups.map(headerGroup => (
          <tr {...headerGroup.getHeaderGroupProps()}>
            {headerGroup.headers.map(column => column.hideHeader === false ? null : (
              <th
              {...column.getHeaderProps([
                {
                  className: column.className,
                  style: column.style,
                },
                getHeaderProps(column),
              ])}
            >
              {column.render('Header')}
            </th>
             ))}
          </tr>
        ))}
      </thead>
      <tbody {...getTableBodyProps()}>
        {rows.map((row) => {
          prepareRow(row);
          return (
            <tr {...row.getRowProps()}>
              {row.cells.map(cell => {
                return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>;
              })}
            </tr>
          );
        })}
      </tbody>
    </table>
  );
}

export default function MatrixTable() {

  const addTasks = () => {
    const taskLength = [];
    for(let i = 0; i < data.length; i += 1){
      for(const [key] of Object.entries(data[i])){
        if(key.includes("T")){
          taskLength.push(key[1]);
        }
      }
    }

    const newTaskLength = (parseInt(taskLength[taskLength.length - 1], 10) + 1) ;

    for(let i = 0; i < data.length; i += 1){
      data[i][`T${newTaskLength}`] = "";
    }
  };

  const taskButton = () => (
    <>
      <div>Tasks</div>
      <button onClick={addColumns} type='button'>Add Column</button>
    </>
  );

  const goalButton = () => (
    <>
      <div>Goals</div>
      <button type='button'>Add Column</button>
    </>
  );

  const columns = React.useMemo(
    () => [
      {
        Header: () => taskButton(),
        accessor: 'tasks',
        style: {
          width: '255px',
          height: '49px',
          background: '#fdf5ed',
          fontSize: '16px',
          color: '#f2994a',
          textAlign: 'center',
          lineHeight: '49px',
        },
        columns: [
          {
            hideHeader: false,
            accessor: 'T1'
          },
          {
            hideHeader: false,
            accessor: 'T2'
          },
          {
            hideHeader: false,
            accessor: 'T3'
          },
          {
            hideHeader: false,
            accessor: 'T4'
          },
          {
            hideHeader: false,
            accessor: 'T5'
          },
        ]
          },
          {
            Header: "Strategic Inititiatives",
            accessor: "strategicInitiative ",
            style: {
              color: '#323b3e',
              width: '460px',
              height: '49px',
              background: '#f2f2f2',
              textAlign: 'center',
              lineHeight: '49px',
            },
            columns: [
              {
                hideHeader: false,
                accessor: 'strategicInitiative'
              }
            ]
          },
          {
            Header: goalButton(),
            accessor: 'goals',
            style: {
              color: '#56ccf2',
              width: '255px',
              height: '49px',
              background: '#f8fcfe',
              textAlign: 'center',
              lineHeight: '49px',
            },
            columns: [
              {
                hideHeader: false,
                accessor: 'G1'
              },
              {
                hideHeader: false,
                accessor: 'G2'
              },
              {
                hideHeader: false,
                accessor: 'G3'
              },
              {
                hideHeader: false,
                accessor: 'G4'
              },
              {
                hideHeader: false,
                accessor: 'G5'
              } 
            ]
          },  
        ],
    []
  );

  const addColumns = () => {
    for(let i = 0; i < columns.length; i += 1) {
      if(columns[i].accessor === "tasks"){
        console.log(columns[i]);
        columns[i].columns.push({
          hideHeader: false,
          accessor: 'T6'
        });
      }
    }
  };

  addColumns();

  const [, setData] = useState(data);

  useEffect(() => {
    setData(data);
  }, [data]);

  const updateMyData = (rowIndex, columnId, value) => {
  
    setData(old =>
      old.map((row, index) => {
        if (index === rowIndex) {
          return {
            ...old[rowIndex],
            [columnId]: value,
          };
        }
        return row;
      })
    );
  };

  return (
    <Styles>
      <Table columns={columns} data={data} updateMyData={updateMyData}/>
    </Styles>
  );
}

2 Answers 2

2

I would imagine that modifying the array columns which stores the data which the table uses to render, would be the correct solution. Once columns is updated, your table should re-render which will then include the updated column.

Sign up to request clarification or add additional context in comments.

1 Comment

Welcome to SO. Please add code to illustrate your answer and review how to write a good answer here: stackoverflow.com/help/how-to-answer
1

Yeah, changing columns array(not by mutation, but by setting columns prop to new required array) is the best way to go.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.