feat(cloud data source config): GUI and API for configuring a cloud data source with Google cloud healthcare implementation (#3589)

This commit is contained in:
Joe Boccanfuso authored and GitHub committed 2023-08-25 14:19:17 -04:00
1 parent a2ef2b0fcb
commit a336992971
28 files changed
+1155 -56

No files matched your search

@@ -1,6 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="none" fill-rule="evenodd">
<path d="M0 0h16v16H0z"/>
<rect stroke="#7BB2CE" fill="#0D0E24" x=".5" y=".5" width="15" height="15" rx="7.5"/>
<rect stroke="currentColor" fill="#0D0E24" x=".5" y=".5" width="15" height="15" rx="7.5"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 273 B

@@ -1,4 +1,4 @@
import Button, {ButtonType, ButtonSize} from '../Button';
import {Button, ButtonEnums} from '../../../components';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
import {
createComponentTemplate,
@@ -45,8 +45,8 @@ There can be different types of buttons: `primary`, and `secondary`.
<Canvas>
<Story name="Types">
<div className="flex space-x-2">
<Button type={ButtonType.primary}>Primary Button</Button>
<Button type={ButtonType.secondary}>Secondary Button</Button>
<Button type={ButtonEnums.type.primary}>Primary Button</Button>
<Button type={ButtonEnums.type.secondary}>Secondary Button</Button>
</div>
</Story>
</Canvas>
@@ -59,8 +59,8 @@ to the button's height.
<Canvas>
<Story name="Colors">
<div className="flex items-center space-x-2">
<Button size={ButtonSize.small}>Small Button</Button>
<Button size={ButtonSize.medium}>Medium Button</Button>
<Button size={ButtonEnums.size.small}>Small Button</Button>
<Button size={ButtonEnums.size.medium}>Medium Button</Button>
</div>
</Story>
</Canvas>
@@ -71,7 +71,7 @@ You can mix different props together to create a button.
<Canvas>
<Story name="Custom">
<Button type={ButtonType.secondary} size={ButtonSize.small}>
<Button type={ButtonEnums.type.secondary} size={ButtonEnums.size.small}>
Small, Secondary Button
</Button>
</Story>
@@ -0,0 +1,91 @@
import classNames from 'classnames';
import debounce from 'lodash.debounce';
import React, {
ReactElement,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import Icon from '../Icon';
type InputFilterTextProps = {
className?: string;
value?: string;
placeholder: string;
onDebounceChange?: (val: string) => void;
onChange?: (val: string) => void;
debounceTime?: number;
};
/**
* A component to use as the input for text to filter by/on. A debounced callback is automatically provided
* so that the filtering in-turn will be debounced. There is also a straight onChange callback when the filter value is
* required immediately and NOT debounced. The debounce time is also configurable.
*/
const InputFilterText = ({
className,
value = '',
placeholder,
onDebounceChange,
onChange,
debounceTime = 200,
}: InputFilterTextProps): ReactElement => {
const [filterValue, setFilterValue] = useState<string>(value);
const searchInputRef = useRef(null);
const debouncedOnChange = useMemo(() => {
return debounce(onDebounceChange || (() => {}), debounceTime);
}, []);
// This allows for the filter value to be updated via the props.
useEffect(() => setFilterValue(value), [value]);
useEffect(() => {
return debouncedOnChange?.cancel();
}, []);
const handleFilterTextChanged = useCallback(value => {
setFilterValue(value);
if (onChange) {
onChange(value);
}
if (onDebounceChange) {
debouncedOnChange(value);
}
}, []);
return (
<label className={classNames('relative', className)}>
<span className="absolute inset-y-0 left-0 flex items-center pl-2">
<Icon name="icon-search"></Icon>
</span>
<input
ref={searchInputRef}
type="text"
className="block bg-black w-full shadow transition duration-300 appearance-none border border-inputfield-main focus:border-inputfield-focus focus:outline-none disabled:border-inputfield-disabled rounded-md w-full py-2 px-9 text-base leading-tight placeholder:text-inputfield-placeholder"
placeholder={placeholder}
onChange={event => handleFilterTextChanged(event.target.value)}
autoComplete="off"
value={filterValue}
></input>
<span className="absolute inset-y-0 right-0 flex items-center pr-2">
<Icon
name="icon-clear-field"
className={classNames('cursor-pointer', filterValue ? '' : 'hidden')}
onClick={() => {
searchInputRef.current.value = '';
handleFilterTextChanged('');
}}
></Icon>
</span>
</label>
);
};
export default InputFilterText;
@@ -0,0 +1,54 @@
import InputFilterText from '../InputFilterText';
import { ArgsTable, Story, Canvas, Meta } from '@storybook/addon-docs';
export const argTypes = {
component: InputFilterText,
title: 'Components/InputFilterText',
};
<Meta title="Components/InputFilterText" component={InputFilterText} />
export const InputFilterTextTemplate = args => (
<div className="w-80">
<InputFilterText {...args} />
</div>
)
<Heading title="InputFilterText" componentRelativePath="InputFilterText/InputFilterText.tsx" />
- [Overview](#overview)
- [Props](#props)
- [Contribute](#contribute)
## Overview
InputFilterText is a component that is styled such that it can be used as a text input to
filter a list of textual items. It allows you to enter any text. There are two (optional)
callbacks that can be invoked as the characters of the text are entered: one
callback is invoked as each character is typed and another is debounced so that
any filtering can occur once the user has entered a significant amount of info and
pausing by a configurable amount of time in milliseconds. The component also
provides a button on the far right of the component that when clicked will clear the input
text. The button only appears when there is text in the component.
<Canvas>
<Story
name="Overview"
args={{
className: "w-full text-white",
placeholder: "Search for...",
onChange: () => console.log('input text changed'),
onDebounceChange: () => console.log('debounce text changed'),
}}
>
{InputFilterTextTemplate.bind({})}
</Story>
</Canvas>
## Props
<ArgsTable of={InputFilterText} />
## Contribute
<Footer componentRelativePath="InputFilterText/__stories__/InputFilterText.stories.mdx" />
@@ -0,0 +1,2 @@
import InputFilterText from './InputFilterText';
export default InputFilterText;
@@ -15,6 +15,7 @@ const StudyListFilter = ({
isFiltering,
numOfStudies,
onUploadClick,
getDataSourceConfigurationComponent,
}) => {
const { t } = useTranslation('StudyList');
const { sortBy, sortDirection } = filterValues;
@@ -30,13 +31,15 @@ const StudyListFilter = ({
return (
<React.Fragment>
<div>
<div className="bg-primary-dark">
<div className="container relative flex flex-col pt-5 m-auto">
<div className="flex flex-row justify-between px-12 mb-5">
<div className="flex flex-row">
<Typography variant="h4" className="mr-6 text-primary-light">
<div className="bg-black">
<div className="container relative flex flex-col pt-5 mx-auto">
<div className="flex flex-row justify-between mb-5">
<div className="flex flex-row items-center gap-6 shrink min-w-[1px]">
<Typography variant="h6" className="text-white">
{t('StudyList')}
</Typography>
{getDataSourceConfigurationComponent &&
getDataSourceConfigurationComponent()}
{onUploadClick && (
<div
className="flex items-center gap-2 cursor-pointer text-primary-active text-lg self-center font-semibold"
@@ -63,7 +66,7 @@ const StudyListFilter = ({
</LegacyButton>
)}
<Typography
variant="h4"
variant="h6"
className="mr-2"
data-cy={'num-studies'}
>
@@ -71,7 +74,7 @@ const StudyListFilter = ({
</Typography>
<Typography
variant="h6"
className="self-end pb-1 text-common-light"
className="self-end pb-1 text-primary-light"
>
{t('Studies')}
</Typography>
@@ -80,8 +83,8 @@ const StudyListFilter = ({
</div>
</div>
</div>
<div className="sticky z-10 border-b-4 border-black -top-1">
<div className="pt-3 pb-3 bg-primary-dark ">
<div className="sticky z-10 border-b-4 border-black -top-1 mx-auto">
<div className="pt-3 pb-3 bg-primary-dark">
<InputGroup
inputMeta={filtersMeta}
values={filterValues}
@@ -134,6 +137,7 @@ StudyListFilter.propTypes = {
clearFilters: PropTypes.func.isRequired,
isFiltering: PropTypes.bool.isRequired,
onUploadClick: PropTypes.func,
getDataSourceConfigurationComponent: PropTypes.func,
};
export default StudyListFilter;
+2
View File
@@ -13,6 +13,7 @@ import Icon from './Icon';
import IconButton from './IconButton';
import Input from './Input';
import InputDateRange from './InputDateRange';
import InputFilterText from './InputFilterText';
import InputGroup from './InputGroup';
import InputLabelWrapper from './InputLabelWrapper';
import InputMultiSelect from './InputMultiSelect';
@@ -97,6 +98,7 @@ export {
IconButton,
Input,
InputDateRange,
InputFilterText,
InputGroup,
InputRange,
InputNumber,
+1
View File
@@ -58,6 +58,7 @@ export {
InputRange,
InputNumber,
InputDateRange,
InputFilterText,
InputGroup,
InputLabelWrapper,
InputMultiSelect,