Skip to content

Commit

Permalink
Metrics/Params selection dropdown (#43)
Browse files Browse the repository at this point in the history
Add a select all checkbox and regex search support for metrics and params selection dropdown.
  • Loading branch information
fabiovincenzi authored Feb 28, 2024
1 parent c6a6b35 commit faddcf6
Show file tree
Hide file tree
Showing 6 changed files with 312 additions and 153 deletions.
27 changes: 27 additions & 0 deletions src/src/components/SelectFormPopper/SelectFormPopper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// @ts-nocheck
/* eslint-disable react/prop-types */
import React from 'react';

import {
ISelectConfig,
ISelectOption,
} from 'types/services/models/explorer/createAppModel';

export interface ISelectFormPopperProps {
id: string | undefined;
type: 'metrics' | 'params';
open: boolean;
anchorEl: HTMLElement | null;
options: ISelectOption[];
selectedData?: ISelectConfig;
onSelect: (event: React.ChangeEvent<{}>, value: ISelectOption[]) => void;
searchValue: string;
handleSearchInputChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
handleClose(event: any, reason: any): void;
regexError: string | null;
setRegexError: (value: string | null) => void;
isRegexSearch: boolean;
setIsRegexSearch: (value: boolean) => void;
className: string;
classes: { [key: string]: string };
}
152 changes: 152 additions & 0 deletions src/src/components/SelectFormPopper/SelectFormPopper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// @ts-nocheck
/* eslint-disable react/prop-types */
import React from 'react';

import ToggleButton from '@material-ui/lab/ToggleButton';
import {
Checkbox,
InputBase,
Popper,
Snackbar,
Tooltip,
} from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import MuiAlert from '@material-ui/lab/Alert';
import CheckBoxOutlineBlank from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';

import { Text } from 'components/kit';

import { ISelectOption } from 'types/services/models/explorer/createAppModel';

const SelectFormPopper: React.FC<ISelectFormPopperProps> = ({
id,
open,
anchorEl,
options,
selectedData,
onSelect,
searchValue,
handleSearchInputChange,
handleClose,
regexError,
setRegexError,
isRegexSearch,
setIsRegexSearch,
className,
classes,
}) => {
return (
<Popper
id={id}
open={open}
anchorEl={anchorEl}
placement='bottom-start'
className={className}
>
<Autocomplete
open
onClose={handleClose}
multiple
size='small'
disablePortal
disableCloseOnSelect
options={options}
value={selectedData?.options}
onChange={onSelect}
classes={{
popper: classes.popper,
}}
groupBy={(option) => option.group}
getOptionLabel={(option) => option.label}
renderTags={() => null}
disableClearable={true}
ListboxProps={{
style: {
height: 400,
},
}}
renderInput={(params) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
<Tooltip title='Select all visible'>
<Checkbox
color='primary'
icon={<CheckBoxOutlineBlank />}
checkedIcon={<CheckBoxIcon />}
checked={selectedData?.options.length === options.length}
onChange={(event) => {
if (event.target.checked) {
onSelect(event, options);
} else {
onSelect(event, []);
}
}}
size='small'
/>
</Tooltip>
<InputBase
ref={params.InputProps.ref}
inputProps={{
...params.inputProps,
value: searchValue,
onChange: handleSearchInputChange,
}}
spellCheck={false}
placeholder='Search'
autoFocus={true}
style={{ flex: 1 }}
className='Popper__SelectForm__select'
/>
<Snackbar
open={!!regexError}
autoHideDuration={6000}
onClose={() => setRegexError(null)}
>
<MuiAlert
elevation={6}
variant='filled'
severity='error'
onClose={() => setRegexError(null)}
>
{regexError}
</MuiAlert>
</Snackbar>
<Tooltip title='Use Regular Expression'>
<ToggleButton
value='check'
selected={isRegexSearch}
onChange={() => {
setIsRegexSearch(!isRegexSearch);
}}
className='RegexToggle'
>
.*
</ToggleButton>
</Tooltip>
</div>
)}
renderOption={(option) => {
let selected: boolean = !!selectedData?.options.find(
(item: ISelectOption) => item.key === option.key,
)?.key;
return (
<div className='Popper__SelectForm__option'>
<Checkbox
color='primary'
icon={<CheckBoxOutlineBlank />}
checkedIcon={<CheckBoxIcon />}
checked={selected}
size='small'
/>
<Text className='Popper__SelectForm__option__label' size={14}>
{option.label}
</Text>
</div>
);
}}
/>
</Popper>
);
};

export default SelectFormPopper;
19 changes: 19 additions & 0 deletions src/src/pages/Metrics/components/SelectForm/SelectForm.scss
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,22 @@
}
}
}

.RegexToggle {
border: none;
width: 2rem;
height: 2rem;
margin: 10px;
}

.Popper__SelectForm__select{
@extend .Metrics__SelectForm__metric__select;
}

.Popper__SelectForm__option{
@extend .Metrics__SelectForm__option !optional;
}

.Popper__SelectForm__option__label{
@extend .Metrics__SelectForm__option__label !optional;
}
130 changes: 48 additions & 82 deletions src/src/pages/Metrics/components/SelectForm/SelectForm.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,13 @@
import React from 'react';
import classNames from 'classnames';

