feat(ui) - funnels - issues filters

This commit is contained in:
Shekar Siri 2022-05-12 12:55:34 +02:00
parent a8fbf50a49
commit 8e1bb95c84
6 changed files with 151 additions and 8 deletions

View file

@ -2,6 +2,7 @@ import { useStore } from 'App/mstore';
import { useObserver } from 'mobx-react-lite';
import React, { useEffect } from 'react';
import { NoContent, Loader } from 'UI';
import FunnelIssuesDropdown from '../FunnelIssuesDropdown';
import FunnelIssuesSort from '../FunnelIssuesSort';
function FunnelIssues(props) {
@ -18,7 +19,12 @@ function FunnelIssues(props) {
<div className="my-8">
<div className="flex justify-between">
<h1 className="font-medium text-2xl">Most significant issues <span className="font-normal">identified in this funnel</span></h1>
<FunnelIssuesSort />
</div>
<div className="my-6 flex justify-between items-start">
<FunnelIssuesDropdown />
<div className="flex-shrink-0">
<FunnelIssuesSort />
</div>
</div>
<Loader loading={loading}>
<NoContent

View file

@ -0,0 +1,89 @@
import React, { Component, ReactNode, FunctionComponent } from 'react';
import Select from 'Shared/Select'
import { components } from 'react-select';
import { Icon } from 'UI';
import FunnelIssuesSelectedFilters from '../FunnelIssuesSelectedFilters';
const options = [
{ value: "click_rage", label: "Click Rage" },
{ value: "dead_click", label: "Dead Click" },
{ value: "excessive_scrolling", label: "Excessive Scrolling" },
{ value: "bad_request", label: "Bad Request" },
{ value: "missing_resource", label: "Missing Image" },
{ value: "memory", label: "High Memory Usage" },
{ value: "cpu", label: "High CPU" },
{ value: "slow_resource", label: "Slow Resource" },
{ value: "slow_page_load", label: "Slow Page" },
{ value: "crash", label: "Crash" },
{ value: "custom_event_error", label: "Custom Error" },
{ value: "js_error", label: "Error" }
]
function FunnelIssuesDropdown(props) {
const [isOpen, setIsOpen] = React.useState(false);
const [selectedValues, setSelectedValues] = React.useState<any>(options.map(option => option.value));
const filteredOptions = options.filter((option: any) => {
return !selectedValues.includes(option.value);
});
const selectedOptions = options.filter((option: any) => {
return selectedValues.includes(option.value);
});
const handleChange = ({ value }: any) => {
toggleSelectedValue(value);
}
const toggleSelectedValue = (value: string) => {
if (selectedValues.includes(value)) {
setSelectedValues(selectedValues.filter(v => v !== value));
} else {
setSelectedValues([...selectedValues, value]);
}
}
return (
<div className="flex items-start">
<Select
menuIsOpen={isOpen}
onMenuOpen={() => setIsOpen(true)}
onMenuClose={() => setIsOpen(false)}
options={filteredOptions}
onChange={handleChange}
styles={{
control: (provided) => ({
...provided,
border: 'none',
boxShadow: 'none',
backgroundColor: 'transparent',
minHeight: 'unset',
}),
menuList: (provided) => ({
...provided,
padding: 0,
minWidth: '190px',
}),
}}
components={{
ValueContainer: () => null,
DropdownIndicator: () => null,
IndicatorSeparator: () => null,
IndicatorsContainer: () => null,
Control: ({ children, ...props }: any) => (
<components.Control {...props}>
{ children }
<button className="px-2 py-1 bg-white rounded-2xl border border-teal border-dashed color-teal flex items-center hover:bg-active-blue" onClick={() => setIsOpen(!isOpen)}>
<Icon name="funnel" size={16} color="teal" />
<span className="ml-2">Issues</span>
</button>
</components.Control>
),
Placeholder: () => null,
SingleValue: () => null,
}}
/>
<FunnelIssuesSelectedFilters options={selectedOptions} removeSelectedValue={toggleSelectedValue} />
</div>
);
}
export default FunnelIssuesDropdown;

View file

@ -0,0 +1 @@
export { default } from './FunnelIssuesDropdown';

View file

@ -0,0 +1,24 @@
import React from 'react';
import { Icon } from 'UI';
interface Props {
options: any[];
removeSelectedValue: (value: string) => void;
}
function FunnelIssuesSelectedFilters(props: Props) {
const { options, removeSelectedValue } = props;
return (
<div className="flex items-center flex-wrap">
{options.map((option, index) => (
<div key={index} className="transition-all ml-2 mb-2 flex items-center border rounded-2xl bg-white select-none overflow-hidden">
<span className="pl-3 color-gray-dark">{option.label}</span>
<button className="ml-1 hover:bg-active-blue px-2 py-2" onClick={() => removeSelectedValue(option.value)}>
<Icon name="close"/>
</button>
</div>
))}
</div>
);
}
export default FunnelIssuesSelectedFilters;

View file

@ -0,0 +1 @@
export { default } from './FunnelIssuesSelectedFilters';

View file

@ -9,27 +9,44 @@ interface Props {
defaultValue?: string;
plain?: boolean;
components?: any;
styles?: any;
[x:string]: any;
}
export default function({ alignRight = false, plain = false, options, isSearchable = false, components = {}, defaultValue = '', ...rest }: Props) {
export default function({ styles= {}, alignRight = false, plain = false, options, isSearchable = false, components = {}, defaultValue = '', ...rest }: Props) {
const defaultSelected = defaultValue ? options.find(x => x.value === defaultValue) : null;
const customStyles = {
option: (provided, state) => ({
...provided,
whiteSpace: 'nowrap',
...provided,
whiteSpace: 'nowrap',
transition: 'all 0.3s',
backgroundColor: state.isFocused ? colors['active-blue'] : 'transparent',
color: state.isFocused ? colors.teal : 'black',
'&:hover': {
transition: 'all 0.2s',
backgroundColor: colors['active-blue'],
},
'&:focus': {
transition: 'all 0.2s',
backgroundColor: colors['active-blue'],
}
}),
menu: (provided, state) => ({
...provided,
top: 31,
border: 'solid thin #ccc',
border: `1px solid ${colors['gray-light']}`,
borderRadius: '3px',
backgroundColor: '#fff',
boxShadow: '0px 0px 10px rgba(0, 0, 0, 0.1)',
boxShadow: '1px 1px 1px rgba(0, 0, 0, 0.1)',
position: 'absolute',
minWidth: 'fit-content',
overflow: 'hidden',
...(alignRight && { right: 0 })
}),
menuList: (provided, state) => ({
...provided,
padding: 0,
}),
control: (provided) => {
const obj = {
...provided,
@ -63,7 +80,12 @@ export default function({ alignRight = false, plain = false, options, isSearchab
const transition = 'opacity 300ms';
return { ...provided, opacity, transition };
}
},
noOptionsMessage: (provided) => ({
...provided,
whiteSpace: 'nowrap !important',
// minWidth: 'fit-content',
}),
}
@ -77,7 +99,7 @@ export default function({ alignRight = false, plain = false, options, isSearchab
DropdownIndicator,
...components,
}}
styles={customStyles}
styles={{ ...customStyles, ...styles }}
theme={(theme) => ({
...theme,
colors: {