Skip to content

PageHeaderFilters ​

Componente per la riga filtri nella topbar delle pagine di interrogazione. Si integra con PageLayout tramite lo slot filters e supporta 6 tipi di filtro tramite discriminated union TypeScript.

Import ​

typescript
import {
  PageHeaderFilters,
  type PageHeaderFiltersConfiguration,
  type PageHeaderFilterItem,
  type FilterOption,
  type DateRangeValue,
  getActiveFilterCount,
} from '@/components/layout/topbar/page-header-filters';

Uso base ​

tsx
<PageLayout title="Movimenti Magazzino" filters={<PageHeaderFilters config={filtersConfig} />}>
  {/* contenuto pagina */}
</PageLayout>

Configurazione ​

L'oggetto PageHeaderFiltersConfiguration definisce il comportamento globale e la lista dei filtri:

typescript
const filtersConfig: PageHeaderFiltersConfiguration = {
  maxRows: 2, // righe visibili prima del collapse
  onReset: handleReset, // callback reset tutti i filtri
  resetLabel: 'Reset filtri', // etichetta bottone reset
  expandLabel: 'Mostra altri filtri', // etichetta bottone espandi
  collapseLabel: 'Mostra meno filtri', // etichetta bottone comprimi
  items: [
    /* ... filtri ... */
  ],
};

Tipi di filtro ​

text β€” Campo di testo libero ​

typescript
{
  type: 'text',
  id: 'codice',
  label: 'Codice Articolo',
  placeholder: 'Es. ART-001',
  value: codiceArticolo,          // string
  onChange: setCodiceArticolo,     // (value: string) => void
}

select β€” Dropdown con opzioni predefinite ​

typescript
{
  type: 'select',
  id: 'magazzino',
  label: 'Magazzino',
  placeholder: 'Tutti i magazzini',
  value: magazzino,               // string
  onChange: setMagazzino,          // (value: string) => void
  options: [
    { value: 'centrale', label: 'Centrale' },
    { value: 'nord', label: 'Nord' },
  ],
}

combobox β€” Select con ricerca integrata ​

typescript
{
  type: 'combobox',
  id: 'cliente',
  label: 'Cliente',
  placeholder: 'Cerca cliente...',
  searchPlaceholder: 'Digita per cercare...',
  value: cliente,                 // string
  onChange: setCliente,            // (value: string) => void
  options: clientiOptions,         // FilterOption[]
  onSearch: async (q) => fetchClienti(q),  // ricerca asincrona (opzionale)
  disabled: isLoading,             // disabilita durante il caricamento
}

date β€” DatePicker ​

typescript
{
  type: 'date',
  id: 'data-documento',
  label: 'Data Documento',
  placeholder: 'Seleziona data',
  value: dataDocumento,           // Date | null
  onChange: setDataDocumento,      // (value: Date | null) => void
  minDate: new Date('2024-01-01'), // vincolo opzionale
}

dateRange β€” Intervallo di date ​

typescript
{
  type: 'dateRange',
  id: 'periodo',
  label: 'Periodo',
  value: periodo,                 // { from: Date | null, to: Date | null }
  onChange: setPeriodo,             // (value: DateRangeValue) => void
}

checkbox β€” Filtro booleano ​

typescript
{
  type: 'checkbox',
  id: 'solo-confermati',
  label: 'Solo confermati',
  value: soloConfermati,          // boolean
  onChange: setSoloConfermati,      // (value: boolean) => void
}

ProprietΓ  comuni a tutti i filtri ​

ProprietΓ TipoDescrizione
idstringIdentificatore univoco del filtro
labelstringEtichetta visibile
hiddenbooleanNasconde il filtro condizionalmente
disabledbooleanDisabilita l'input
widthstringClasse Tailwind per la larghezza (es. 'w-64')
classNamesobjectClassi CSS per wrapper, label, input

Filtri condizionali con hidden ​

Usa hidden per mostrare/nascondere filtri in base allo stato di altri filtri:

typescript
{
  id: 'motivo-annullamento',
  type: 'text',
  label: 'Motivo Annullamento',
  value: motivoAnnullamento,
  onChange: setMotivoAnnullamento,
  hidden: stato !== 'annullato',    // visibile solo se stato === 'annullato'
}

Conteggio filtri attivi ​

typescript
import { getActiveFilterCount } from '@/components/layout/topbar/page-header-filters';

const count = getActiveFilterCount(filtersConfig.items);
// Conta i filtri con valore diverso dal default (stringa vuota, false, null, ecc.)

Integrazione con AG Grid ​

PageHeaderFilters Γ¨ fully controlled: il componente parent possiede tutti i valori tramite value/onChange. Questo rende immediato il collegamento con AG Grid.

