Value is not captured after clicking in the option in a dropdown using the primereact datatable with cell editor

116 views Asked by At

The code is ready to work at codesandbox

Using the mouse to select an option from the dropdown doesn't set the value in the "Country" column of the datatable. But, using the keyboard (tab ,enter ), said value is updated in the primereact datatable with cell editor.

App.tsx:

import React, { useEffect, useState } from "react";
import { DataTable } from "primereact/datatable";
import { Column, ColumnEvent, ColumnEditorOptions } from "primereact/column";
import { InputText } from "primereact/inputtext";
import {
  InputNumber,
  InputNumberValueChangeEvent
} from "primereact/inputnumber";
import { ProductService } from "./service/ProductService";
import { Dropdown, DropdownChangeEvent } from "primereact/dropdown";

interface Product {
  id: string;
  code: string;
  name: string;
  description: string;
  image: string;
  price: number;
  category: string;
  quantity: number;
  inventoryStatus: string;
  rating: number;
}

interface ColumnMeta {
  field: string;
  header: string;
}

interface Country {
  name: string;
  code: string;
}

export default function CellEditingDemo() {
  const [products, setProducts] = useState<Product[] | null>(null);

  const columns: ColumnMeta[] = [
    { field: "country", header: "Country" },
    { field: "code", header: "Code" },
    { field: "name", header: "Name" },
    { field: "quantity", header: "Quantity" },
    { field: "price", header: "Price" }
  ];

  useEffect(() => {
    ProductService.getProductsSmall().then((data) => setProducts(data));
  }, []);

  const isPositiveInteger = (val: any) => {
    let str = String(val);

    str = str.trim();

    if (!str) {
      return false;
    }

    str = str.replace(/^0+/, "") || "0";
    let n = Math.floor(Number(str));

    return n !== Infinity && String(n) === str && n >= 0;
  };

  const onCellEditComplete = (e: ColumnEvent) => {
    let { rowData, newValue, field, originalEvent: event } = e;

    switch (field) {
      case "quantity":
      case "price":
        if (isPositiveInteger(newValue)) rowData[field] = newValue;
        else event.preventDefault();
        break;
      case "country":
        if (typeof newValue == "object") rowData[field] = newValue.name;
        else event.preventDefault();
        break;
      default:
        if (newValue.trim().length > 0) {
          rowData[field] = newValue;
        } else {
          console.log("before prevent default");
          event.preventDefault();
        }
        break;
    }
  };

  const cellEditor = (options: ColumnEditorOptions) => {
    if (options.field === "price") return priceEditor(options);
    else if (options.field === "country") return dropdownEditor(options);
    else return textEditor(options);
  };

  const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);
  const dropdownEditor = (options: ColumnEditorOptions) => {
    const countries: Country[] = [
      { name: "Australia", code: "AU" },
      { name: "Brazil", code: "BR" },
      { name: "China", code: "CN" },
      { name: "Egypt", code: "EG" },
      { name: "France", code: "FR" },
      { name: "Germany", code: "DE" },
      { name: "India", code: "IN" },
      { name: "Japan", code: "JP" },
      { name: "Spain", code: "ES" },
      { name: "United States", code: "US" }
    ];

    return (
      <div className="flex">
        <Dropdown
          value={selectedCountry}
          onChange={(e: DropdownChangeEvent) => {
            setSelectedCountry(e.value);
            options.editorCallback!(e.target.value);
            console.log("filterEditor onChange triggered");
          }}
          options={countries}
          optionLabel="name"
          placeholder="Select a Country"
          className="w-full md:w-14rem"
        />
      </div>
    );
  };

  const textEditor = (options: ColumnEditorOptions) => {
    return (
      <InputText
        type="text"
        value={options.value}
        onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
          options.editorCallback!(e.target.value)
        }
      />
    );
  };

  const priceEditor = (options: ColumnEditorOptions) => {
    return (
      <InputNumber
        value={options.value}
        onValueChange={(e: InputNumberValueChangeEvent) =>
          options.editorCallback!(e.value)
        }
        mode="currency"
        currency="USD"
        locale="en-US"
      />
    );
  };

  const priceBodyTemplate = (rowData: Product) => {
    return new Intl.NumberFormat("en-US", {
      style: "currency",
      currency: "USD"
    }).format(rowData.price);
  };

  return (
    <div className="card p-fluid">
      <DataTable
        value={products}
        editMode="cell"
        tableStyle={{ minWidth: "50rem" }}
      >
        {columns.map(({ field, header }) => {
          return (
            <Column
              key={field}
              field={field}
              header={header}
              style={{ width: "20%" }}
              body={field === "price" && priceBodyTemplate}
              editor={(options) => cellEditor(options)}
              onCellEditComplete={onCellEditComplete}
            />
          );
        })}
      </DataTable>
    </div>
  );
}

./service/ProductService.tsx:

export const ProductService = {
  getProductsData() {
    return [
      {
        id: "1000",
        code: "f230fh0g3",
        name: "Bamboo Watch",
        country: "Country Origin",
        image: "bamboo-watch.jpg",
        price: 65,
        category: "Accessories",
        quantity: 24,
        inventoryStatus: "INSTOCK",
        rating: 5
      },
      {
        id: "1001",
        code: "nvklal433",
        name: "Black Watch",
        country: "Country Origin",
        image: "black-watch.jpg",
        price: 72,
        category: "Accessories",
        quantity: 61,
        inventoryStatus: "INSTOCK",
        rating: 4
      },
      {
        id: "1002",
        code: "zz21cz3c1",
        name: "Blue Band",
        country: "Country Origin",
        image: "blue-band.jpg",
        price: 79,
        category: "Fitness",
        quantity: 2,
        inventoryStatus: "LOWSTOCK",
        rating: 3
      },
      {
        id: "1003",
        code: "244wgerg2",
        name: "Blue T-Shirt",
        country: "Country Origin",
        image: "blue-t-shirt.jpg",
        price: 29,
        category: "Clothing",
        quantity: 25,
        inventoryStatus: "INSTOCK",
        rating: 5
      },
      {
        id: "1004",
        code: "h456wer53",
        name: "Bracelet",
        country: "Country Origin",
        image: "bracelet.jpg",
        price: 15,
        category: "Accessories",
        quantity: 73,
        inventoryStatus: "INSTOCK",
        rating: 4
      }
    ];
  },

  getProductsSmall() {
    return Promise.resolve(this.getProductsData().slice(0, 5));
  }
};

Screensample of the problem.

0

There are 0 answers