imballinst / react-bs-datatable

Bootstrap datatable without jQuery. Features include: filter, sort, pagination, checkbox, and control customization.
https://imballinst.github.io/react-bs-datatable
MIT License
59 stars 20 forks source link

Can't get controlled sort to work in custom header #183

Closed devMagno closed 1 year ago

devMagno commented 1 year ago

(english is not my main language so sorry if there are any errors)

Hello there! I need some help, I'm trying to add a DataTable with custom header and controlled sorting based on state and I'm struggling really hard, can someone help me? My page looks like this:

function PainelDeControleAtendimentos() {
    const initialSort = { prop: 'abertos_ontem_sum', order: 'asc' }

    const [ordenacao, setOrdenacao] = useState(initialSort)

    const headers = [
        { 
            title: unidadeSelecionada ? 'Agente' : 'Unidade',
            prop: unidadeSelecionada ? 'producao_agente' : 'producao_unidade',
            isFilterable: true, 
        },
        { title: 'Ontem', prop: 'abertos_ontem_sum', isSortable: true },
        { title: 'Hoje', prop: 'abertos_hoje_sum', isSortable: true },
        { title: 'Total', prop: 'abertos_total_sum', isSortable: true },
        { title: 'Agendados', prop: 'agendados_sum', isSortable: true },
        { title: 'Ontem', prop: 'tabulados_ontem_sum', isSortable: true },
        { title: 'Hoje', prop: 'tabulados_hoje_sum', isSortable: true },
    ]

   const handleChangeSort = (sort) => {
        if (!sort)
            return

        if (ordenacao.prop === sort.prop) {
            setOrdenacao((prev) => ({ prop: sort.prop, order: prev.order === 'asc' ? 'desc' : 'asc' }))
        } else {
            setOrdenacao(sort)
        }
    }

    return (
  <DatatableWrapper
    body={dadosTabela}
    headers={headers}
    paginationOptionsProps={{
      initialState: {
        rowsPerPage: 10,
      },
    }}
    sortProps={{
      initialState: initialSort,
    }}
  >
    <Table striped bordered hover className="align-middle mb-3">
      <TableHead
        ordenacaoAtual={ordenacao}
        handleChangeSort={handleChangeSort}
        unidadeSelecionada={unidadeSelecionada}
      />

      <TableHeader
        controlledProps={{
          sortState: {
            prop: ordenacao.prop,
            order: ordenacao.order,
          },
          onSortChange: handleChangeSort,
        }}
      />

      <TableBody>
        {(rows) =>
          rows.map((row, index) => (
            <tr key={`${row.id}-${index}`}>
              <td className="text-start">
                {!unidadeSelecionada ? (
                  <Tooltip value="Clique para filtrar">
                      <Button
                        variant="link"
                        className="p-0 text-start"
                        onClick={() =>
                          handleBranchNameClick(row.producao_unidade)
                        }
                      >
                        {row.producao_unidade}
                      </Button>
                  </Tooltip>
                ) : (
                  <span>{row.producao_agente}</span>
                )}
              </td>
              <td className="text-center">{row?.abertos_ontem_sum || 0}</td>
              <td className="text-center">{row?.abertos_hoje_sum || 0}</td>
              <td className="text-center">{row?.abertos_total_sum || 0}</td>
              <td className="text-center">{row?.agendados_sum || 0}</td>
              <td className="text-center">{row?.tabulados_ontem_sum || 0}</td>
              <td className="text-center">{row?.tabulados_hoje_sum || 0}</td>
            </tr>
          ))
        }
        </TableBody>
      </Table>
    </DatatableWrapper>
  );

}

(TableHead component is my own custom component, that handles sort change and TableHeader is react-bs-datatable's component, that I'm currently hiding using CSS)

Is there an easier to create a custom table header component or maybe I'm doing something wrong with my controlled sorting? I need some help fixing it one way or the other

imballinst commented 1 year ago

hi @devMagno, thanks for creating an understandable issue! At the moment, unfortunately when we want to make a controlled table, we have to define like, everything. So we can't just define the sort state only, but we have to define sort state, filter state, etc.

Example sandbox here: https://codesandbox.io/s/clever-danilo-3dfdvl?file=/src/App.tsx (note that the example here is not optimized; so if you're using it in your use case, use it with caution)

However, making only certain things controlled perhaps is a better way to go. I'll see what I can do to tweak it. Meanwhile, would the temporary solution above sufficient for you?

imballinst commented 1 year ago

hi @devMagno, after contemplating, I don't think I want to support partial controlled table, because the nature of controlled tables are that, consumers are in control of the table and as such, if we are allowing partially controlled table, it may lead to some edge cases (and more complexity in the codebase).

What I suggest is, keep using uncontrolled table, but take advantage of useDatatableWrapper hook. Something like this sandbox: https://codesandbox.io/s/crazy-lucy-2fw4t2?file=/src/App.tsx.

function SemiControlledTableSetter({ sortState }: { sortState: SortType }) {
  const { onSortChange } = useDatatableWrapper();
  useEffect(() => {
    onSortChange(sortState);
  }, [onSortChange, sortState]);

  return null;
}

<DatatableWrapper
  body={STORY_BODY}
  headers={STORY_HEADERS}
  paginationOptionsProps={{
    initialState: {
      rowsPerPage: 10,
      options: [5, 10, 15, 20]
    }
  }}
>
  <SemiControlledTableSetter sortState={sortState} />

  {/* ... */}
</DatatableWrapper>

So the way it works above is that, we are taking advantage of the <DatatableWrapper> being a React.Context. Inside SemiControlledTableSetter, we accept the prop sortState, in which we will propagate the sort state inside the table internals. That way, we can somewhat "control" the table from outside, but the rest of the mechanism are still using the internals for uncontrolled table. Hence, we can achieve partially controlled table.

Let me know if that helps. Thanks!

devMagno commented 1 year ago

Hi @imballinst, thanks for the quick reply! I'll try the useDatatableWrapper and I'll let you know if it works! Thank you!

devMagno commented 1 year ago

@imballinst It worked exactly as I expected, thanks!!

imballinst commented 1 year ago

Awesome, happy to help @devMagno! I'll close this issue then, let me know if there are other things that I can help with šŸ™‡ā€ā™‚ļø