Pattern: ricerca globale con quickFilter ​

Il filtro quickFilterText di AG Grid cerca in tutte le colonne. Si collega con un filtro text nella barra filtri:

typescript
import { DataGrid } from '@/components/ui/data-grid';
import type { GridApi, GridReadyEvent } from 'ag-grid-community';

// Stato ricerca globale
const [ricercaGlobale, setRicercaGlobale] = useState('');
const gridApiRef = useRef<GridApi | null>(null);

// Sincronizza con AG Grid
useEffect(() => {
  gridApiRef.current?.setGridOption('quickFilterText', ricercaGlobale);
}, [ricercaGlobale]);

// Filtro nella configurazione (primo item per massima visibilita')
const filtersConfig: PageHeaderFiltersConfiguration = {
  items: [
    {
      id: 'ricerca-globale',
      type: 'text',
      label: 'Ricerca',
      placeholder: 'Cerca in tutte le colonne...',
      value: ricercaGlobale,
      onChange: setRicercaGlobale,
      width: 'w-64',
    },
    // ... altri filtri specifici
  ],
};

// Cattura gridApi al mount della griglia
const handleGridReady = (event: GridReadyEvent) => {
  gridApiRef.current = event.api;
};

<DataGrid
  rowData={data}
  columnDefs={columnDefs}
  onGridReady={handleGridReady}
/>

Pattern: filtri specifici + ricerca globale combinati ​

Per pagine con sia filtri specifici che ricerca globale, usa un approccio a due livelli:

  1. Filtri specifici (magazzino, stato, ecc.) β†’ filtrano rowData lato client (o via API)
  2. Ricerca globale β†’ usa quickFilterText di AG Grid sui dati gia' filtrati
typescript
// I filtri specifici controllano quali righe passare alla griglia
const filteredData = allData.filter((row) => {
  if (magazzino && row.magazzino !== magazzino) return false;
  if (stato && row.stato !== stato) return false;
  if (tipoMovimento && row.tipoMovimento !== tipoMovimento) return false;
  return true;
});

// La ricerca globale opera sul sottoinsieme gia' filtrato
<DataGrid
  rowData={filteredData}          // pre-filtrato dai filtri specifici
  columnDefs={columnDefs}
  onGridReady={handleGridReady}   // quickFilterText applicato via useEffect
/>

Accesso completo all'API AG Grid ​

DataGrid espone l'intera API di AG Grid tramite onGridReady. La GridApi permette di:

  • setGridOption('quickFilterText', value) β€” ricerca globale
  • setFilterModel(model) β€” filtri colonna programmatici
  • exportDataAsCsv() β€” export dati
  • getSelectedRows() β€” righe selezionate
  • refreshCells() β€” aggiornamento celle

Vedi la documentazione AG Grid per l'elenco completo.

Comportamento responsive ​

  • Desktop: I filtri si dispongono in flex-wrap. Un ResizeObserver misura quali items superano maxRows righe e li nasconde. Il bottone "Mostra altri filtri (N)" espande la vista.
  • Mobile (<768px): Layout a colonna, ogni filtro occupa la larghezza completa. Gli overflow vengono mostrati in un bottom sheet modale.
  • Reset: Il bottone reset appare solo quando ci sono filtri attivi. Un badge mostra il conteggio.

Best practices ​

  • Ordine filtri: metti i filtri piu' usati nelle prime posizioni (visibili senza espandere)
  • maxRows: usa 1 per pochi filtri primari, 2 (default) per la maggior parte delle pagine
  • hidden: usa per nascondere filtri contestuali (es. "Motivo Annullamento" solo se stato === "annullato")
  • onReset: fornisci sempre per pagine di interrogazione β€” il reset deve riportare tutti i filtri ai valori iniziali
  • Performance: per combobox con molte opzioni, usa onSearch asincrono. Il debouncing e' gestito internamente (300ms)
  • Ricerca globale: posizionala come primo filtro e usa width: 'w-64' per darle risalto

Accessibilita' ​

  • Container con role="search" e aria-label="Filtri"
  • Ogni input ha un Label associato via htmlFor
  • Il bottone expand usa aria-expanded
  • Il bottone reset ha aria-label con conteggio filtri attivi

Demo ​

La pagina di test /test-pagelayout-with-filters mostra un esempio completo con:

  • Tutti i tipi di filtro
  • DataGrid AG Grid con dati mock
  • Ricerca globale collegata a quickFilterText
  • Filtri specifici che controllano rowData
  • Filtro condizionale hidden
  • Reset di tutti i filtri

Documentazione Elerama Frontend