feat(ui) - funnels - issues filters
This commit is contained in:
parent
a8fbf50a49
commit
8e1bb95c84
6 changed files with 151 additions and 8 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FunnelIssuesDropdown';
|
||||
|
|
@ -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;
|
||||
|
|
@ -0,0 +1 @@
|
|||
export { default } from './FunnelIssuesSelectedFilters';
|
||||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue