console: fix operations filters

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8097
GitOrigin-RevId: 33a72186a38094bbcdee85d87d0f0a05aadb1736
This commit is contained in:
Nicolas Inchauspe 2023-02-24 12:56:18 +01:00 committed by hasura-bot
parent fbab8cd755
commit 975d4ae91b
6 changed files with 249 additions and 182 deletions

View File

@ -711,24 +711,27 @@
vertical-align: middle;
}
.selectBox {
background-color: #ffffff;
font-family: 'Gudea';
font-size: 15px;
font-weight: bold;
color: #505050;
padding: 16px 16px;
display: inline-block;
padding: 16px 20px;
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
.selectBoxRow {
display: inline-block;
}
span {
min-width: 100px;
padding-right: 12px;
display: block;
flex-shrink: 1;
white-space: nowrap;
}
}
.dropDownBtn {
min-width: 180px;
button {
width: 100%;
border-radius: 2px;
border: solid 1px #dddddd;
@ -743,10 +746,6 @@
align-items: center;
cursor: pointer;
position: relative;
img {
position: absolute;
right: 12px;
}
&:focus {
outline: none;
border: solid 1px #f8c533;
@ -755,7 +754,6 @@
border: solid 1px #f8c533;
background-color: #ffffff;
}
}
span {
padding-right: 0;
}
@ -787,6 +785,8 @@
/* common checkbox */
.commonCheckBox,
.defaultCheckBox {
min-width: 110px;
input[type='checkbox'] {
position: absolute; // take it out of document flow
opacity: 0; // hide it
@ -836,7 +836,7 @@
content: '';
position: absolute;
left: 7px;
top: 3px;
top: 5px;
width: 5px;
height: 10px;
border: solid white;

View File

@ -1,99 +0,0 @@
import React, { useState } from 'react';
import Dropdown from 'react-bootstrap/lib/Dropdown';
import MenuItem from 'react-bootstrap/lib/MenuItem';
import { SELECT_ALL_NAME_DISPLAY } from '../../constants';
import styles from '../../Metrics.module.scss';
import dropdown from '../../images/drop-down.svg';
const DropdownComponent = props => {
/* Accepts children which can be a single Menu Items
* or array of Menu Items
* TODO: How to validate whether the childrens are only of Menu item type?
* */
const [show, setShow] = useState(false);
const {
id,
displayValue,
options,
onChange,
selectedValues,
children,
selectAll,
} = props;
const selectedValuesList =
Object.keys(selectedValues).length === options.length - 1
? Object.assign({ ...selectedValues }, { 'Select All': true })
: selectedValues;
const handleToggle = (isOpen, event, metadata) => {
if (isOpen || metadata.source !== 'select') {
setShow(isOpen);
}
};
const handleChange = val => {
if (
val === SELECT_ALL_NAME_DISPLAY &&
Object.keys(selectedValuesList).length === options.length
) {
selectAll(id, options, true);
} else if (val === SELECT_ALL_NAME_DISPLAY) {
selectAll(id, options);
} else {
onChange(val);
}
};
const renderTitle = title => {
if (
title === 'Select All' &&
Object.keys(selectedValuesList).length === options.length
) {
return 'Unselect All';
}
return title;
};
return (
<Dropdown
open={show}
id="dropdown-custom-menu"
onToggle={handleToggle}
onSelect={e => {
handleChange(e);
}}
className={styles.dropDownBtn}
>
<Dropdown.Toggle noCaret>
{displayValue}
<img src={dropdown} alt={'Dropdown arrow'} />
</Dropdown.Toggle>
<Dropdown.Menu>
{options.map((list, index) => {
return (
<MenuItem key={displayValue + '_' + index} eventKey={list.title}>
<div className={styles.commonCheckBox}>
<input
id={id + '_' + index}
type="checkbox"
className="legacy-input-fix"
value={list.title}
onChange={() => null}
checked={selectedValuesList[list.title] ? true : false}
/>
<label
className={styles.eclipseText + ' ' + styles.fw_light}
htmlFor={id + '_' + index}
>
{renderTitle(list.title)}
</label>
</div>
</MenuItem>
);
})}
{children}
</Dropdown.Menu>
</Dropdown>
);
};
export default DropdownComponent;

View File

@ -0,0 +1,134 @@
import React, { useState, useEffect } from 'react';
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import clsx from 'clsx';
import { FaChevronDown } from 'react-icons/fa';
import { SELECT_ALL_NAME_DISPLAY } from '../../constants';
import styles from '../../Metrics.module.scss';
import { v4 as uuid } from 'uuid';
const DropdownComponent = (props: any) => {
/* Accepts children which can be a single Menu Items
* or array of Menu Items
* TODO: How to validate whether the childrens are only of Menu item type?
* */
const [componentId] = useState(uuid());
const [show, setShow] = useState(false);
const {
id,
displayValue,
options,
onChange,
selectedValues,
children,
selectAll,
} = props;
const selectedValuesList =
Object.keys(selectedValues).length === options.length - 1
? Object.assign({ ...selectedValues }, { 'Select All': true })
: selectedValues;
// Force dropdown content to be same width as trigger
useEffect(() => {
const component = document.getElementById(componentId);
document.documentElement.style.setProperty(
`--radix-dropdown-menu-trigger-width-${componentId}`,
component?.offsetWidth + 'px'
);
}, [displayValue, componentId]);
const handleToggle = (open: boolean) => {
setShow(open);
};
const handleChange = (val: string) => {
if (
val === SELECT_ALL_NAME_DISPLAY &&
Object.keys(selectedValuesList).length === options.length
) {
selectAll(id, options, true);
} else if (val === SELECT_ALL_NAME_DISPLAY) {
selectAll(id, options);
} else {
onChange(val);
}
};
const renderTitle = (title: string) => {
if (
title === 'Select All' &&
Object.keys(selectedValuesList).length === options.length
) {
return 'Unselect All';
}
return title;
};
return (
<DropdownMenu.Root open={show} onOpenChange={handleToggle}>
<DropdownMenu.Trigger
id={componentId}
className={clsx(
styles['dropDownBtn'],
'flex justify-between gap-3 px-3 py-2 cursor-pointer',
'font-normal block w-full h-input shadow-sm rounded border border-gray-300 hover:border-gray-400 focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-yellow-200 focus-visible:border-yellow-400 placeholder-gray-500'
)}
>
<div
className={
Object.keys(selectedValuesList).length > 0 ? '' : 'text-gray-500'
}
>
{displayValue}
</div>
<FaChevronDown className={clsx(show ? 'rotate-180' : 'rotate-0')} />
</DropdownMenu.Trigger>
<DropdownMenu.Content
className="border border-slate-300 rounded bg-white cursor-pointer z-50"
align="start"
style={{
minWidth: `var(--radix-dropdown-menu-trigger-width-${componentId})`,
}}
>
{options.map((list: any, index: string) => {
return (
<DropdownMenu.Item
key={displayValue + '_' + index}
className="bg-white hover:bg-slate-200 px-3 py-2 border-slate-300 border-b font-sans"
onSelect={() => handleChange(list.title as string)}
>
<div
className={clsx(
styles['commonCheckBox'],
'flex flex-row items-baseline'
)}
>
<input
id={id + '_' + index}
type="checkbox"
className="legacy-input-fix"
value={list.title}
onChange={() => {}}
checked={selectedValuesList[list?.title || ''] ? true : false}
/>
<label
className={clsx(styles['eclipseText'], styles['fw_light'])}
htmlFor={id + '_' + index}
>
{renderTitle(list.title as string)}
</label>
</div>
</DropdownMenu.Item>
);
})}
{children ? (
<div className="bg-white hover:bg-slate-200 px-3 py-2 border-slate-300 border-b list-none font-sans">
{children}
</div>
) : null}
</DropdownMenu.Content>
</DropdownMenu.Root>
);
};
export default DropdownComponent;

View File

@ -1,4 +1,5 @@
import React, { useState } from 'react';
import clsx from 'clsx';
/* calls the onChange configured when enter is pressed
* and clears the current input
@ -19,10 +20,14 @@ const FilterInputComponent = props => {
}
};
return (
<div className={styles.selectBox} id={id}>
<div className={clsx(styles.selectBox)} id={id}>
<span>{filterTitle}</span>
<input
className={styles.commonInput}
className={clsx(
styles.commonInput,
'w-full',
'block font-normal w-full h-input shadow-sm rounded border border-gray-300 hover:border-gray-400 focus-visible:outline-0 focus-visible:ring-2 focus-visible:ring-yellow-200 focus-visible:border-yellow-400 placeholder-gray-500'
)}
type="text"
placeholder={placeholder}
onKeyDown={handleEnterKeyPress}

View File

@ -1,11 +1,13 @@
import React, { useState } from 'react';
import GenerateFilters from './GenerateFilters';
import { Button } from '@hasura/console-legacy-ce';
import { FaChevronDown } from 'react-icons/fa';
import FilterSection from './FilterSection';
import FilterBadge from './FilterBadge';
import filter from '../../images/filter.svg';
import styles from '../../Metrics.module.scss';
import clsx from 'clsx';
const Filters = ({
projectId,
@ -19,7 +21,7 @@ const Filters = ({
retrieveDefaultDropdownOptions,
reset,
selectAll,
}) => {
}: any) => {
const [displayFilters, setFiltersDisplay] = useState(false);
const renderSelectedFiltersCount = () => {
return values.length > 0 ? `(${values.length})` : '';
@ -28,7 +30,7 @@ const Filters = ({
if (values.length > 0) {
return (
<Button mode="destructive" onClick={reset}>
Remove all filters
Reset all filters
</Button>
);
}
@ -36,8 +38,8 @@ const Filters = ({
};
const renderSelectedFilters = () => {
if (values.length > 0) {
return values.map((f, i) => {
const composeFilterObj = o => {
return values.map((f: { value: any; type: any }, i: any) => {
const composeFilterObj = (o: { [x: string]: any }) => {
const keyElements = Object.keys(o);
if (keyElements.length > 0) {
return keyElements.map(k => `${k}: ${o[k]}`).join(', ');
@ -53,7 +55,7 @@ const Filters = ({
return (
<FilterBadge
key={i}
text={`${getTitle(f.type)}: ${filterValue(f.value)}`}
text={`${getTitle(f.type)}: ${filterValue()}`}
onClick={() => onChange(f.type, f.value)}
/>
);
@ -69,8 +71,19 @@ const Filters = ({
return (
<div className="filtersElementWrapper">
<FilterSection>
<div className={styles.filterBtnWrapper}>
<div onClick={toggleFilters} className={styles.cursorPointer}>
<div className={styles['filterBtnWrapper']}>
<button
className={clsx('cursor-pointer group mr-2')}
onClick={toggleFilters}
>
<FaChevronDown
className={clsx(
'w-8 h-8 p-2 group-hover:bg-slate-200 transition-all duration-150 rounded-full',
displayFilters ? 'rotate-180' : 'rotate-0'
)}
/>
</button>
<div onClick={toggleFilters} className={styles['cursorPointer']}>
<img
src={filter}
alt="Filter"
@ -81,7 +94,7 @@ const Filters = ({
height: '20px',
}}
/>
<div className={styles.subHeader}>
<div className={styles['subHeader']}>
Filters {renderSelectedFiltersCount()}
</div>
</div>

View File

@ -14,6 +14,7 @@ import FilterTypeInput from './FilterTypeInput';
import TimeRangeFilter from './TimeRangeFilter';
import FilterTypeCheckbox from './FilterTypeCheckbox';
import ComposeDropdownFilter from './ComposeDropdownFilter';
import { DocumentNode } from 'graphql';
/*
* Dropdown filters are not rendered. The pattern ensures that data for
@ -40,7 +41,7 @@ const GenerateFilters = ({
filters,
values,
selectAll,
}) => {
}: any) => {
const filtersHtml = [];
const [dropdownFilters, nonDropdownFilters] = splitByType(
filters,
@ -50,7 +51,8 @@ const GenerateFilters = ({
return null;
}
const hOrderFn = compose(onChange);
nonDropdownFilters.forEach((filter, i) => {
nonDropdownFilters.forEach(
(filter: { type: any; value: any }, i: React.Key | null | undefined) => {
const { type, value } = filter;
const onFilterChange = hOrderFn(value);
switch (type) {
@ -90,7 +92,8 @@ const GenerateFilters = ({
default:
console.error('Unsupported type');
}
});
}
);
if (dropdownFilters.length > 0) {
filtersHtml.push(
@ -109,7 +112,18 @@ const GenerateFilters = ({
);
}
return filtersHtml;
return (
<div
className="grid"
style={{
backgroundColor: '#f5f5f5',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gridGap: '2px',
}}
>
{filtersHtml}
</div>
);
};
export default GenerateFilters;