Skip to content

Commit

Permalink
feat(docs): implement order by, limit and offset in query generator #524
Browse files Browse the repository at this point in the history
  • Loading branch information
fengelniederhammer committed Dec 19, 2023
1 parent 5434e49 commit 28ea487
Show file tree
Hide file tree
Showing 12 changed files with 1,467 additions and 535 deletions.
1,552 changes: 1,130 additions & 422 deletions lapis2-docs/package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 deletions lapis2-docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"preview": "astro preview",
"astro": "astro",
"check-format": "prettier --check \"**/*.{ts,tsx,json,astro,md,mdx,mjs,cjs}\"",
"format": "prettier --write \"**/*.{ts,tsx,json,astro,md,mdx,mjs,cjs}\""
"format": "prettier --write \"**/*.{ts,tsx,json,astro,md,mdx,mjs,cjs}\"",
"check-types": "tsc --noEmit && CONFIG_FILE=../siloLapisTests/testData/testDatabaseConfig.yaml astro check"
},
"dependencies": {
"@astrojs/react": "^3.0.7",
Expand All @@ -29,8 +30,10 @@
"yaml": "^2.3.4"
},
"devDependencies": {
"@types/swagger-ui": "^3.52.3",
"@astrojs/check": "^0.3.2",
"@types/swagger-ui": "^3.52.4",
"prettier": "^3.0.0",
"prettier-plugin-astro": "^0.12.1"
"prettier-plugin-astro": "^0.12.1",
"typescript": "^5.3.3"
}
}
19 changes: 19 additions & 0 deletions lapis2-docs/src/components/QueryGenerator/LabelledInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
type Props = {
label: string;
value: string | number | undefined;
onChange: (value: string) => void;
};

