fix: resolve column filter checkboxes not responding to clicks

Replace label+hidden-input pattern in ColumnFilterPopover with direct
onClick on div — browsers don't reliably fire onChange for display:none
inputs activated via label click. Also adds missing columnFilters to
useMemo deps in ProductDescriptions and ArticleDetails, and restores
rowStatuses prop wiring in ProductDescriptions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Vidal Wolf
2026-04-08 19:49:48 +02:00
co-authored by Claude Sonnet 4.6
parent 71a4897f28
commit d3675bba0b
4 changed files with 29 additions and 15 deletions
+1
View File
@@ -369,6 +369,7 @@ export default function App() {
<ProductDescriptions
data={appState.data}
onEdit={(index) => setEditingRowIndex(index)}
rowStatuses={rowStatuses}
/>
)}
{activeModule === 'matrix' && (
+1 -1
View File
@@ -71,7 +71,7 @@ export function ArticleDetails({ data, onEdit, rowStatuses }: ArticleDetailsProp
}
return result;
}, [data, activeTab, search, lineFilter, sortCol, sortDesc]);
}, [data, activeTab, search, lineFilter, columnFilters, sortCol, sortDesc]);
const paginatedData = useMemo(() => {
const start = (page - 1) * pageSize;
+11 -9
View File
@@ -1,5 +1,5 @@
import React, { useState, useMemo } from 'react';
import { Search, Check, X } from 'lucide-react';
import { Search, Check } from 'lucide-react';
import { cn } from '../lib/utils';
interface ColumnFilterPopoverProps {
@@ -54,21 +54,23 @@ export function ColumnFilterPopover({
<div className="max-h-48 overflow-y-auto space-y-0.5 pr-1 custom-scrollbar">
{filteredValues.map(val => (
<label key={val} className="flex items-center gap-2 p-1.5 hover:bg-slate-700/50 rounded cursor-pointer group">
<div
key={val}
role="checkbox"
aria-checked={selectedValues.includes(val)}
tabIndex={0}
onClick={() => onToggle(val)}
onKeyDown={(e) => { if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); onToggle(val); } }}
className="flex items-center gap-2 p-1.5 hover:bg-slate-700/50 rounded cursor-pointer group"
>
<div className={cn(
"w-4 h-4 rounded border flex items-center justify-center shrink-0 transition-colors",
selectedValues.includes(val) ? "bg-blue-600 border-blue-600" : "border-slate-600 bg-slate-900 group-hover:border-slate-500"
)}>
{selectedValues.includes(val) && <Check className="w-3 h-3 text-white" />}
</div>
<input
type="checkbox"
className="hidden"
checked={selectedValues.includes(val)}
onChange={() => onToggle(val)}
/>
<span className="text-xs text-slate-300 truncate" title={val}>{val || '(Empty)'}</span>
</label>
</div>
))}
{filteredValues.length === 0 && (
<div className="text-[10px] text-slate-500 text-center py-4 italic">No values found</div>
+16 -5
View File
@@ -7,6 +7,7 @@ import { ColumnFilterPopover } from './ColumnFilterPopover';
interface ProductDescriptionsProps {
data: ExcelRow[];
onEdit: (index: number) => void;
rowStatuses: Record<string, string>;
}
type TabType = 'all' | 'missingLongDE' | 'missingLongEN' | 'missingLongAny' | 'missingShortDE' | 'missingShortEN' | 'missingShortAny' | 'complete' | 'incomplete';
@@ -14,7 +15,7 @@ type TabType = 'all' | 'missingLongDE' | 'missingLongEN' | 'missingLongAny' | 'm
// Description columns that should only have Present/Missing filters
const DESCRIPTION_COLUMNS = [COLUMNS.LONG_DE, COLUMNS.LONG_EN, COLUMNS.SHORT_DE, COLUMNS.SHORT_EN];
export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps) {
export function ProductDescriptions({ data, onEdit, rowStatuses }: ProductDescriptionsProps) {
const [activeTab, setActiveTab] = useState<TabType>('all');
const [search, setSearch] = useState('');
const [lineFilter, setLineFilter] = useState('');
@@ -91,7 +92,7 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
}
return result;
}, [data, activeTab, search, lineFilter, licenseFilter, sortCol, sortDesc]);
}, [data, activeTab, search, lineFilter, licenseFilter, columnFilters, sortCol, sortDesc]);
const paginatedData = useMemo(() => {
const start = (page - 1) * pageSize;
@@ -304,8 +305,17 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
</tr>
</thead>
<tbody className="divide-y divide-slate-700/50">
{paginatedData.map(({ row, index }) => (
<tr key={index} className={cn("transition-colors", getRowColor(row))}>
{paginatedData.map(({ row, index }) => {
const isPending = rowStatuses[String(row[COLUMNS.ARTICLE_NO])] === 'pending';
return (
<tr
key={index}
className={cn(
"transition-colors",
getRowColor(row),
isPending ? "bg-yellow-400/20 border-l-4 border-l-yellow-400" : ""
)}
>
<td className="px-4 py-3 font-mono text-slate-300 text-xs">{row[COLUMNS.ARTICLE_NO]}</td>
<td className="px-4 py-3 font-medium text-white max-w-[200px] truncate" title={row[COLUMNS.ARTICLE_NAME]}>{row[COLUMNS.ARTICLE_NAME]}</td>
<td className="px-4 py-3 text-slate-300 text-xs">{row[COLUMNS.LINE]}</td>
@@ -334,7 +344,8 @@ export function ProductDescriptions({ data, onEdit }: ProductDescriptionsProps)
</button>
</td>
</tr>
))}
);
})}
{paginatedData.length === 0 && (
<tr>
<td colSpan={9} className="px-4 py-8 text-center text-slate-500">