-
Notifications
You must be signed in to change notification settings - Fork 80
added sorting feature on button click #202
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import ContextMenu from "./ContextMenu"; | ||
| import { useContextMenu } from "../hooks/useContextMenu"; | ||
| import { useState, useEffect } from "react"; | ||
| import { useState, useEffect, useMemo } from "react"; | ||
| import { transformProject } from "../api"; | ||
| import { useProjectContext } from "../context/ProjectContext"; | ||
| import { | ||
|
|
@@ -36,6 +36,7 @@ const Table = ({ projectId, data: externalData }) => { | |
| const [columns, setColumns] = useState([]); | ||
| const [editingCell, setEditingCell] = useState(null); | ||
| const [editValue, setEditValue] = useState(""); | ||
| const [sortConfig, setSortConfig] = useState(null); | ||
| const { isOpen, position, contextData, open, close } = useContextMenu(); | ||
|
|
||
| const [inputConfig, setInputConfig] = useState(null); | ||
|
|
@@ -63,6 +64,48 @@ const Table = ({ projectId, data: externalData }) => { | |
| updateData(columns, rows, newDtypes); | ||
| }; | ||
|
|
||
| const handleSort = (columnIndex) => { | ||
| let direction = "ascending"; | ||
| if (sortConfig && sortConfig.key === columnIndex && sortConfig.direction === "ascending") { | ||
| direction = "descending"; | ||
| } else if (sortConfig && sortConfig.key === columnIndex && sortConfig.direction === "descending") { | ||
| direction = null; | ||
| } | ||
|
|
||
| if (direction) { | ||
| setSortConfig({ key: columnIndex, direction }); | ||
| } else { | ||
| setSortConfig(null); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] S.No. (serial number) column is inadvertently sortable The Suggestion: Or render a plain
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW] handleSort logic is verbose and uses an indirect null-direction pattern The function sets Suggestion: |
||
| } | ||
| }; | ||
|
|
||
| const sortedData = useMemo(() => { | ||
| let sortableData = [...data]; | ||
| if (sortConfig !== null) { | ||
| sortableData.sort((a, b) => { | ||
| let aValue = a[sortConfig.key]; | ||
| let bValue = b[sortConfig.key]; | ||
|
|
||
| // Attempt to parse as numbers for natural numerical sorting | ||
| const numA = Number(aValue); | ||
| const numB = Number(bValue); | ||
| if (aValue !== "" && bValue !== "" && !isNaN(numA) && !isNaN(numB)) { | ||
| aValue = numA; | ||
| bValue = numB; | ||
| } | ||
|
|
||
| if (aValue < bValue) { | ||
| return sortConfig.direction === "ascending" ? -1 : 1; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW] String comparison is not locale-aware When values are not numeric, the comparator uses raw Suggestion: |
||
| if (aValue > bValue) { | ||
| return sortConfig.direction === "ascending" ? 1 : -1; | ||
| } | ||
| return 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] Sort comparator does not handle null, undefined, or mixed-type values The numeric guard only checks
This produces visually wrong orderings that are difficult to debug. Suggestion:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [MEDIUM] useMemo unnecessarily copies the full array when sort is inactive Even when Suggestion:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW] No tests added for the new sorting feature The PR introduces a non-trivial sorting feature with three distinct states, a custom comparator with numeric type detection, and Critical cases that should be covered:
Suggestion:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [INFO] Positive: useMemo is correctly applied for sort computation Placing the sort inside Suggestion: |
||
| }); | ||
| } | ||
| return sortableData; | ||
| }, [data, sortConfig]); | ||
|
|
||
| const handleAddRow = async (index) => { | ||
| try { | ||
| const response = await transformProject(projectId, { | ||
|
|
@@ -231,17 +274,27 @@ const Table = ({ projectId, data: externalData }) => { | |
| className="py-1.5 px-3 border-b border-gray-200 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" | ||
| onContextMenu={(e) => open(e, { type: "column", columnIndex })} | ||
| > | ||
| <button className="w-full text-left text-gray-500 hover:text-gray-700 hover:bg-gray-100 py-0.5 px-1.5 rounded-md transition-colors duration-150"> | ||
| {column} | ||
| {column !== "S.No." && <DtypeBadge dtype={dtypes[column]} />} | ||
| <button | ||
| onClick={() => handleSort(columnIndex)} | ||
| className="w-full flex items-center justify-between text-left text-gray-500 hover:text-gray-700 hover:bg-gray-100 py-0.5 px-1.5 rounded-md transition-colors duration-150" | ||
| > | ||
| <span className="flex items-center gap-1.5"> | ||
| {column} | ||
| {column !== "S.No." && <DtypeBadge dtype={dtypes[column]} />} | ||
| </span> | ||
| {sortConfig && sortConfig.key === columnIndex && ( | ||
| <span className="text-[10px] ml-1"> | ||
| {sortConfig.direction === "ascending" ? "▲" : "▼"} | ||
| </span> | ||
| )} | ||
| </button> | ||
| </th> | ||
| ))} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LOW] Missing ARIA aria-sort attributes reduce accessibility The WAI-ARIA specification defines Suggestion: Also wrap the arrow character in a visually-hidden label:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [INFO] Positive: Sort indicator rendering is clean and well-structured The conditional rendering of ▲/▼ only for Suggestion: |
||
| </tr> | ||
| </thead> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [HIGH] Row index mismatch breaks cell editing when sort is active The tbody now iterates over For example: if a row originally at index 5 becomes index 0 after sorting, clicking "edit" stores Previous behavior: New behavior: Suggestion: |
||
|
|
||
| <tbody> | ||
| {data.map((row, rowIndex) => ( | ||
| {sortedData.map((row, rowIndex) => ( | ||
| <tr | ||
| key={rowIndex} | ||
| className="border-b border-gray-100 hover:bg-gray-50 transition-colors duration-150" | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[MEDIUM] sortConfig is not reset when external data changes
When
externalData(the prop) is refreshed — after a server sync, row addition, or project switch —sortConfigpersists. The user will continue to see a sorted view over a new dataset, and the sort arrow indicator stays visible. If columns are removed or reordered between refreshes,sortConfig.key(a column index) will silently point to the wrong column.Suggestion:
If columns can change independently, also reset on a column-identity change.