Skip to content

Commit

Permalink
feat(components): add option to filter option selection shown in text…
Browse files Browse the repository at this point in the history
…-input and lineage-filter using lapisFilter objects
  • Loading branch information
anna-parker committed Jan 3, 2025
1 parent 78af02a commit 3519e7c
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ describe('fetchLineageAutocompleteList', () => {
test('should add sublineage values', async () => {
lapisRequestMocks.aggregated({ fields: ['lineageField'] }, { data: [{ lineageField: 'B.1.1.7', count: 1 }] });

const result = await fetchLineageAutocompleteList(DUMMY_LAPIS_URL, 'lineageField');
const result = await fetchLineageAutocompleteList({}, DUMMY_LAPIS_URL, 'lineageField');

expect(result).to.deep.equal([
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import { FetchAggregatedOperator } from '../../operator/FetchAggregatedOperator';
import { type LapisFilter } from '../../types';

type GroupType = Record<string, string> & { count: number };

export async function fetchLineageAutocompleteList(lapis: string, field: string, signal?: AbortSignal) {
const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string>>({}, [field]);
export async function fetchLineageAutocompleteList(
lapisFilter: LapisFilter,
lapis: string,
field: string,
signal?: AbortSignal,
) {
const fetchAggregatedOperator = new FetchAggregatedOperator<Record<string, string>>(lapisFilter, [field]);

const data = (await fetchAggregatedOperator.evaluate(lapis, signal)).content;

Expand Down
7 changes: 5 additions & 2 deletions components/src/preact/lineageFilter/lineage-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useContext, useRef, useState } from 'preact/hooks';
import z from 'zod';

import { fetchLineageAutocompleteList } from './fetchLineageAutocompleteList';
import { lapisFilterSchema } from '../../types';
import { LapisUrlContext } from '../LapisUrlContext';
import { ErrorBoundary } from '../components/error-boundary';
import { LoadingDisplay } from '../components/loading-display';
Expand All @@ -12,6 +13,7 @@ import { useQuery } from '../useQuery';

const lineageFilterInnerPropsSchema = z.object({
lapisField: z.string().min(1),
lapisFilter: lapisFilterSchema,
placeholderText: z.string().optional(),
initialValue: z.string(),
});
Expand All @@ -38,6 +40,7 @@ export const LineageFilter: FunctionComponent<LineageFilterProps> = (props) => {

const LineageFilterInner: FunctionComponent<LineageFilterInnerProps> = ({
lapisField,
lapisFilter,
placeholderText,
initialValue,
}) => {
Expand All @@ -49,8 +52,8 @@ const LineageFilterInner: FunctionComponent<LineageFilterInnerProps> = ({
const [inputValue, setInputValue] = useState(initialValue || '');

const { data, error, isLoading } = useQuery(
() => fetchLineageAutocompleteList(lapis, lapisField),
[lapisField, lapis],
() => fetchLineageAutocompleteList(lapisFilter, lapis, lapisField),
[lapisFilter, lapisField, lapis],
);

if (isLoading) {
Expand Down
15 changes: 8 additions & 7 deletions components/src/preact/textInput/fetchAutocompleteList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,26 @@ export async function fetchAutocompleteList(

const data = (await fetchAggregatedOperator.evaluate(lapis, signal)).content;

return sortDataByField(data, field);
const filteredData = data.filter((record) => record[field] !== null);

return sortDataByField(filteredData, field);
}

const sortDataByField = (data: (Record<string, string | null> & { count: number })[], field: string) => {
const sortDataByField = (data: (Record<string, string> & { count: number })[], field: string) => {
return data.sort((a, b) => {
const aValue = a[field];
const bValue = b[field];

if ((aValue === undefined || aValue === null) && bValue !== undefined && bValue !== null) {
if (aValue === undefined && bValue !== undefined) {
return 1;
}
if ((bValue === undefined || bValue === null) && aValue !== undefined && aValue !== null) {
if (bValue === undefined && aValue !== undefined) {
return -1;
}
if ((aValue === undefined || aValue === null) && (bValue === undefined || bValue === null)) {
if (aValue === undefined && bValue === undefined) {
return 0;
}

// Compare values when both are non-null and defined
return aValue!.localeCompare(bValue!);
return aValue.localeCompare(bValue);
});
};
2 changes: 2 additions & 0 deletions components/src/preact/textInput/text-input.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const Default: StoryObj<TextInputProps> = {
render: (args) => (
<LapisUrlContext.Provider value={LAPIS_URL}>
<TextInput
lapisFilter={args.lapisFilter}
lapisField={args.lapisField}
placeholderText={args.placeholderText}
initialValue={args.initialValue}
Expand All @@ -72,6 +73,7 @@ export const Default: StoryObj<TextInputProps> = {
</LapisUrlContext.Provider>
),
args: {
lapisFilter: {},
lapisField: 'host',
placeholderText: 'Enter a host name',
initialValue: '',
Expand Down
14 changes: 12 additions & 2 deletions components/src/preact/textInput/text-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useContext, useRef, useState } from 'preact/hooks';
import z from 'zod';

import { fetchAutocompleteList } from './fetchAutocompleteList';
import { lapisFilterSchema } from '../../types';
import { LapisUrlContext } from '../LapisUrlContext';
import { ErrorBoundary } from '../components/error-boundary';
import { LoadingDisplay } from '../components/loading-display';
Expand All @@ -11,6 +12,7 @@ import { ResizeContainer } from '../components/resize-container';
import { useQuery } from '../useQuery';

const textInputInnerPropsSchema = z.object({
lapisFilter: lapisFilterSchema,
lapisField: z.string().min(1),
placeholderText: z.string().optional(),
initialValue: z.string().optional(),
Expand All @@ -36,15 +38,23 @@ export const TextInput: FunctionComponent<TextInputProps> = (props) => {
);
};

const TextInputInner: FunctionComponent<TextInputInnerProps> = ({ lapisField, placeholderText, initialValue }) => {
const TextInputInner: FunctionComponent<TextInputInnerProps> = ({
lapisFilter,
lapisField,
placeholderText,
initialValue,
}) => {
const lapis = useContext(LapisUrlContext);

const inputRef = useRef<HTMLInputElement>(null);

const [hasInput, setHasInput] = useState<boolean>(!!initialValue);
const [inputValue, setInputValue] = useState(initialValue || '');

const { data, error, isLoading } = useQuery(() => fetchAutocompleteList(lapis, lapisField), [lapisField, lapis]);
const { data, error, isLoading } = useQuery(
() => fetchAutocompleteList(lapisFilter, lapis, lapisField),
[lapisFilter, lapis, lapisField],
);

if (isLoading) {
return <LoadingDisplay />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { withinShadowRoot } from '../withinShadowRoot.story';
const codeExample = String.raw`
<gs-lineage-filter
lapisField="pangoLineage"
lapisFilter={{}}
placeholderText="Enter lineage"
initialValue="B.1.1.7"
width="50%">
Expand Down Expand Up @@ -60,6 +61,7 @@ export const Default: StoryObj<Required<LineageFilterProps>> = {
<div class="max-w-screen-lg">
<gs-lineage-filter
.lapisField=${args.lapisField}
.lapisFilter=${args.lapisFilter}
.placeholderText=${args.placeholderText}
.initialValue=${args.initialValue}
.width=${args.width}
Expand All @@ -69,6 +71,7 @@ export const Default: StoryObj<Required<LineageFilterProps>> = {
},
args: {
lapisField: 'pangoLineage',
lapisFilter: {},
placeholderText: 'Enter lineage',
initialValue: 'B.1.1.7',
width: '100%',
Expand Down
18 changes: 18 additions & 0 deletions components/src/web-components/input/gs-lineage-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ export class LineageFilterComponent extends PreactLitAdapter {
@property()
initialValue: string = '';

/**
* A LAPIS filter to fetch the number of sequences for.
*
* The `lapisFilter` will be sent as is to LAPIS to select the data.
* It must be a valid LAPIS filter object.
*/
@property({ type: Object })
lapisFilter: Record<string, string | string[] | number | null | boolean | undefined> & {
nucleotideMutations?: string[];
aminoAcidMutations?: string[];
nucleotideInsertions?: string[];
aminoAcidInsertions?: string[];
} = {};

/**
* Required.
*
Expand Down Expand Up @@ -62,6 +76,7 @@ export class LineageFilterComponent extends PreactLitAdapter {
return (
<LineageFilter
lapisField={this.lapisField}
lapisFilter={this.lapisFilter}
placeholderText={this.placeholderText}
initialValue={this.initialValue}
width={this.width}
Expand Down Expand Up @@ -90,6 +105,9 @@ declare global {
}

/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
type LapisFilterMatches = Expect<
Equals<typeof LineageFilterComponent.prototype.lapisFilter, LineageFilterProps['lapisFilter']>
>;
type InitialValueMatches = Expect<
Equals<typeof LineageFilterComponent.prototype.initialValue, LineageFilterProps['initialValue']>
>;
Expand Down
3 changes: 3 additions & 0 deletions components/src/web-components/input/gs-text-input.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { withinShadowRoot } from '../withinShadowRoot.story';
const codeExample = String.raw`
<gs-text-input
lapisField="host"
lapisFilter={{}}
placeholderText="Enter host name"
initialValue="Homo sapiens"
width="50%">
Expand Down Expand Up @@ -82,6 +83,7 @@ export const Default: StoryObj<Required<TextInputProps>> = {
<div class="max-w-screen-lg">
<gs-text-input
.lapisField=${args.lapisField}
.lapisFilter=${args.lapisFilter}
.placeholderText=${args.placeholderText}
.initialValue=${args.initialValue}
.width=${args.width}
Expand All @@ -91,6 +93,7 @@ export const Default: StoryObj<Required<TextInputProps>> = {
},
args: {
lapisField: 'host',
lapisFilter: {},
placeholderText: 'Enter host name',
initialValue: 'Homo sapiens',
width: '100%',
Expand Down
18 changes: 18 additions & 0 deletions components/src/web-components/input/gs-text-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@ export class TextInputComponent extends PreactLitAdapter {
@property()
initialValue: string | undefined = undefined;

/**
* A LAPIS filter to fetch the number of sequences for.
*
* The `lapisFilter` will be sent as is to LAPIS to select the data.
* It must be a valid LAPIS filter object.
*/
@property({ type: Object })
lapisFilter: Record<string, string | string[] | number | null | boolean | undefined> & {
nucleotideMutations?: string[];
aminoAcidMutations?: string[];
nucleotideInsertions?: string[];
aminoAcidInsertions?: string[];
} = {};

/**
* Required.
*
Expand All @@ -55,6 +69,7 @@ export class TextInputComponent extends PreactLitAdapter {
override render() {
return (
<TextInput
lapisFilter={this.lapisFilter}
lapisField={this.lapisField}
placeholderText={this.placeholderText}
initialValue={this.initialValue}
Expand Down Expand Up @@ -84,6 +99,9 @@ declare global {
}

/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */
type LapisFilterMatches = Expect<
Equals<typeof TextInputComponent.prototype.lapisFilter, TextInputProps['lapisFilter']>
>;
type InitialValueMatches = Expect<
Equals<typeof TextInputComponent.prototype.initialValue, TextInputProps['initialValue']>
>;
Expand Down

0 comments on commit 3519e7c

Please sign in to comment.