Skip to content

Commit b1b2c1e

Browse files
committed
Merge branch 'main' into timeslider-date-input
2 parents f8801c5 + ff20159 commit b1b2c1e

File tree

24 files changed

+795
-322
lines changed

24 files changed

+795
-322
lines changed

packages/libs/coreui/src/components/containers/Modal.tsx

+10-13
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type ModalStyleSpec = {
4747
width: CSSProperties['width'];
4848
height: CSSProperties['height'];
4949
};
50+
position: Pick<CSSProperties, 'top' | 'right' | 'bottom' | 'left'>;
5051
};
5152

5253
export type ModalProps = {
@@ -102,10 +103,6 @@ export default function Modal({
102103
// Track the height of the title text.
103104
const { observe, height: titleHeight } = useDimensions();
104105

105-
// Track the height of the modal content.
106-
const { observe: observeModalContent, height: modalContentHeight } =
107-
useDimensions();
108-
109106
const componentStyle: ModalStyleSpec = useMemo(() => {
110107
const defaultStyle: ModalStyleSpec = {
111108
border: {
@@ -138,6 +135,7 @@ export default function Modal({
138135
width: undefined,
139136
height: undefined,
140137
},
138+
position: {},
141139
};
142140

143141
// TODO: Handle color problems when level is too dark.
@@ -176,7 +174,6 @@ export default function Modal({
176174

177175
return (
178176
<ResponsiveModal
179-
ref={observeModalContent}
180177
open={visible}
181178
onClose={() => toggleVisible && toggleVisible(false)}
182179
showCloseIcon={false}
@@ -206,20 +203,19 @@ export default function Modal({
206203
},
207204
modalContainer: {
208205
position: 'absolute',
209-
...(componentStyle.size.width
210-
? {
211-
width: componentStyle.size.width,
212-
height: componentStyle.size.height,
213-
}
214-
: { top: 75, right: 75, bottom: 75, left: 75 }),
215-
206+
width: componentStyle.size.width,
207+
height: componentStyle.size.height,
216208
background: colors.white,
217209
borderRadius: componentStyle.border.radius,
218210
borderColor: componentStyle.border.color,
219211
borderWidth: componentStyle.border.width,
220212
borderStyle: componentStyle.border.style,
221213
overflow: 'hidden',
222214
opacity: visible ? 1 : 0,
215+
top: componentStyle.position.top,
216+
right: componentStyle.position.right,
217+
bottom: componentStyle.position.bottom,
218+
left: componentStyle.position.left,
223219
},
224220
modal: {
225221
width: '100%',
@@ -299,7 +295,7 @@ export default function Modal({
299295
)}
300296
<div
301297
css={{
302-
height: modalContentHeight - headerHeight,
298+
height: `calc(100% - ${headerHeight}px)`,
303299
overflowX: componentStyle.content.overflow.x,
304300
overflowY: componentStyle.content.overflow.y,
305301
}}
@@ -313,6 +309,7 @@ export default function Modal({
313309
paddingRight: componentStyle.content.padding.right,
314310
paddingBottom: componentStyle.content.padding.bottom,
315311
paddingLeft: componentStyle.content.padding.left,
312+
maxHeight: `calc(90vh - ${headerHeight}px)`,
316313
}}
317314
>
318315
{children}

packages/libs/coreui/src/components/inputs/SingleSelect.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Item } from './checkboxes/CheckboxList';
44
import { css } from '@emotion/react';
55
import { uniqueId } from 'lodash';
66
import { CheckIcon } from '../icons';
7+
import { PartialButtonStyleSpec } from '../buttons';
78

89
export interface ItemGroup<T> {
910
label: ReactNode;
@@ -23,6 +24,7 @@ export interface SingleSelectProps<T> {
2324
onSelect: (value: T) => void;
2425
buttonDisplayContent: ReactNode;
2526
isDisabled?: boolean;
27+
styleOverrides?: PartialButtonStyleSpec;
2628
}
2729

2830
const checkIconContainer = { height: 16, width: 16 };
@@ -33,6 +35,7 @@ export default function SingleSelect<T>({
3335
onSelect,
3436
buttonDisplayContent,
3537
isDisabled = false,
38+
styleOverrides,
3639
}: SingleSelectProps<T>) {
3740
const [isPopoverOpen, setIsPopoverOpen] = useState<boolean>(false);
3841

@@ -88,6 +91,7 @@ export default function SingleSelect<T>({
8891
buttonDisplayContent={buttonDisplayContent}
8992
setIsPopoverOpen={setIsPopoverOpen}
9093
isDisabled={isDisabled}
94+
styleOverrides={styleOverrides}
9195
>
9296
<ul
9397
aria-label={'Menu of selectable options'}

packages/libs/eda/src/lib/core/components/variableSelectors/VariableList.tsx

+114-84
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { uniq } from 'lodash';
1+
import { uniq, groupBy } from 'lodash';
22
import React, {
33
useCallback,
44
useEffect,
@@ -40,7 +40,7 @@ import { Tooltip } from '@veupathdb/coreui';
4040
import { safeHtml } from '@veupathdb/wdk-client/lib/Utils/ComponentUtils';
4141
// import ShowHideVariableContext
4242
import { VariableDescriptor } from '../../types/variable';
43-
import { VariableScope } from '../../types/study';
43+
import { FieldWithMetadata, VariableScope } from '../../types/study';
4444
import { ShowHideVariableContext } from '../../utils/show-hide-variable-context';
4545

4646
import { pruneEmptyFields } from '../../utils/wdk-filter-param-adapter';
@@ -192,7 +192,7 @@ interface VariableListProps {
192192
toggleStarredVariable: (targetVariableId: VariableDescriptor) => void;
193193
disabledFieldIds?: string[];
194194
customDisabledVariableMessage?: string;
195-
featuredFields: VariableField[];
195+
featuredFields: FieldWithMetadata[];
196196
showMultiFilterDescendants: boolean;
197197
// Entities in which single child nodes should be promoted
198198
// (replacing their parent in the tree)
@@ -221,7 +221,7 @@ export default function VariableList({
221221
disabledFieldIds,
222222
valuesMap,
223223
fieldTree,
224-
featuredFields = [],
224+
featuredFields,
225225
autoFocus,
226226
starredVariables,
227227
toggleStarredVariable,
@@ -645,7 +645,14 @@ export default function VariableList({
645645
* Render featured fields panel, if data supports it.
646646
*/
647647
const renderFeaturedFields = () => {
648-
return featuredFields.length && allowedFeaturedFields.length ? (
648+
if (!(featuredFields.length && allowedFeaturedFields.length)) return null;
649+
650+
const groupedFeaturedFields = groupBy(
651+
allowedFeaturedFields,
652+
(field) => field.entityName
653+
);
654+
655+
return (
649656
<div
650657
style={{
651658
padding: '0.5em 1em',
@@ -676,97 +683,120 @@ export default function VariableList({
676683
padding: '0.25em',
677684
margin: 0,
678685
color: '#222',
679-
fontWeight: 500,
686+
fontWeight: 'bold',
680687
}}
681688
>
682689
Featured variables
683690
</h3>
684691
</summary>
685-
<ul
686-
style={{
687-
listStyle: 'none',
688-
margin: 0,
689-
marginTop: '0.25em',
690-
padding: 0,
691-
}}
692-
>
693-
{allowedFeaturedFields.map((field) => {
694-
const isActive = field.term === activeField?.term;
695-
const isDisabled = disabledFields.has(field.term);
696-
const [entityId, variableId] = field.term.split('/');
697-
const CustomCheckbox =
698-
customCheckboxes && field.term in customCheckboxes
699-
? customCheckboxes[field.term]
700-
: undefined;
701-
const checked = selectedFields.some((f) => f.term === field.term);
702-
const onChange = (node: any, checked: boolean) => {
703-
if (onSelectedFieldsChange == null) return;
704-
const nextSelectedFields = (
705-
checked
706-
? selectedFields.concat(field)
707-
: selectedFields.filter((f) => f.term !== field.term)
708-
).map((field) => field.term);
709-
onSelectedFieldsChange(nextSelectedFields);
710-
};
711-
712-
return (
713-
<li
714-
key={field.term}
715-
style={{
716-
lineHeight: '15px',
717-
}}
718-
>
719-
<div
692+
<div style={{ height: '0.5em' }} />
693+
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
694+
{Object.entries(groupedFeaturedFields).map(
695+
([entityName, fields]) => (
696+
<li>
697+
<h4 style={{ fontSize: '1.05em', padding: 0 }}>
698+
{entityName}
699+
</h4>
700+
<ul
720701
style={{
721-
position: 'relative',
722-
display: 'flex',
723-
alignItems: 'center',
724-
marginLeft: '1em',
725-
padding: scope === 'download' ? '0.2em 0' : undefined,
702+
listStyle: 'none',
703+
margin: 0,
704+
marginTop: '0.25em',
705+
padding: 0,
726706
}}
727707
>
728-
{isMultiPick &&
729-
(CustomCheckbox ? (
730-
<CustomCheckbox
731-
checked={checked}
732-
onChange={() => onChange(null, checked)}
733-
/>
734-
) : (
735-
<input
736-
type="checkbox"
737-
checked={checked}
738-
onChange={(e) => onChange(null, e.target.checked)}
739-
/>
740-
))}
741-
<FieldNode
742-
isMultiPick={isMultiPick}
743-
isMultiFilterDescendant={false}
744-
showMultiFilterDescendants={showMultiFilterDescendants}
745-
field={field}
746-
isActive={isActive}
747-
isDisabled={isDisabled}
748-
customDisabledVariableMessage={
749-
customDisabledVariableMessage
750-
}
751-
searchTerm=""
752-
variableLinkConfig={variableLinkConfig}
753-
isStarred={starredVariableTermsSet.has(field.term)}
754-
starredVariablesLoading={starredVariablesLoading}
755-
onClickStar={() =>
756-
toggleStarredVariable({ entityId, variableId })
757-
}
758-
scrollIntoView={false}
759-
asDropdown={asDropdown}
760-
isFeaturedField={true}
761-
/>
762-
</div>
708+
{fields.map((field) => {
709+
const isActive = field.term === activeField?.term;
710+
const isDisabled = disabledFields.has(field.term);
711+
const [entityId, variableId] = field.term.split('/');
712+
const CustomCheckbox =
713+
customCheckboxes && field.term in customCheckboxes
714+
? customCheckboxes[field.term]
715+
: undefined;
716+
const checked = selectedFields.some(
717+
(f) => f.term === field.term
718+
);
719+
const onChange = (_node: any, checked: boolean) => {
720+
if (onSelectedFieldsChange == null) return;
721+
const nextSelectedFields = (
722+
checked
723+
? selectedFields.concat(field)
724+
: selectedFields.filter(
725+
(f) => f.term !== field.term
726+
)
727+
).map((field) => field.term);
728+
onSelectedFieldsChange(nextSelectedFields);
729+
};
730+
731+
return (
732+
<li
733+
key={field.term}
734+
style={{
735+
lineHeight: '15px',
736+
}}
737+
>
738+
<div
739+
style={{
740+
position: 'relative',
741+
display: 'flex',
742+
alignItems: 'center',
743+
marginLeft: '0.25em',
744+
padding:
745+
scope === 'download' ? '0.2em 0' : undefined,
746+
}}
747+
>
748+
{isMultiPick &&
749+
(CustomCheckbox ? (
750+
<CustomCheckbox
751+
checked={checked}
752+
onChange={() => onChange(null, checked)}
753+
/>
754+
) : (
755+
<input
756+
type="checkbox"
757+
checked={checked}
758+
onChange={(e) =>
759+
onChange(null, e.target.checked)
760+
}
761+
/>
762+
))}
763+
<FieldNode
764+
isMultiPick={isMultiPick}
765+
isMultiFilterDescendant={false}
766+
showMultiFilterDescendants={
767+
showMultiFilterDescendants
768+
}
769+
field={field}
770+
isActive={isActive}
771+
isDisabled={isDisabled}
772+
customDisabledVariableMessage={
773+
customDisabledVariableMessage
774+
}
775+
searchTerm=""
776+
variableLinkConfig={variableLinkConfig}
777+
isStarred={starredVariableTermsSet.has(
778+
field.term
779+
)}
780+
starredVariablesLoading={starredVariablesLoading}
781+
onClickStar={() =>
782+
toggleStarredVariable({ entityId, variableId })
783+
}
784+
scrollIntoView={false}
785+
asDropdown={asDropdown}
786+
isFeaturedField={true}
787+
/>
788+
</div>
789+
</li>
790+
);
791+
})}
792+
</ul>
763793
</li>
764-
);
765-
})}
794+
)
795+
)}
766796
</ul>
767797
</details>
768798
</div>
769-
) : null;
799+
);
770800
};
771801

772802
const sharedProps = {

packages/libs/eda/src/lib/core/components/variableSelectors/hooks.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export const useFlattenedFields = (
6767
export const useFeaturedFields = (
6868
entities: StudyEntity[],
6969
scope: VariableScope
70-
): Field[] =>
70+
): FieldWithMetadata[] =>
7171
useMemo(() => {
7272
return entities.flatMap((entity) => {
7373
const hiddenVariablesInScope = makeHiddenVariablesInScope(entity, scope);
@@ -82,7 +82,7 @@ export const useFeaturedFields = (
8282
.map((variable) => ({
8383
...variable,
8484
id: `${entity.id}/${variable.id}`,
85-
displayName: `<span class="Entity">${entity.displayName}</span>: ${variable.displayName}`,
85+
entityName: entity.displayName,
8686
}))
8787
.map((variable) => edaVariableToWdkField(variable));
8888
});
@@ -125,8 +125,8 @@ const getFeaturedFieldsFromTreeRecursive = (
125125
...treeNode,
126126
field: {
127127
...treeNode.field,
128+
entityName,
128129
parent: treeNode.field.parent?.split('/')[1],
129-
display: `<span class="Entity">${entityName}</span>: ${treeNode.field.display}`,
130130
},
131131
};
132132

0 commit comments

Comments
 (0)