import {
Box,
Checkbox,
Divider,
InputBase,
Popper,
Tooltip,
} from '@material-ui/core';
import Autocomplete from '@material-ui/lab/Autocomplete';
import {
CheckBox as CheckBoxIcon,
CheckBoxOutlineBlank,
} from '@material-ui/icons';
import { Box, Divider, Tooltip } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';

import { Button, Icon, Badge, Text } from 'components/kit';
import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary';
import AutocompleteInput from 'components/AutocompleteInput';
import SelectFormPopper from 'components/SelectFormPopper/SelectFormPopper';

import { ANALYTICS_EVENT_KEYS } from 'config/analytics/analyticsKeysMap';

Expand All @@ -29,6 +19,12 @@ import { ISelectOption } from 'types/services/models/explorer/createAppModel';

import './SelectForm.scss';

const useStyles = makeStyles({
popper: {
width: '100% !important',
},
});

function SelectForm({
requestIsPending,
isDisabled = false,
Expand All @@ -42,6 +38,8 @@ function SelectForm({
}: ISelectFormProps): React.FunctionComponentElement<React.ReactNode> {
const [anchorEl, setAnchorEl] = React.useState<any>(null);
const [searchValue, setSearchValue] = React.useState<string>('');
const [isRegexSearch, setIsRegexSearch] = React.useState(false);
const [regexError, setRegexError] = React.useState<string | null>(null);
const searchRef: any = React.useRef<React.MutableRefObject<any>>(null);
const autocompleteRef: any = React.useRef<React.MutableRefObject<any>>(null);
const advancedAutocompleteRef: any =
Expand All @@ -51,6 +49,7 @@ function SelectForm({
searchRef.current?.abort();
};
}, []);
const classes = useStyles();

function handleMetricSearch(): void {
if (requestIsPending) {
Expand Down Expand Up @@ -82,7 +81,7 @@ function SelectForm({
event: React.ChangeEvent<{}>,
value: ISelectOption[],
): void {
if (event.type === 'click') {
if (event.type === 'click' || event.type === 'change') {
const lookup = value.reduce(
(acc: { [key: string]: number }, curr: ISelectOption) => {
acc[curr.key] = ++acc[curr.key] || 0;
Expand Down Expand Up @@ -133,12 +132,27 @@ function SelectForm({
}

const options = React.useMemo(() => {
return (
selectFormData?.options?.filter(
(option) => option.label.indexOf(searchValue) !== -1,
) ?? []
);
}, [searchValue, selectFormData?.options]);
if (isRegexSearch) {
try {
const regex = new RegExp(searchValue, 'i');
setRegexError(null);
return (
selectFormData?.options?.filter((option) =>
regex.test(option.label),
) ?? []
);
} catch (error) {
setRegexError('Invalid Regex');
return [];
}
} else {
return (
selectFormData?.options?.filter(
(option) => option.label.indexOf(searchValue) !== -1,
) ?? []
);
}
}, [searchValue, selectFormData?.options, isRegexSearch]);

const open: boolean = !!anchorEl;
const id = open ? 'select-metric' : undefined;
Expand Down Expand Up @@ -177,72 +191,24 @@ function SelectForm({
<Icon name='plus' style={{ marginRight: '0.5rem' }} />
Metrics
</Button>
<Popper
<SelectFormPopper
id={id}
type='metrics'
open={open}
anchorEl={anchorEl}
placement='bottom-start'
options={options}
selectedData={selectedMetricsData}
onSelect={onSelect}
searchValue={searchValue}
handleSearchInputChange={handleSearchInputChange}
handleClose={handleClose}
regexError={regexError}
setRegexError={setRegexError}
isRegexSearch={isRegexSearch}
setIsRegexSearch={setIsRegexSearch}
className='Metrics__SelectForm__Popper'
>
<Autocomplete
open
onClose={handleClose}
multiple
className='Autocomplete__container'
size='small'
disablePortal={true}
disableCloseOnSelect
options={options}
value={selectedMetricsData?.options}
onChange={onSelect}
groupBy={(option) => option.group}
getOptionLabel={(option) => option.label}
renderTags={() => null}
disableClearable={true}
ListboxProps={{
style: {
height: 400,
},
}}
renderInput={(params) => (
<InputBase
ref={params.InputProps.ref}
inputProps={{
...params.inputProps,
value: searchValue,
onChange: handleSearchInputChange,
}}
spellCheck={false}
placeholder='Search'
autoFocus={true}
className='Metrics__SelectForm__metric__select'
/>
)}
renderOption={(option) => {
let selected: boolean =
!!selectedMetricsData?.options.find(
(item: ISelectOption) => item.key === option.key,
)?.key;
return (
<div className='Metrics__SelectForm__option'>
<Checkbox
color='primary'
icon={<CheckBoxOutlineBlank />}
checkedIcon={<CheckBoxIcon />}
checked={selected}
size='small'
/>
<Text
className='Metrics__SelectForm__option__label'
size={14}
>
{option.label}
</Text>
</div>
);
}}
/>
</Popper>
classes={classes}
/>
<Divider
style={{ margin: '0 1rem' }}
orientation='vertical'
Expand Down
Loading

0 comments on commit faddcf6

Please sign in to comment.