Skip to content
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,67 @@ import { ColumnFiltersState, Table } from "@tanstack/react-table";
import { ReadRun } from "../../types";
import { PRESELECTED_COLUMN_FILTERS } from "./constants";

/**
* Builds column filters from preselected values only if they are valid for the current data.
* A set of preselected filters is considered valid if at least one row in the table satisfies all column filters (AND across columns),
* where each column filter matches if the row contains at least one of the desired values for that column (OR within a column).
* If no rows satisfy the filters, an empty list is returned.
*
* Note: This does not mutate or filter out individual values; it validates the combination overall.
*
* @param table - Table instance.
* @param preselectedFilters - Record of column IDs to arrays of desired filter values.
* @returns Column filters state.
*/
function buildValidatedColumnFilters(
table: Table<ReadRun>,
preselectedFilters: Record<string, string[]>
): ColumnFiltersState {
const { rows } = table.getRowModel();

// We need to ensure that the combined column filters are valid for the given data.
// A row must have at least one of the desired values (OR-join) for each column filter (AND-join).
for (const row of rows) {
let isRowValid = true;

for (const [id, value] of Object.entries(preselectedFilters)) {
// Determine the filter set (we can safely assert the value as string array).
const filterValueSet = new Set(value as string[]);

// Determine the column values.
const columnValues = toStringArray(row.getValue(id));

// At least one of the desired values must be present in the column "OR".
let hasAny = false;
for (const v of columnValues) {
if (filterValueSet.has(v)) {
hasAny = true;
break;
}
}

// If the row is not valid, break (no need to check the rest of the columns).
if (!hasAny) {
isRowValid = false;
break;
}
}

// If the row is not valid, continue to the next row.
if (!isRowValid) continue;

// Found a valid row i.e. we know the pre-selected column filters will be a valid
// combination for the table data. There is no need to continue checking the rest of the rows.
return Object.entries(preselectedFilters).map(([id, value]) => ({
id,
value,
}));
}

// No valid rows found, return empty column filters.
return [];
}

/**
* Returns true if the table is using data from ENA query method by taxonomy ID.
* @param table - The table.
Expand Down Expand Up @@ -35,36 +96,12 @@ export function preSelectColumnFilters(
}

/**
* Builds validated column filters from preselected filter values.
* Filters out columns that don't exist in the table and values that aren't available
* in the column's faceted unique values.
* @param table - Table instance.
* @param preselectedFilters - Record of column IDs to arrays of desired filter values.
* @returns Column filters state.
* Converts a value to an array of strings.
* @param value - The value to convert.
* @returns An array of strings.
*/
function buildValidatedColumnFilters(
table: Table<ReadRun>,
preselectedFilters: Record<string, string[]>
): ColumnFiltersState {
const columnFiltersState: ColumnFiltersState = [];

for (const [columnId, desiredValues] of Object.entries(preselectedFilters)) {
const column = table.getColumn(columnId);
if (!column) continue;

const availableValues = column.getFacetedUniqueValues();
const validValues: string[] = [];

// Only include values that exist in the column's faceted data.
for (const value of desiredValues) {
if (!availableValues.has(value)) continue;
validValues.push(value);
}

if (validValues.length === 0) continue;

columnFiltersState.push({ id: columnId, value: validValues });
}

return columnFiltersState;
function toStringArray(value: unknown): string[] {
if (value == null) return [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checking == is intentional here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, no. I let Windsurf write that function 😱 .

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (value == null) return [];
if (value === null) return [];

if (Array.isArray(value)) return value.map(String);
return [String(value)];
}
Loading