export const LabelledInput = ({ label, value, onChange }: Props) => {
return (
<div>
<label className='mr-2'>{label}</label>
<input
type='text'
className='input input-bordered w-48'
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react';
import { LabelledInput } from './LabelledInput.tsx';
import { getResultFields, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';
import type { Config } from '../../config.ts';

export type OrderByLimitOffset = {
orderBy: string[];
limit: number | undefined;
offset: number | undefined;
};

type Props = {
config: Config;
queryType: QueryTypeSelectionState;
orderByLimitOffset: OrderByLimitOffset;
onOrderByLimitOffsetChange: Dispatch<SetStateAction<OrderByLimitOffset>>;
};

export const OrderLimitOffsetSelection = ({
config,
queryType,
orderByLimitOffset,
onOrderByLimitOffsetChange,
}: Props) => {
return (
<div className='flex flex-col gap-4'>
<OderBySelection
config={config}
queryType={queryType}
orderBy={orderByLimitOffset.orderBy}
onOrderByLimitOffsetChange={onOrderByLimitOffsetChange}
/>
<LabelledInput
label='Limit:'
value={orderByLimitOffset.limit}
onChange={(value) =>
onOrderByLimitOffsetChange((prev) => ({
...prev,
limit: value === '' ? undefined : parseInt(value),
}))
}
/>
<LabelledInput
label='Offset:'
value={orderByLimitOffset.offset}
onChange={(value) =>
onOrderByLimitOffsetChange((prev) => ({
...prev,
offset: value === '' ? undefined : parseInt(value),
}))
}
/>
</div>
);
};

type OrderBySelectionProps = {
config: Config;
queryType: QueryTypeSelectionState;
orderBy: string[];
onOrderByLimitOffsetChange: Dispatch<SetStateAction<OrderByLimitOffset>>;
};

const OderBySelection = ({ config, queryType, orderBy, onOrderByLimitOffsetChange }: OrderBySelectionProps) => {
let unselectedOrderByFields = useMemo(
() =>
getResultFields(queryType, config)
.map((resultField) => resultField.name)
.filter((fieldName) => !orderBy.includes(fieldName)),
[orderBy, queryType],
);

const [selectedOrderByField, setSelectedOrderByField] = useState(unselectedOrderByFields[0]);

useEffect(() => {
if (!unselectedOrderByFields.includes(selectedOrderByField)) {
setSelectedOrderByField(unselectedOrderByFields[0]);
}
}, [unselectedOrderByFields, selectedOrderByField]);

return (
<div className='flex flex-column justify-start'>
<label className='mr-2'>Order by:</label>

<div>
{orderBy.map((value, index) => (
<div key={index}>
<input readOnly className='input input-bordered' value={value} />
<button
className='btn'
onClick={() =>
onOrderByLimitOffsetChange((prev) => ({
...prev,
orderBy: prev.orderBy?.filter((_, i) => i !== index),
}))
}
>
-
</button>
</div>
))}

{unselectedOrderByFields.length > 0 && (
<>
<label className='mr-2'>Add a field:</label>
<select
className='input input-bordered'
onChange={(e) => setSelectedOrderByField(e.target.value)}
>
{unselectedOrderByFields.map((fieldName) => (
<option key={fieldName}>{fieldName}</option>
))}
</select>
<button
className='btn'
onClick={() => {
onOrderByLimitOffsetChange((prev) => ({
...prev,
orderBy: [...prev.orderBy, selectedOrderByField],
}));
}}
>
+
</button>
</>
)}
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { QueryTypeSelectionState } from './QueryTypeSelection';
import { CheckBoxesWrapper, LabeledCheckBox, LabelWrapper } from './styled-components';
import type { QueryTypeSelectionState } from './QueryTypeSelectionState.ts';

const availableFormats = ['json', 'tsv', 'csv'] as const;
export type TabularOutputFormat = (typeof availableFormats)[number];
Expand Down
55 changes: 27 additions & 28 deletions lapis2-docs/src/components/QueryGenerator/QueryGenerator.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { useState } from 'react';
import { QueryTypeSelection, type QueryTypeSelectionState } from './QueryTypeSelection';
import { QueryTypeSelection } from './QueryTypeSelection';
import { type Filters, FiltersSelection } from './FiltersSelection';
import { Result } from './Result';
import type { Config } from '../../config';
import { type Config } from '../../config';
import { type OrderByLimitOffset, OrderLimitOffsetSelection } from './OrderLimitOffsetSelection.tsx';
import { getInitialQueryState, type QueryTypeSelectionState } from './QueryTypeSelectionState.ts';

type Props = {
config: Config;
Expand All @@ -11,31 +13,13 @@ type Props = {

export const QueryGenerator = ({ config, lapisUrl }: Props) => {
const [step, setStep] = useState(0);
const [queryType, setQueryType] = useState<QueryTypeSelectionState>({
selection: 'aggregatedAll',
aggregatedAll: {},
aggregatedStratified: {
fields: new Set<string>(),
},
mutations: {
type: 'nucleotide',
minProportion: '0.05',
},
insertions: {
type: 'nucleotide',
},
details: {
type: 'all',
fields: new Set<string>(),
},
nucleotideSequences: {
type: 'unaligned',
},
aminoAcidSequences: {
gene: '',
},
});
const [queryType, setQueryType] = useState<QueryTypeSelectionState>(getInitialQueryState());
const [filters, setFilters] = useState<Filters>(new Map());
const [orderByLimitOffset, setOrderByLimitOffset] = useState<OrderByLimitOffset>({
orderBy: [],
limit: undefined,
offset: undefined,
});

return (
<div className='not-content'>
Expand All @@ -51,8 +35,23 @@ export const QueryGenerator = ({ config, lapisUrl }: Props) => {
<div className='mt-8'>
{step === 0 && <QueryTypeSelection config={config} state={queryType} onStateChange={setQueryType} />}
{step === 1 && <FiltersSelection config={config} filters={filters} onFiltersChange={setFilters} />}
{step === 2 && <>TODO</>}
{step === 3 && <Result queryType={queryType} filters={filters} config={config} lapisUrl={lapisUrl} />}
{step === 2 && (
<OrderLimitOffsetSelection
config={config}
queryType={queryType}
orderByLimitOffset={orderByLimitOffset}
onOrderByLimitOffsetChange={setOrderByLimitOffset}
/>
)}
{step === 3 && (
<Result
queryType={queryType}
filters={filters}
config={config}
lapisUrl={lapisUrl}
orderByLimitOffset={orderByLimitOffset}
/>
)}
</div>

<div className='w-full flex justify-between mt-8'>
Expand Down
49 changes: 9 additions & 40 deletions lapis2-docs/src/components/QueryGenerator/QueryTypeSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,15 @@
import type { ReactNode } from 'react';
import type { Config } from '../../config';
import { CheckBoxesWrapper, ContainerWrapper, LabeledCheckBox, LabelWrapper } from './styled-components';

type Selection =
| 'aggregatedAll'
| 'aggregatedStratified'
| 'mutations'
| 'insertions'
| 'details'
| 'nucleotideSequences'
| 'aminoAcidSequences';

const sequenceTypes = ['nucleotide', 'aminoAcid'] as const;
type SequenceType = (typeof sequenceTypes)[number];
type DetailsType = 'all' | 'selected';
const alignmentTypes = ['unaligned', 'aligned'] as const;
type AlignmentType = (typeof alignmentTypes)[number];

export type QueryTypeSelectionState = {
selection: Selection;
aggregatedAll: {};
aggregatedStratified: {
fields: Set<string>;
};
mutations: {
type: SequenceType;
minProportion: string;
};
insertions: {
type: SequenceType;
};
details: {
type: DetailsType;
fields: Set<string>;
};
nucleotideSequences: {
type: AlignmentType;
};
aminoAcidSequences: {
gene: string;
};
};
import {
type AlignmentType,
alignmentTypes,
type DetailsType,
type QueryTypeSelectionState,
type Selection,
type SequenceType,
sequenceTypes,
} from './QueryTypeSelectionState.ts';

type Props = {
config: Config;
Expand Down
Loading

0 comments on commit 28ea487

Please sign in to comment.