Skip to content

Commit 9467b86

Browse files
committed
Merge branch 'main' into production
2 parents ba09378 + d3e2088 commit 9467b86

File tree

4 files changed

+168
-74
lines changed

4 files changed

+168
-74
lines changed

.github/ISSUE_TEMPLATE/ui_change.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ body:
1212
id: uiIssueName
1313
attributes:
1414
label: Issue Name
15-
description: Provide a name for the page or documentation meant to be generated in STRAPI
15+
description: Provide a name for the page or feature that cannot be generated in STRAPI
1616
placeholder: ex. Featured Page
1717
validations:
1818
required: True
1919
- type: textarea
2020
id: uiIssueDescription
2121
attributes:
2222
label: Issue Description
23-
description: Please describe the page or documentation to be generated in STRAPI
23+
description: Provide a name for the page or feature that cannot be generated in STRAPI
2424
placeholder: ex- The featured page will highligh NIAID priority repositories
2525
validations:
2626
required: true

src/views/ontology-browser/utils/api-helpers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ export const fetchLineageFromBioThingsAPI = async (
290290
commonName: item?.genbank_common_name || item?.common_name || '',
291291
hasChildren: item?.children.length > 0, // [TO DO]:BioThings API does not provide this information
292292
iri: formatIRI(taxonId, params.ontology),
293-
label: item.scientific_name.toLowerCase(),
293+
label: item.scientific_name,
294294
ontologyName: params.ontology,
295295
parentTaxonId: isRootNode ? null : item.parent_taxid.toString(),
296296
rank: item.rank,
@@ -423,7 +423,7 @@ export const fetchChildrenFromBioThingsAPI = async (
423423
commonName: item?.genbank_common_name || item?.common_name || '',
424424
hasChildren: item?.children.length > 0, // [TO DO]:BioThings API does not provide this information
425425
iri: formatIRI(taxonId, params.ontology),
426-
label: item.scientific_name.toLowerCase(),
426+
label: item.scientific_name,
427427
ontologyName: params.ontology,
428428
parentTaxonId: isRootNode ? null : item.parent_taxid.toString(),
429429
rank: item.rank,
@@ -521,7 +521,7 @@ export const fetchLineageFromOLSAPI = async (
521521
commonName: item?.synonyms?.[0] || '',
522522
hasChildren: item.has_children,
523523
iri: item.iri,
524-
label: item.label.toLowerCase(),
524+
label: item.label,
525525
ontologyName: item.ontology_name,
526526
parentTaxonId,
527527
state: {
@@ -610,7 +610,7 @@ export const fetchChildrenFromOLSAPI = async (
610610
commonName: item?.synonyms?.[0] || '',
611611
hasChildren: item.has_children,
612612
iri: item.iri,
613-
label: item.label.toLowerCase(),
613+
label: item.label,
614614
ontologyName: item.ontology_name,
615615
parentTaxonId: params.id, // Parent is the current node
616616
state: {

src/views/search/components/filters/components/tag/index.tsx

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,37 @@ export const FilterTags: React.FC<FilterTagsProps> = React.memo(
5959
[selectedFilters, configMap],
6060
);
6161

62-
//Removes a single filter value from selectedFilters and updates the route.
62+
// Removes a single filter value from selectedFilters and updates the route.
6363
const removeSelectedFilter = (
6464
filterKey: string,
6565
filterValue: SelectedFilterTypeValue | SelectedFilterTypeValue[],
6666
) => {
67-
const updatedFilters: SelectedFilterType = {
68-
...selectedFilters,
69-
[filterKey]: selectedFilters[filterKey].filter(v => {
70-
if (Array.isArray(filterValue)) return !filterValue.includes(v);
71-
return !isEqual(v, filterValue);
72-
}),
73-
};
67+
let updatedFilters: SelectedFilterType;
68+
69+
// If filterValue is an array of 2 dates, clear all
70+
if (
71+
filterKey === 'date' &&
72+
Array.isArray(filterValue) &&
73+
filterValue.length === 2 &&
74+
typeof filterValue[0] === 'string' &&
75+
typeof filterValue[1] === 'string'
76+
) {
77+
// Remove the entire date range
78+
updatedFilters = {
79+
...selectedFilters,
80+
[filterKey]: [],
81+
};
82+
} else {
83+
// For other filters, remove the specific value(s)
84+
updatedFilters = {
85+
...selectedFilters,
86+
[filterKey]: selectedFilters[filterKey].filter(v => {
87+
if (Array.isArray(filterValue)) return !filterValue.includes(v);
88+
return !isEqual(v, filterValue);
89+
}),
90+
};
91+
}
92+
7493
resetPagination();
7594
handleRouteUpdate({
7695
from: defaultQuery.from,

src/views/search/components/filters/components/tag/utils.ts

Lines changed: 135 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -8,62 +8,155 @@ import { capitalize, has, isPlainObject } from 'lodash';
88
import SCHEMA_DEFINITIONS from 'configs/schema-definitions.json';
99
import { SchemaDefinitions } from 'scripts/generate-schema-definitions/types';
1010

11-
/**
12-
* Controls how a selected filter is displayed in the tag.
13-
*/
11+
// Constants
12+
const DISPLAY_NAME_SEPARATOR = ' | ';
13+
const DATE_FILTER_KEY = 'date';
14+
const EXISTS_PREFIX = '_exists_';
15+
const NOT_EXISTS_PREFIX = '-_exists_';
16+
17+
const schema = SCHEMA_DEFINITIONS as SchemaDefinitions;
18+
19+
// Type guards
20+
const isStringValue = (value: unknown): value is string =>
21+
typeof value === 'string';
22+
23+
const isObjectValue = (value: unknown): value is Record<string, unknown> =>
24+
isPlainObject(value);
25+
26+
const isDateRangeValues = (
27+
values: (string | SelectedFilterTypeValue)[],
28+
): values is [string, string] =>
29+
values.length === 2 && isStringValue(values[0]) && isStringValue(values[1]);
30+
31+
// Helper Functions
32+
33+
// Formats a display name with common and scientific names
34+
const formatDisplayName = (value: string): string => {
35+
if (!value.includes(DISPLAY_NAME_SEPARATOR)) {
36+
return value;
37+
}
38+
39+
const [commonName, scientificName] = value.split(DISPLAY_NAME_SEPARATOR);
40+
return `${capitalize(scientificName)} (${capitalize(commonName)})`;
41+
};
42+
43+
// Formats a date range for display
44+
const formatDateRange = (startDate: string, endDate: string): string =>
45+
`From ${startDate} to ${endDate}`;
46+
47+
// Applies custom label transformation from filter config
48+
const applyConfigTransform = (value: string, config?: FilterConfig): string => {
49+
if (!config?.transformData) {
50+
return value;
51+
}
52+
53+
return config.transformData({ term: value, count: 0 })?.label || value;
54+
};
55+
56+
// Controls how a selected filter is displayed in the tag
1457
const getDisplayValue = (
1558
key: string,
1659
value: string | SelectedFilterTypeValue,
1760
values: (string | SelectedFilterTypeValue)[],
1861
index: number,
1962
config?: FilterConfig,
2063
): string => {
21-
if (isPlainObject(value)) {
64+
// Handle object values (exists/not exists queries)
65+
if (isObjectValue(value)) {
2266
const objectKey = Object.keys(value)[0];
23-
return objectKey.startsWith('-_exists_') ? 'None' : 'Any';
67+
return objectKey?.startsWith(NOT_EXISTS_PREFIX) ? 'None' : 'Any';
2468
}
2569

26-
// Format date ranges
27-
if (
28-
key === 'date' &&
29-
values.length === 2 &&
30-
typeof values[0] === 'string' &&
31-
typeof values[1] === 'string'
32-
) {
33-
if (index > 0) return '';
34-
return `From ${values[0]} to ${values[1]}`;
70+
// Handle date ranges (skip subsequent values in range)
71+
if (key === DATE_FILTER_KEY && isDateRangeValues(values)) {
72+
return index === 0 ? formatDateRange(values[0], values[1]) : '';
3573
}
3674

37-
// Display names that have it, with both common and scientific forms
38-
if (typeof value === 'string') {
39-
if (key.includes('displayName') && value.includes(' | ')) {
40-
const [commonName, scientificName] = value.split(' | ');
41-
return `${capitalize(scientificName)} (${capitalize(commonName)})`;
75+
// Handle string values
76+
if (isStringValue(value)) {
77+
// Format display names
78+
if (key.includes('displayName')) {
79+
return formatDisplayName(value);
4280
}
4381

44-
// Apply any custom label transformation from config
45-
if (
46-
(key === '@type' || key === 'conditionsOfAccess') &&
47-
config?.transformData
48-
) {
49-
return config.transformData({ term: value, count: 0 })?.label || '';
82+
// Apply config transformations for specific keys
83+
if (key === '@type' || key === 'conditionsOfAccess') {
84+
return applyConfigTransform(value, config);
5085
}
5186
}
5287

5388
return String(value);
5489
};
5590

56-
/**
57-
* Generates a flat list of tag metadata objects from selected filters.
58-
*/
59-
const schema = SCHEMA_DEFINITIONS as SchemaDefinitions;
60-
export const generateTagName = (key: string, config?: FilterConfig): string => {
61-
if (config?.name) return config.name;
62-
if (schema?.[key]?.name) return schema[key].name;
91+
// Checks if a filter represents a date exists/not exists query
92+
const isDateExistsQuery = (
93+
key: string,
94+
values: (string | SelectedFilterTypeValue)[],
95+
): boolean => {
96+
if (key !== DATE_FILTER_KEY || values.length !== 1) {
97+
return false;
98+
}
6399

64-
return key;
100+
const value = values[0];
101+
return (
102+
isObjectValue(value) &&
103+
(has(value, EXISTS_PREFIX) || has(value, NOT_EXISTS_PREFIX))
104+
);
65105
};
66106

107+
// Creates a tag info object for a date range
108+
const createDateRangeTag = (
109+
key: string,
110+
name: string,
111+
values: [string, string],
112+
): TagInfo => ({
113+
key: `${key}-range`,
114+
filterKey: key,
115+
name,
116+
value: values,
117+
displayValue: formatDateRange(values[0], values[1]),
118+
});
119+
120+
// Creates tag info objects for individual filter values
121+
const createValueTags = (
122+
key: string,
123+
name: string,
124+
values: (string | SelectedFilterTypeValue)[],
125+
config?: FilterConfig,
126+
): TagInfo[] => {
127+
return values
128+
.map((rawValue, index): TagInfo | null => {
129+
const displayValue = getDisplayValue(
130+
key,
131+
rawValue,
132+
values,
133+
index,
134+
config,
135+
);
136+
137+
if (!displayValue) {
138+
return null;
139+
}
140+
141+
return {
142+
key: `${key}-${index}`,
143+
filterKey: key,
144+
name,
145+
value: rawValue,
146+
displayValue,
147+
};
148+
})
149+
.filter((tag): tag is TagInfo => tag !== null);
150+
};
151+
152+
// Exported functions
153+
154+
// Generates a human-readable name for a filter key
155+
export const generateTagName = (key: string, config?: FilterConfig): string => {
156+
return config?.name ?? schema?.[key]?.name ?? key;
157+
};
158+
159+
// Generates a flat list of tag metadata objects from selected filters
67160
export const generateTags = (
68161
selectedFilters: SelectedFilterType,
69162
configMap: Record<string, FilterConfig>,
@@ -72,35 +165,17 @@ export const generateTags = (
72165
const config = configMap[key];
73166
const name = generateTagName(key, config);
74167

75-
if (
76-
key === 'date' &&
77-
values.length === 1 &&
78-
isPlainObject(values[0]) &&
79-
(has(values[0], '_exists_') || has(values[0], '-_exists_'))
80-
) {
168+
// Skip date exists/not exists queries (they don't display as tags)
169+
if (isDateExistsQuery(key, values)) {
81170
return [];
82171
}
83172

84-
// Generate tag info for each value
85-
return values
86-
.map((rawValue, index) => {
87-
const displayValue = getDisplayValue(
88-
key,
89-
rawValue,
90-
values,
91-
index,
92-
config,
93-
);
94-
if (!displayValue) return null;
95-
96-
return {
97-
key: `${key}-${index}`,
98-
filterKey: key,
99-
name,
100-
value: rawValue,
101-
displayValue,
102-
};
103-
})
104-
.filter(Boolean) as TagInfo[];
173+
// Handle date ranges as a single tag
174+
if (key === DATE_FILTER_KEY && isDateRangeValues(values)) {
175+
return [createDateRangeTag(key, name, values)];
176+
}
177+
178+
// Handle all other filters
179+
return createValueTags(key, name, values, config);
105180
});
106181
};

0 commit comments

Comments
 (0)